diff --git a/Documentation/ABI/testing/debugfs-iio-ad9910 b/Documentation/ABI/testing/debugfs-iio-ad9910 new file mode 100644 index 000000000000..7083a20d5a40 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-iio-ad9910 @@ -0,0 +1,23 @@ +What: /sys/kernel/debug/iio/iio:deviceX/frequency_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output frequency. + +What: /sys/kernel/debug/iio/iio:deviceX/phase_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output phase. + +What: /sys/kernel/debug/iio/iio:deviceX/amplitude_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output amplitude. diff --git a/Documentation/ABI/testing/debugfs-iio-backend b/Documentation/ABI/testing/debugfs-iio-backend index 01ab94469432..2a2f2e5508c0 100644 --- a/Documentation/ABI/testing/debugfs-iio-backend +++ b/Documentation/ABI/testing/debugfs-iio-backend @@ -8,13 +8,13 @@ What: /sys/kernel/debug/iio/iio:deviceX/backendY/direct_reg_access KernelVersion: 6.11 Contact: linux-iio@vger.kernel.org Description: - Directly access the registers of backend Y. Typical usage is: + Directly access the registers of backend Y. Typical usage is:: - Reading address 0x50 - echo 0x50 > direct_reg_access - cat direct_reg_access + # Reading address 0x50 + echo 0x50 > direct_reg_access + cat direct_reg_access - Writing address 0x50 - echo 0x50 0x3 > direct_reg_access - //readback address 0x50 - cat direct_reg_access + # Writing address 0x50 + echo 0x50 0x3 > direct_reg_access + # readback address 0x50 + cat direct_reg_access diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index a4f5595722ad..2b6f57041763 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -442,6 +442,14 @@ 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_currentY_offset What: /sys/bus/iio/devices/iio:deviceX/in_current_offset +What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_phase_offset +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_offset +What: /sys/bus/iio/devices/iio:deviceX/out_phase_offset +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_frequency_offset +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_offset +What: /sys/bus/iio/devices/iio:deviceX/out_frequency_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 @@ -475,10 +483,22 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltage_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_phaseY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_phase_scale +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_phase_scale +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_frequency_scale +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_frequency_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_current_q_scale +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrent_scale +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrent_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_scale @@ -507,6 +527,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_z_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_clear_scale What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale @@ -762,6 +783,8 @@ What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_voltage_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_altvoltage_powerdown_mode +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_powerdown_mode +What: /sys/bus/iio/devices/iio:deviceX/out_current_powerdown_mode KernelVersion: 2.6.38 Contact: linux-iio@vger.kernel.org Description: @@ -774,6 +797,7 @@ Description: 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor, 10kohm_to_gnd: connected to ground via a 10kOhm resistor, + 15kohm_to_gnd: connected to ground via a 15kOhm resistor, 16kohm_to_gnd: connected to ground via a 16kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, 32kohm_to_gnd: connected to ground via a 32kOhm resistor, @@ -802,6 +826,10 @@ What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_voltage_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltage_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_current_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrent_powerdown KernelVersion: 2.6.38 Contact: linux-iio@vger.kernel.org Description: @@ -812,6 +840,7 @@ Description: together. What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_frequency KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org Description: @@ -838,8 +867,10 @@ Description: all the other channels, since it involves changing the VCO fundamental output frequency. +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_phase 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_altcurrentY_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 @@ -853,6 +884,7 @@ Description: channel. What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw Date: May 2012 KernelVersion: 3.5 Contact: Johan Hovold @@ -1739,6 +1771,7 @@ 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_altcurrentY_raw KernelVersion: 3.17 Contact: linux-iio@vger.kernel.org Description: @@ -2129,7 +2162,9 @@ Description: Raw counter device counters direction for channel Y. +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_label What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_label +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_label What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_label KernelVersion: 5.8 Contact: linux-iio@vger.kernel.org @@ -2139,6 +2174,50 @@ Description: specific attributes. This is useful for userspace to be able to better identify an individual channel. +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_currentY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_parent +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only attribute containing the sysfs name prefix of the parent + channel for hierarchical channel relationships (e.g. "out_altvoltage0"). + The prefix uniquely identifies the parent channel and can be used by + userspace to organize channels in tree-like structures that reflects + virtual or logical relationships between them. + + For example, an IIO device may expose a primary output channel as + "out_voltage0" together with a set of profile-based or mode-based + subchannels used to configure or compose the output signal. These + subchannels may carry common attributes such as frequency, phase, etc. + They do not represent physical outputs, but rather logical/virtual + channels grouping configurations that affect the primary output. + Each subchannel exposes a _[Y]_parent attribute reading + "out_voltage0", allowing userspace to group controls under the output + channel they belong to rather than treating them as unrelated top-level + channels. + +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) frequency reading from channel Y. + Units after application of scale and offset are Hz. + +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled, no bias etc.) output frequency for channel Y. + The number must always be specified and unique if the output + corresponds to a single channel. Units after application of + scale and offset are Hz. + What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw KernelVersion: 4.18 Contact: linux-iio@vger.kernel.org @@ -2146,6 +2225,15 @@ Description: Raw (unscaled) phase difference reading from channel Y. Units after application of scale and offset are radians. +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled, no bias etc.) output phase for channel Y. + The number must always be specified and unique if the output + corresponds to a single channel. Units after application of + scale and offset are radians. + What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm2p5_input @@ -2380,6 +2468,7 @@ Description: * "sinc4+lp" - Sinc4 + Low Pass Filter. * "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion time. + * "sinc4+sinc1+pf1" - Sinc4 + Sinc1 + device specific Post Filter 1. * "sinc4+rej60" - Sinc4 + 60Hz rejection. * "sinc5" - The digital sinc5 filter. Excellent noise performance @@ -2401,6 +2490,32 @@ Description: Specifies which filter type apply to the channel. The possible values are given by the filter_type_available attribute. +What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_settlingtime +What: /sys/bus/iio/devices/iio:deviceX/in_tempY_settlingtime +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_settlingtime +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_settlingtime +KernelVersion: 7.4 +Contact: linux-iio@vger.kernel.org +Description: + Set/read total settling time in seconds for a device to settle + after starting the first conversion after any settings have + changed. The data for the conversion is outputted at the end of + this time. + + Depending on the device and how it is used, this settling time + may apply to more than just the first conversion, e.g. to every + conversion in a single-shot conversion mode or when a device + setting changes after each conversion (input chopping). + +What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_settlingtime_available +What: /sys/bus/iio/devices/iio:deviceX/in_tempY_settlingtime_available +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_settlingtime_available +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_settlingtime_available +KernelVersion: 7.4 +Contact: linux-iio@vger.kernel.org +Description: + Available settlingtime values in IIO range or list format. + What: /sys/.../events/in_proximity_thresh_either_runningperiod KernelVersion: 6.6 Contact: linux-iio@vger.kernel.org diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc b/Documentation/ABI/testing/sysfs-bus-iio-adc new file mode 100644 index 000000000000..e1309b371efb --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc @@ -0,0 +1,9 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_burnoutraw +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_burnoutraw +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_burnoutraw +KernelVersion: 7.4 +Contact: linux-iio@vger.kernel.org +Description: + Raw value from channel Y read using a single conversion with + the channel burnout current enabled. This is typically used + for diagnostic purposes to detect an open or shorted input. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 new file mode 100644 index 000000000000..4e25f538af4a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 @@ -0,0 +1,31 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_dwell_en +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_dwell_en +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_dwell_en +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + For a channel that produces parametric sweeps, this attribute controls + the sweep behavior at the configured limit. It enables dwell mode at a + sweep limit when set to 1, i.e., after the sweep is complete the output + value stays at the limit. Otherwise (setting this value to 0), the sweep + may stop or restart from the initial position, or even continue by + reversing the sweep direction. + +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw_roc +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_raw_roc +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_raw_roc +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + For a channel that produces parametric sweeps, this attribute controls + the rate of change of the parameter in raw units per second (slope). + This value may be influenced by the channel sampling_frequency attribute + if available. Multiplying this value by the scale attribute of the + channel yields the rate of change in physical units per second. + +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw_roc_available +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Lists the available roc values for the channel. Values are + space-separated in ascending order. diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml index b6ba7ad1a8d5..3ce5fb1be574 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml @@ -4,21 +4,23 @@ $id: http://devicetree.org/schemas/iio/accel/adi,adis16201.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ADIS16201 Dual Axis Inclinometer and similar +title: ADIS16201 Inclinometer and similar maintainers: - Jonathan Cameron description: | - Two similar parts from external interface point of view. + Three similar parts from external interface point of view. SPI interface. https://www.analog.com/en/products/adis16201.html + https://www.analog.com/en/products/adis16203.html https://www.analog.com/en/products/adis16209.html properties: compatible: enum: - adi,adis16201 + - adi,adis16203 - adi,adis16209 reg: diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml index f10d98d34cb8..115170eac058 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices ADXL367 3-Axis Digital Accelerometer maintainers: - - Cosmin Tanislav + - Antoniu Miclaus description: | The ADXL367 is an ultralow power, 3-axis MEMS accelerometer. @@ -33,7 +33,14 @@ properties: maxItems: 1 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - enum: [INT1, INT2] + - const: INT2 vdd-supply: true vddio-supply: true @@ -61,6 +68,7 @@ examples: reg = <0x53>; interrupt-parent = <&gpio>; interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; }; }; - | @@ -76,5 +84,6 @@ examples: spi-max-frequency = <1000000>; interrupt-parent = <&gpio>; interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index db8467b98e67..c8ac92e6f8e1 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -41,6 +41,7 @@ properties: - adi,ad4880 - adi,ad4883 - adi,ad4884 + - adi,ad4885 reg: minItems: 1 @@ -105,6 +106,7 @@ allOf: - adi,ad4880 - adi,ad4883 - adi,ad4884 + - adi,ad4885 then: properties: reg: @@ -127,15 +129,15 @@ examples: #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>; + 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>; }; }; - | @@ -144,15 +146,15 @@ examples: #size-cells = <0>; adc@0 { - compatible = "adi,ad4880"; - reg = <0>, <1>; - spi-max-frequency = <10000000>; - vdd33-supply = <&vdd33>; - vddldo-supply = <&vddldo>; - vrefin-supply = <&vrefin>; - clocks = <&cnv>; - clock-names = "cnv"; - io-backends = <&iio_backend_cha>, <&iio_backend_chb>; + compatible = "adi,ad4880"; + reg = <0>, <1>; + spi-max-frequency = <10000000>; + vdd33-supply = <&vdd33>; + vddldo-supply = <&vddldo>; + vrefin-supply = <&vrefin>; + clocks = <&cnv>; + clock-names = "cnv"; + io-backends = <&iio_backend_cha>, <&iio_backend_chb>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml index cc38617bb829..a4ab6adef1c6 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml @@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices AD4130 family ADCs maintainers: - - Cosmin Tanislav + - Marcelo Schmitt + - Antoniu Miclaus description: | Bindings for the Analog Devices AD4130 family ADCs. diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml index b107322e0ea3..49708ce0b230 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml @@ -142,13 +142,13 @@ examples: io-backends = <&iio_backend>; channel@0 { - reg = <0>; - diff-channels = <0 0>; - bipolar; + reg = <0>; + diff-channels = <0 0>; + bipolar; }; channel@1 { - reg = <1>; + reg = <1>; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml index 62d906e24997..e178007b9a22 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml @@ -443,7 +443,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - adc@0 { + adc@0 { compatible = "adi,ad4111"; reg = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index dfa2d7fa9fb3..7b72d14e104c 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -238,9 +238,9 @@ examples: }; regulators { - vcm_reg: vcm-output { - regulator-name = "ad7768-1-vcm"; - }; + vcm_reg: vcm-output { + regulator-name = "ad7768-1-vcm"; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml new file mode 100644 index 000000000000..d8e489fa1f6f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml @@ -0,0 +1,270 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7768.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD7768 and AD7768-4 ADC + +maintainers: + - Janani Sunil + +description: | + The AD7768 is an 8-channel, 24-bit simultaneous sampling ADC with configurable + power and performance modes. The AD7768-4 is a 4-channel version. + + This binding covers SPI mode only (register access via SPI). Pin control mode, + where configuration is set via hardware pins rather than SPI registers, is not + supported. + + The io-backend represents the high-speed data output interface, consuming the + DOUTx data lines, DCLK (data clock), and DRDY (data ready) signals. + + Datasheet at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-ad7768-4.pdf + + HDL reference design: + https://github.com/analogdevicesinc/hdl/tree/main/projects/ad7768evb + +properties: + compatible: + enum: + - adi,ad7768 + - adi,ad7768-4 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + description: + Describes the type of master clock (MCLK) attached to the XTAL1 and + XTAL2/MCLK pins. A CMOS clock is supplied on the XTAL2/MCLK pin, a + crystal oscillator is connected between the XTAL1 and XTAL2 pins, and an + LVDS differential clock is supplied on the XTAL1/XTAL2 pin pair. CMOS and + LVDS clocks must operate from 1.15 MHz to 34 MHz. Crystals must operate + from 8 MHz to 34 MHz. + enum: + - mclk + - xtal + - lvds + + avdd1-supply: + description: + Analog supply. Its voltage is used to derive the supply-dependent VCM + output level, so it must be a regulator that can report its voltage. + + avss-supply: + description: + Negative analog supply. If omitted, AVSS is assumed to be at system + ground. + + avdd2-supply: true + + iovdd-supply: true + + ref1p-supply: true + + ref1n-supply: + description: + Negative reference input supply for REF1. If omitted, REF1- is assumed + to be connected to ground. + + ref2p-supply: true + + ref2n-supply: + description: + Negative reference input supply for REF2. If omitted, REF2- is assumed + to be connected to ground. + + reset-gpios: + maxItems: 1 + description: GPIO connected to the active-low RESET pin + + start-gpios: + maxItems: 1 + description: + GPIO connected to the START pin. An active-low pulse on this pin + synchronizes the device to other AD7768 devices. When not used, this + pin must be tied to a logic high level through a pull-up resistor. + When used, SYNC_OUT must be wired back to SYNC_IN on all devices in + the synchronization chain. + + sync-in-gpios: + maxItems: 1 + description: + GPIO connected to the SYNC_IN pin. Receives the synchronization pulse + generated on SYNC_OUT in response to a START pulse. Must be synchronous + to the MCLK clock domain. In multi-device or daisy-chained systems, + two successive synchronization pulses must be applied to guarantee all + ADCs are synchronized. + + gpio-controller: true + + '#gpio-cells': + const: 2 + + adi,data-lines-number: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 4, 8] + description: + Number of DOUTx lines used for the high-speed data output interface + (distinct from the SPI control interface). AD7768 supports 1, 2, or 8 + lines; AD7768-4 supports 1 or 4 lines. Defaults to the maximum number + of lines for the selected variant if omitted. + + regulators: + type: object + description: Container for the on-chip VCM regulator. + additionalProperties: false + + properties: + vcm-output: + type: object + description: + Buffered common-mode voltage output on the VCM pin. Use + regulator-always-on when no consumer node references this output. + Channel 0 must be present and enabled while VCM is used, since + putting channel 0 in standby disables the VCM output. + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + io-backends: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@[0-7]$": + type: object + description: + Represents the external channels which are connected to the device. + AD7768 supports channels 0-7, AD7768-4 supports channels 0-3. + + properties: + reg: + minimum: 0 + maximum: 7 + description: The channel number + + adi,prechargebuf-pos-enable: + type: boolean + description: Enable positive input precharge buffer + + adi,prechargebuf-neg-enable: + type: boolean + description: Enable negative input precharge buffer + + adi,refbuf-pos-enable: + type: boolean + description: Enable positive reference buffer + + adi,refbuf-neg-enable: + type: boolean + description: Enable negative reference buffer + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - avdd1-supply + - avdd2-supply + - iovdd-supply + - ref1p-supply + - ref2p-supply + - io-backends + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: adi,ad7768-4 + then: + properties: + adi,data-lines-number: + enum: [1, 4] + patternProperties: + "^channel@[0-3]$": + properties: + reg: + maximum: 3 + "^channel@[4-7]$": false + else: + properties: + adi,data-lines-number: + enum: [1, 2, 8] + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad7768"; + reg = <0>; + spi-max-frequency = <1000000>; + + avdd1-supply = <&avdd1_regulator>; + avss-supply = <&avss_regulator>; + avdd2-supply = <&avdd2_regulator>; + iovdd-supply = <&iovdd_regulator>; + ref1p-supply = <&ref1p_regulator>; + ref1n-supply = <&ref1n_regulator>; + ref2p-supply = <&ref2p_regulator>; + ref2n-supply = <&ref2n_regulator>; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + + clocks = <&ad7768_mclk>; + clock-names = "mclk"; + + adi,data-lines-number = <8>; + gpio-controller; + #gpio-cells = <2>; + + io-backends = <&iio_backend>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,prechargebuf-pos-enable; + adi,refbuf-pos-enable; + }; + + channel@1 { + reg = <1>; + adi,prechargebuf-pos-enable; + adi,prechargebuf-neg-enable; + adi,refbuf-pos-enable; + adi,refbuf-neg-enable; + }; + + channel@7 { + reg = <7>; + adi,prechargebuf-pos-enable; + adi,refbuf-pos-enable; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml index ba3f7b2bd6cf..37e25a6196e7 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml @@ -122,14 +122,14 @@ examples: #size-cells = <0>; adc@0 { - compatible = "adi,ad7779"; - reg = <0>; - start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; - reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>; - interrupt-parent = <&intc>; - interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "adc_rdy"; - clocks = <&adc_clk>; + compatible = "adi,ad7779"; + reg = <0>; + start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "adc_rdy"; + clocks = <&adc_clk>; }; }; @@ -140,13 +140,13 @@ examples: #size-cells = <0>; adc@0 { - compatible = "adi,ad7779"; - reg = <0>; - start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; - reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>; - clocks = <&adc_clk>; - io-backends = <&iio_backend>; - adi,num-lanes = <4>; + compatible = "adi,ad7779"; + reg = <0>; + start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>; + clocks = <&adc_clk>; + io-backends = <&iio_backend>; + adi,num-lanes = <4>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml index f22eba0250ee..0971a29bf372 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml @@ -5,21 +5,23 @@ $id: http://devicetree.org/schemas/iio/adc/adi,ade9000.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering +title: Analog Devices ADE9000/ADE9078 High Performance, Polyphase Energy Metering maintainers: - Antoniu Miclaus description: | - The ADE9000 is a highly accurate, fully integrated, multiphase energy and power - quality monitoring device. Superior analog performance and a digital signal - processing (DSP) core enable accurate energy monitoring over a wide dynamic - range. An integrated high end reference ensures low drift over temperature - with a combined drift of less than ±25 ppm/°C maximum for the entire channel - including a programmable gain amplifier (PGA) and an analog-to-digital - converter (ADC). + The ADE9000 and ADE9078 are highly accurate, fully integrated, multiphase + energy and power quality monitoring devices. Superior analog performance and a + digital signal processing (DSP) core enable accurate energy monitoring over a + wide dynamic range. An integrated high end reference ensures low drift over + temperature with a combined drift of less than ±25 ppm/°C maximum for the + entire channel including a programmable gain amplifier (PGA) and an + analog-to-digital converter (ADC). The ADE9078 does not provide the on-chip + dip/swell detection available on the ADE9000. https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9078.pdf $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -27,6 +29,7 @@ properties: compatible: enum: - adi,ade9000 + - adi,ade9078 reg: maxItems: 1 @@ -67,6 +70,17 @@ required: - reg - vdd-supply +allOf: + - if: + properties: + compatible: + contains: + const: adi,ade9078 + then: + properties: + spi-max-frequency: + maximum: 10000000 + unevaluatedProperties: false examples: @@ -75,20 +89,20 @@ examples: #include spi { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; - adc@0 { - compatible = "adi,ade9000"; - reg = <0>; - spi-max-frequency = <7000000>; + adc@0 { + compatible = "adi,ade9000"; + reg = <0>; + spi-max-frequency = <7000000>; - #clock-cells = <0>; - reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; - interrupts = <2 IRQ_TYPE_EDGE_FALLING>, <3 IRQ_TYPE_EDGE_FALLING>, <4 IRQ_TYPE_EDGE_FALLING>; - interrupt-names = "irq0", "irq1", "dready"; - interrupt-parent = <&gpio>; - clocks = <&ext_clock_24576khz>; - vdd-supply = <&vdd_reg>; - }; + #clock-cells = <0>; + reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>, <3 IRQ_TYPE_EDGE_FALLING>, <4 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "irq0", "irq1", "dready"; + interrupt-parent = <&gpio>; + clocks = <&ext_clock_24576khz>; + vdd-supply = <&vdd_reg>; + }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml new file mode 100644 index 000000000000..b0ec9159a293 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,max40080.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX40080 bidirectional current-sense amplifier + +maintainers: + - Ciprian Hegbeli + - Stefan Popa + +description: | + The MAX40080 is a high-precision, bidirectional current-sense amplifier with + an integrated 12-bit ADC and an I2C/SMBus interface. It measures the voltage + across an external shunt resistor and the input bus voltage, and stores the + results in an internal FIFO. + + Datasheet: + https://www.analog.com/max40080 + +properties: + compatible: + const: adi,max40080 + + reg: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vdd-supply: + description: Power supply for the device. + + interrupts: + maxItems: 1 + description: ALERT_ output for overcurrent/undervoltage threshold events. + + shunt-resistor-micro-ohms: + description: + Value of the current-sense shunt resistor connected between the RS+ and + RS- inputs. Used to scale the reported current. + +required: + - compatible + - reg + - shunt-resistor-micro-ohms + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@20 { + compatible = "adi,max40080"; + reg = <0x20>; + #io-channel-cells = <1>; + vdd-supply = <&vdd>; + shunt-resistor-micro-ohms = <100000>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml new file mode 100644 index 000000000000..b910852aa56f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/axiado,ax3000-saradc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axiado AX3000/AX3005 Successive Approximation Register ADC + +description: + The Axiado AX3000/AX3005 SAR ADC is a 10-bit ADC with sixteen input + channels on AX3000 and eight input channels on AX3005. + +maintainers: + - Petar Stepanovic + - Akhila Kavi + - Prasad Bolisetty + +properties: + compatible: + enum: + - axiado,ax3000-saradc + - axiado,ax3005-saradc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: saradc + + '#io-channel-cells': + const: 1 + + vref-supply: + description: Reference voltage regulator supplying the ADC + +required: + - compatible + - reg + - clocks + - clock-names + - '#io-channel-cells' + - vref-supply + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + adc@806a0000 { + compatible = "axiado,ax3000-saradc"; + reg = <0x0 0x806a0000 0x0 0x400>; + clocks = <&pclk>; + clock-names = "saradc"; + vref-supply = <&vref_reg>; + #io-channel-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index c884b6e03767..67445ad2bb3b 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/adc/lltc,ltc2497.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Linear Technology / Analog Devices LTC2497 and LTC2309 ADC +title: Linear Technology / Analog Devices LTC2497 and similar ADCs maintainers: - Michael Hennerich diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max34408.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max34408.yaml index 4cba856e8d47..bad780d1bbfb 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max34408.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max34408.yaml @@ -118,22 +118,22 @@ examples: #size-cells = <0>; adc@1e { - compatible = "maxim,max34409"; - reg = <0x1e>; - powerdown-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; - powerdown-status-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; + compatible = "maxim,max34409"; + reg = <0x1e>; + powerdown-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + powerdown-status-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; - channel@0 { - reg = <0x0>; - maxim,rsense-val-micro-ohms = <5000>; - }; + channel@0 { + reg = <0x0>; + maxim,rsense-val-micro-ohms = <5000>; + }; - channel@1 { - reg = <0x1>; - maxim,rsense-val-micro-ohms = <10000>; - }; + channel@1 { + reg = <0x1>; + maxim,rsense-val-micro-ohms = <10000>; + }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml index d9e825e5054f..750e1e837fb8 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml @@ -38,6 +38,7 @@ properties: - const: mediatek,mt7986-auxadc - items: - enum: + - mediatek,mt6572-auxadc - mediatek,mt6893-auxadc - mediatek,mt8183-auxadc - mediatek,mt8186-auxadc diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml index 70b38038a080..1e2df3dd15d1 100644 --- a/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml @@ -56,6 +56,6 @@ examples: clocks = <&ccu1 CLK_APB3_ADC0>; vref-supply = <®_vdda>; resets = <&rgu 40>; - }; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,r9a09g077-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,r9a09g077-adc.yaml index dc0206b28231..5b496729022e 100644 --- a/Documentation/devicetree/bindings/iio/adc/renesas,r9a09g077-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/renesas,r9a09g077-adc.yaml @@ -34,8 +34,6 @@ properties: - description: A/D scan end interrupt for Group C - description: Window A compare match - description: Window B compare match - - description: Compare match - - description: Compare mismatch interrupt-names: items: @@ -44,8 +42,6 @@ properties: - const: gcadi - const: cmpai - const: cmpbi - - const: wcmpm - - const: wcmpum clocks: items: @@ -57,6 +53,18 @@ properties: - const: adclk - const: pclk + dmas: + minItems: 1 + maxItems: 3 + description: + Must contain a list of references to DMA RX specifiers. + + dma-names: + minItems: 1 + maxItems: 3 + items: + const: rx + power-domains: maxItems: 1 @@ -107,11 +115,9 @@ examples: , , , - , - , - ; + ; interrupt-names = "adi", "gbadi", "gcadi", - "cmpai", "cmpbi", "wcmpm", "wcmpum"; + "cmpai", "cmpbi"; clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKL>, <&cpg CPG_MOD 225>; clock-names = "adclk", "pclk"; diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml index 40341d541726..f08f5439d127 100644 --- a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml @@ -25,7 +25,9 @@ properties: - renesas,r9a07g054-adc # RZ/V2L - const: renesas,rzg2l-adc - items: - - const: renesas,r9a08g045-adc # RZ/G3S + - enum: + - renesas,r9a08g045-adc # RZ/G3S + - renesas,r9a08g046-adc1 # RZ/G3L ADC for TSU reg: maxItems: 1 @@ -121,6 +123,19 @@ allOf: reg: maximum: 7 + - if: + properties: + compatible: + contains: + const: renesas,r9a08g046-adc1 + then: + patternProperties: + "^channel@[0-7]$": false + "^channel@[8]$": + properties: + reg: + const: 8 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml index 718f633c6e04..777a75c21d7f 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml @@ -66,6 +66,11 @@ patternProperties: - minimum: 0 maximum: 7 + label: + $ref: /schemas/types.yaml#/definitions/string + description: + A descriptive name for this channel, like "vcc_ram" or "CH3". + ti,gain: $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 @@ -125,6 +130,7 @@ examples: reg = <4>; ti,gain = <3>; ti,datarate = <5>; + label = "CH3"; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml index 970ccab15e1e..28c5e2dd0ad6 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml @@ -4,19 +4,23 @@ $id: http://devicetree.org/schemas/iio/adc/ti,ads1100.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: TI ADS1100/ADS1000 single channel I2C analog to digital converter +title: TI ADS1100 and similar single channel I2C Analog to Digital Converters maintainers: - Mike Looijmans description: | - Datasheet at: https://www.ti.com/lit/gpn/ads1100 + Datasheets: + - https://www.ti.com/lit/gpn/ads1000 + - https://www.ti.com/lit/gpn/ads1100 + - https://www.ti.com/lit/gpn/ads1110 properties: compatible: enum: - - ti,ads1100 - ti,ads1000 + - ti,ads1100 + - ti,ads1110 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml new file mode 100644 index 000000000000..95a123cd7b67 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads112c04.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments ADS112C04 ADC + +maintainers: + - Kyle Hsieh + +description: + The ADS112C04 (16-bit) are precision analog-to-digital converters (ADCs) + with an I2C interface. They feature a flexible input multiplexer, a + low-noise programmable gain amplifier (PGA), two programmable excitation + current sources, a voltage reference, and a precision temperature sensor. + +properties: + compatible: + enum: + - ti,ads112c04 + + reg: + maxItems: 1 + description: I2C address of the device. + + interrupts: + maxItems: 1 + description: + Data ready (DRDY) interrupt output. DRDY pulses low when a conversion + result is ready and returns high once the result is latched, so an + edge trigger should be used. + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + reset-gpios: + maxItems: 1 + description: GPIO connected to the RESET pin. Active low. + + avdd-supply: true + dvdd-supply: true + + refp-supply: true + refn-supply: true + + ti,refp-refn-resistor-ohms: + description: Resistance of the external resistor between REFP and REFN. + +patternProperties: + "^channel@[0-9a-b]$": + $ref: adc.yaml + unevaluatedProperties: false + properties: + reg: + items: + - maximum: 11 + + single-channel: + maximum: 3 + + diff-channels: + items: + maximum: 3 + + excitation-channels: + maxItems: 2 + items: + maximum: 5 + description: + Additionally to AIN0-AIN3, IDAC1/IDAC2 can also be routed to + REFP0 (4) and REFN0 (5). + + excitation-current-nanoamp: + enum: [10000, 50000, 100000, 250000, 500000, 1000000, 1500000] + + burn-out-current-nanoamp: + const: 10000 + + reference-sources: + items: + - enum: [internal, external, avdd] + default: [ internal ] + + dependencies: + excitation-channels: [ excitation-current-nanoamp ] + excitation-current-nanoamp: [ excitation-channels ] + + oneOf: + - required: [ single-channel ] + - required: [ diff-channels ] + +required: + - compatible + - reg + - avdd-supply + - dvdd-supply + +dependencies: + refn-supply: [ refp-supply ] + +oneOf: + - required: + - refp-supply + - required: + - ti,refp-refn-resistor-ohms + - properties: + refp-supply: false + refn-supply: false + ti,refp-refn-resistor-ohms: false + +unevaluatedProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@40 { + compatible = "ti,ads112c04"; + reg = <0x40>; + interrupt-parent = <&gpio>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + + reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + avdd-supply = <&vdd_3v3_reg>; + dvdd-supply = <&vdd_3v3_reg>; + refp-supply = <&vref_reg>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + diff-channels = <0>, <1>; + }; + + channel@1 { + reg = <1>; + single-channel = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml index 71f9f9b745cb..afcc683db149 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml @@ -65,15 +65,15 @@ examples: #size-cells = <0>; adc@1 { - reg = <1>; - compatible = "ti,ads1298"; - label = "ads1298-1-ecg"; - avdd-supply = <®_iso_5v_a>; - clocks = <&clk_ads1298>; - interrupt-parent = <&gpio0>; - interrupts = <78 IRQ_TYPE_EDGE_FALLING>; - spi-max-frequency = <20000000>; - spi-cpha; + reg = <1>; + compatible = "ti,ads1298"; + label = "ads1298-1-ecg"; + avdd-supply = <®_iso_5v_a>; + clocks = <&clk_ads1298>; + interrupt-parent = <&gpio0>; + interrupts = <78 IRQ_TYPE_EDGE_FALLING>; + spi-max-frequency = <20000000>; + spi-cpha; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml index 2a04906531fb..6aa86cb56032 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml @@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices AD74115H device maintainers: - - Cosmin Tanislav + - Marcelo Schmitt + - Antoniu Miclaus description: | The AD74115H is a single-channel software configurable input/output diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml index 590ea7936ad7..5318a00e7669 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices AD74412R/AD74413R device maintainers: - - Cosmin Tanislav + - Marcelo Schmitt + - Antoniu Miclaus description: | The AD74412R and AD74413R are quad-channel software configurable input/output diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml index 2fe098619772..bf0278335583 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml @@ -10,13 +10,13 @@ maintainers: - Kim Seer Paller description: | - The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), and AD3532/AD3532R - (16-channel) are low-power, 16-bit, buffered voltage output digital-to-analog - converters (DACs) with software-programmable gain controls, providing - full-scale output spans of 2.5V or 5V for reference voltages of 2.5V. These - devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic - by design. The "R" variants include a 2.5V, 5ppm/°C internal reference, which - is disabled by default. + The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), AD3532/AD3532R + (16-channel), 16-bit and AD3536R (16-channel), 12-bit are low-power, buffered + voltage output digital-to-analog converters (DACs) with software-programmable + gain controls, providing full-scale output spans of 2.5V or 5V for reference + voltages of 2.5V. These devices operate from a single 2.7V to 5.5V supply and + are guaranteed monotonic by design. The "R" variants include a 2.5V, 5ppm/°C + internal reference, which is disabled by default. Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3530_ad530r.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/ad3531-ad3531r.pdf @@ -31,6 +31,7 @@ properties: - adi,ad3531r - adi,ad3532 - adi,ad3532r + - adi,ad3536r reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml new file mode 100644 index 000000000000..9c390d460e5f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml @@ -0,0 +1,254 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad5529r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5529R 16-Channel 12/16-bit High Voltage DAC + +maintainers: + - Janani Sunil + +description: | + The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage + output digital-to-analog converter (DAC) with an integrated precision reference. + The device operates from unipolar and bipolar supplies. It is guaranteed + monotonic and has built-in rail-to-rail output buffers that can source or + sink up to 25mA. + + Specifications: + * 16 independent 12-bit or 16-bit DAC channels + * Independently programmable output ranges: 0V to 5V, 0V to 10V, 0V to 20V, + 0V to 40V, ±5V, ±10V, ±15V, and ±20V + * The device supports SPI communication with Mode 0 and Mode 3. + * 4.096V precision reference, 12ppm/°C maximum + * Built-in function generation: Toggle, Sinusoidal Dither, and Ramp waveforms + * Multiplexer for output voltage, load current sense and die temperature + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf + +properties: + compatible: + enum: + - adi,ad5529r-16 # 16-bit variant + - adi,ad5529r-12 # 12-bit variant + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + description: + Maximum SPI frequency. The device supports SPI Mode 0 and Mode 3. + Read operations are limited to 25MHz maximum. + + reset-gpios: + maxItems: 1 + description: + GPIO connected to the RESET pin. Active low. When asserted low, + performs a power-on reset and initializes the device to its default state. + + clear-gpios: + maxItems: 1 + description: + GPIO connected to the CLEAR pin. Active low. When asserted low, + clears all DAC data registers without affecting configuration settings. + + interrupts: + maxItems: 1 + description: + Interrupt connected to the ALARM pin. Active low interrupt output + for overtemperature conditions, SPI CRC errors, and function completion. + + pwms: + minItems: 1 + maxItems: 4 + description: + PWM signals connected to the TG0-TG3 toggle pins. Pulsing these pins + based on trigger edge settings allows selected DACs to be updated + synchronously for digital function generation. + + pwm-names: + minItems: 1 + maxItems: 4 + items: + enum: [ tg0, tg1, tg2, tg3 ] + + io-channels: + maxItems: 1 + description: + ADC channel connected to the MUXOUT pin for monitoring output voltage, + load current sense, and die temperature. + + io-channel-names: + const: muxout + + vdd-io-supply: + description: Digital power supply (1.08V to 1.98V) + + vdd-5v-supply: + description: Analog power supply (4.75V to 5.25V) + + vdd-hv0-supply: + description: + High voltage positive supply (7V to 45V) for channels 0 to 3. + + vdd-hv1-supply: + description: + High voltage positive supply (7V to 45V) for channels 4 to 7. + + vdd-hv2-supply: + description: + High voltage positive supply (7V to 45V) for channels 8 to 11. + + vdd-hv3-supply: + description: + High voltage positive supply (7V to 45V) for channels 12 to 15. + + vss-hv0-supply: + description: + High voltage negative supply (-22.5V to 0V) for channels 0 to 3. + + vss-hv1-supply: + description: + High voltage negative supply (-22.5V to 0V) for channels 4 to 7. + + vss-hv2-supply: + description: + High voltage negative supply (-22.5V to 0V) for channels 8 to 11. + + vss-hv3-supply: + description: + High voltage negative supply (-22.5V to 0V) for channels 12 to 15. + + vref-supply: + description: + External voltage reference supply (4.056V to 4.136V, typically 4.096V). + When specified, the device uses external reference mode and the VREF pin + becomes an input. The device uses the internal 4.096V precision reference + otherwise. + + spi-device-addr: + minItems: 1 + maxItems: 4 + items: + enum: [0, 1, 2, 3] + default: 0 + description: + Hardware address of each device, selected by the ID0 and ID1 pins. + Up to four AD5529R devices can share a single SPI chip select. + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^channel@([0-9a-f]{1,2})$": + $ref: dac.yaml + type: object + description: Child nodes for individual channel configuration + + properties: + reg: + description: + Flattened channel number across all AD5529R devices sharing the chip + select. Within each 16-channel block, the physical channel number is reg % 16. + minimum: 0 + maximum: 63 + + output-range-microvolt: + description: + Output voltage range for this channel as [min, max] in + microvolts. + oneOf: + - items: + - const: 0 + default: 0 + - enum: [5000000, 10000000, 20000000, 40000000] + default: 5000000 + - items: + - const: -5000000 + - const: 5000000 + - items: + - const: -10000000 + - const: 10000000 + - items: + - const: -15000000 + - const: 15000000 + - items: + - const: -20000000 + - const: 20000000 + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - vdd-io-supply + - vdd-5v-supply + - vdd-hv0-supply + - vdd-hv1-supply + - vdd-hv2-supply + - vdd-hv3-supply + +dependencies: + spi-cpha: [ spi-cpol ] + spi-cpol: [ spi-cpha ] + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "adi,ad5529r-16"; + reg = <0>; + spi-max-frequency = <25000000>; + spi-device-addr = <0>; + + vdd-io-supply = <&vdd_io_regulator>; + vdd-5v-supply = <&vdd_5v_regulator>; + vdd-hv0-supply = <&vdd_hv0_regulator>; + vdd-hv1-supply = <&vdd_hv1_regulator>; + vdd-hv2-supply = <&vdd_hv2_regulator>; + vdd-hv3-supply = <&vdd_hv3_regulator>; + vss-hv0-supply = <&vss_hv0_regulator>; + vss-hv1-supply = <&vss_hv1_regulator>; + vss-hv2-supply = <&vss_hv2_regulator>; + vss-hv3-supply = <&vss_hv3_regulator>; + + reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + output-range-microvolt = <0 5000000>; + }; + + channel@1 { + reg = <1>; + output-range-microvolt = <(-10000000) 10000000>; + }; + + channel@2 { + reg = <2>; + output-range-microvolt = <0 40000000>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml new file mode 100644 index 000000000000..cdcc3da4f4ea --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml @@ -0,0 +1,145 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad5710r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5710R/AD5711R 8-channel 12-/16-bit Configurable IDAC/VDAC + +maintainers: + - Kim Seer Paller + +description: | + The AD5710R (16-bit) and AD5711R (12-bit) are 8-channel, low-power, + configurable current/voltage output digital-to-analog converters (DACs) with + an on-chip 2.5V, 3ppm/°C reference. Each channel can be independently + configured as a voltage output (0V to VREF or 0V to 2 x VREF) or a current + output (0mA to 50mA). These devices operate from a single 2.7V to 5.5V + supply and are guaranteed monotonic by design. + + Datasheet can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad5711r-ad5710r.pdf + +properties: + compatible: + enum: + - adi,ad5710r + - adi,ad5711r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 20000000 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + vdd-supply: + description: Power Supply Input. + + iovdd-supply: + description: Digital Power Supply Input. + + io-channels: + description: + ADC channel used to monitor internal die temperature, output voltages, and + current of a selected channel via the MUXOUT pin. + maxItems: 1 + + ref-supply: + description: + Reference Input/Output. The voltage at the REF pin sets the full-scale + range of all channels. If not provided the internal reference is used and + also provided on the VREF pin. + + reset-gpios: + description: + Active low signal that is falling edge sensitive. When it is deasserted, + the digital core initialization is performed and all DAC registers except + the Interface Configuration A register are reset to their default values. + maxItems: 1 + + ldac-gpios: + description: + LDAC pin to be used as a hardware trigger to update the DAC channels. If + not present, the DAC channels are updated by Software LDAC. + maxItems: 1 + + adi,range-double: + description: + Configure the output range for all channels. If the property is present, + the output will range from 0V to 2 x Vref. If the property is not present, + the output will range from 0V to Vref. + type: boolean + +patternProperties: + "^channel@[0-7]$": + $ref: /schemas/iio/dac/dac.yaml# + type: object + description: + Represents the external channels which are connected to the DAC. + + properties: + reg: + description: Channel number + minimum: 0 + maximum: 7 + + adi,ch-func: + description: + Channel output type. The values are defined in + . Use + CH_FUNC_VOLTAGE_OUTPUT (1) for voltage output or + CH_FUNC_CURRENT_OUTPUT (2) for current output. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + + required: + - reg + - adi,ch-func + + unevaluatedProperties: false + +required: + - compatible + - reg + - vdd-supply + - iovdd-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + dac@0 { + compatible = "adi,ad5710r"; + reg = <0>; + spi-max-frequency = <1000000>; + vdd-supply = <&vdd>; + iovdd-supply = <&iovdd>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,ch-func = ; + }; + + channel@1 { + reg = <1>; + adi,ch-func = ; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml index d131f136bd15..a86d094f0d4b 100644 --- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml @@ -4,58 +4,92 @@ $id: http://devicetree.org/schemas/iio/dac/microchip,mcp47feb02.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Microchip MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families +title: Microchip MCP4(7/8)F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C/SPI Interface Families maintainers: - Ariana Lazar description: | - Datasheet for MCP47FEB01, MCP47FEB11, MCP47FEB21, MCP47FEB02, MCP47FEB12, - MCP47FEB22 can be found here: - https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf - Datasheet for MCP47FVB01, MCP47FVB11, MCP47FVB21, MCP47FVB02, MCP47FVB12, - MCP47FVB22 can be found here: - https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf - Datasheet for MCP47FEB04, MCP47FEB14, MCP47FEB24, MCP47FEB08, MCP47FEB18, - MCP47FEB28, MCP47FVB04, MCP47FVB14, MCP47FVB24, MCP47FVB08, MCP47FVB18, - MCP47FVB28 can be found here: - https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf + Datasheets for MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families: + Datasheet for MCP47FEB01, MCP47FEB02, MCP47FEB11, MCP47FEB12, MCP47FEB21, + MCP47FEB22 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf + Datasheet for MCP47FVB01, MCP47FVB02, MCP47FVB11, MCP47FVB12, MCP47FVB21, + MCP47FVB22 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf + Datasheet for MCP47FEB04, MCP47FEB08, MCP47FEB14, MCP47FEB18, MCP47FEB24, + MCP47FEB28, MCP47FVB04, MCP47FVB08, MCP47FVB14, MCP47FVB18, MCP47FVB24, + MCP47FVB28 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf + Datasheets for MCP48F(E/V)B(0/1/2)(1/2/4/8) DAC with SPI Interface Families: + Datasheet for MCP48FEB01, MCP48FEB02, MCP48FEB11, MCP48FEB12, MCP48FEB21, + MCP48FEB22 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005429B.pdf + Datasheet for MCP48FVB01, MCP48FVB02, MCP48FVB11, MCP48FVB12, MCP48FVB21, + MCP48FVB22 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005466A.pdf + Datasheet for MCP48FEB04, MCP48FEB14, MCP48FEB24, MCP48FEB08, MCP48FEB18, + MCP48FEB28, MCP48FVB04, MCP48FVB14, MCP48FVB24, MCP48FVB08, MCP48FVB18, + MCP48FVB28 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP48FXBX4-8-Family-Data-Sheet-DS20006362A.pdf - +------------+--------------+-------------+-------------+------------+ - | Device | Resolution | Channels | Vref number | Memory | - |------------|--------------|-------------|-------------|------------| - | MCP47FEB01 | 8-bit | 1 | 1 | EEPROM | - | MCP47FEB11 | 10-bit | 1 | 1 | EEPROM | - | MCP47FEB21 | 12-bit | 1 | 1 | EEPROM | - |------------|--------------|-------------|-------------|------------| - | MCP47FEB02 | 8-bit | 2 | 1 | EEPROM | - | MCP47FEB12 | 10-bit | 2 | 1 | EEPROM | - | MCP47FEB22 | 12-bit | 2 | 1 | EEPROM | - |------------|--------------|-------------|-------------|------------| - | MCP47FVB01 | 8-bit | 1 | 1 | RAM | - | MCP47FVB11 | 10-bit | 1 | 1 | RAM | - | MCP47FVB21 | 12-bit | 1 | 1 | RAM | - |------------|--------------|-------------|-------------|------------| - | MCP47FVB02 | 8-bit | 2 | 1 | RAM | - | MCP47FVB12 | 10-bit | 2 | 1 | RAM | - | MCP47FVB22 | 12-bit | 2 | 1 | RAM | - |------------|--------------|-------------|-------------|------------| - | MCP47FVB04 | 8-bit | 4 | 2 | RAM | - | MCP47FVB14 | 10-bit | 4 | 2 | RAM | - | MCP47FVB24 | 12-bit | 4 | 2 | RAM | - |------------|--------------|-------------|-------------|------------| - | MCP47FVB08 | 8-bit | 8 | 2 | RAM | - | MCP47FVB18 | 10-bit | 8 | 2 | RAM | - | MCP47FVB28 | 12-bit | 8 | 2 | RAM | - |------------|--------------|-------------|-------------|------------| - | MCP47FEB04 | 8-bit | 4 | 2 | EEPROM | - | MCP47FEB14 | 10-bit | 4 | 2 | EEPROM | - | MCP47FEB24 | 12-bit | 4 | 2 | EEPROM | - |------------|--------------|-------------|-------------|------------| - | MCP47FEB08 | 8-bit | 8 | 2 | EEPROM | - | MCP47FEB18 | 10-bit | 8 | 2 | EEPROM | - | MCP47FEB28 | 12-bit | 8 | 2 | EEPROM | - +------------+--------------+-------------+-------------+------------+ + +------------+--------------+-------------+-------------+------------+------------+ + | Device | Resolution | Channels | Vref number | Memory | Interface | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FEB01 | | 1 | 1 | EEPROM | I2C | + | MCP47FEB02 | | 2 | 1 | EEPROM | I2C | + | MCP47FEB04 | | 4 | 2 | EEPROM | I2C | + | MCP47FEB08 | 8-bit | 8 | 2 | EEPROM | I2C | + | MCP48FEB01 | | 1 | 1 | EEPROM | SPI | + | MCP48FEB02 | | 2 | 1 | EEPROM | SPI | + | MCP48FEB04 | | 4 | 2 | EEPROM | SPI | + | MCP48FEB08 | | 8 | 2 | EEPROM | SPI | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FEB11 | | 1 | 1 | EEPROM | I2C | + | MCP47FEB12 | | 2 | 1 | EEPROM | I2C | + | MCP47FEB14 | | 4 | 2 | EEPROM | I2C | + | MCP47FEB18 | 10-bit | 8 | 2 | EEPROM | I2C | + | MCP48FEB11 | | 1 | 1 | EEPROM | SPI | + | MCP48FEB12 | | 2 | 1 | EEPROM | SPI | + | MCP48FEB14 | | 4 | 2 | EEPROM | SPI | + | MCP48FEB18 | | 8 | 2 | EEPROM | SPI | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FEB21 | | 1 | 1 | EEPROM | I2C | + | MCP47FEB22 | | 2 | 1 | EEPROM | I2C | + | MCP47FEB24 | | 4 | 2 | EEPROM | I2C | + | MCP47FEB28 | 12-bit | 8 | 2 | EEPROM | I2C | + | MCP48FEB21 | | 1 | 1 | EEPROM | SPI | + | MCP48FEB22 | | 2 | 1 | EEPROM | SPI | + | MCP48FEB24 | | 4 | 2 | EEPROM | SPI | + | MCP48FEB28 | | 8 | 2 | EEPROM | SPI | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FVB01 | | 1 | 1 | RAM | I2C | + | MCP47FVB02 | | 2 | 1 | RAM | I2C | + | MCP47FVB04 | | 4 | 2 | RAM | I2C | + | MCP47FVB08 | 8-bit | 8 | 2 | RAM | I2C | + | MCP48FVB01 | | 1 | 1 | RAM | SPI | + | MCP48FVB02 | | 2 | 1 | RAM | SPI | + | MCP48FVB04 | | 4 | 2 | RAM | SPI | + | MCP48FVB08 | | 8 | 2 | RAM | SPI | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FVB11 | | 1 | 1 | RAM | I2C | + | MCP47FVB12 | | 2 | 1 | RAM | I2C | + | MCP47FVB14 | | 4 | 2 | RAM | I2C | + | MCP47FVB18 | 10-bit | 8 | 2 | RAM | I2C | + | MCP48FVB11 | | 1 | 1 | RAM | SPI | + | MCP48FVB12 | | 2 | 1 | RAM | SPI | + | MCP48FVB14 | | 4 | 2 | RAM | SPI | + | MCP48FVB18 | | 8 | 2 | RAM | SPI | + |------------|--------------|-------------|-------------|------------|------------| + | MCP47FVB21 | | 1 | 1 | RAM | I2C | + | MCP47FVB22 | | 2 | 1 | RAM | I2C | + | MCP47FVB24 | | 4 | 2 | RAM | I2C | + | MCP47FVB28 | 12-bit | 8 | 2 | RAM | I2C | + | MCP48FVB21 | | 1 | 1 | RAM | SPI | + | MCP48FVB22 | | 2 | 1 | RAM | SPI | + | MCP48FVB24 | | 4 | 2 | RAM | SPI | + | MCP48FVB28 | | 8 | 2 | RAM | SPI | + +------------+--------------+-------------+-------------+------------+------------+ properties: compatible: @@ -84,10 +118,37 @@ properties: - microchip,mcp47feb08 - microchip,mcp47feb18 - microchip,mcp47feb28 + - microchip,mcp48feb01 + - microchip,mcp48feb02 + - microchip,mcp48feb04 + - microchip,mcp48feb08 + - microchip,mcp48feb11 + - microchip,mcp48feb12 + - microchip,mcp48feb14 + - microchip,mcp48feb18 + - microchip,mcp48feb21 + - microchip,mcp48feb22 + - microchip,mcp48feb24 + - microchip,mcp48feb28 + - microchip,mcp48fvb01 + - microchip,mcp48fvb02 + - microchip,mcp48fvb04 + - microchip,mcp48fvb08 + - microchip,mcp48fvb11 + - microchip,mcp48fvb12 + - microchip,mcp48fvb14 + - microchip,mcp48fvb18 + - microchip,mcp48fvb21 + - microchip,mcp48fvb22 + - microchip,mcp48fvb24 + - microchip,mcp48fvb28 reg: maxItems: 1 + spi-max-frequency: + maximum: 10000000 + "#address-cells": const: 1 @@ -178,6 +239,21 @@ required: - vdd-supply allOf: + - if: + properties: + compatible: + contains: + pattern: "^microchip,mcp48f[ev]b[0-2][1248]$" + then: + $ref: /schemas/spi/spi-peripheral-props.yaml# + dependencies: + spi-cpha: [ spi-cpol ] + spi-cpol: [ spi-cpha ] + else: + properties: + spi-max-frequency: false + spi-cpha: false + spi-cpol: false - if: properties: compatible: @@ -189,6 +265,12 @@ allOf: - microchip,mcp47fvb01 - microchip,mcp47fvb11 - microchip,mcp47fvb21 + - microchip,mcp48feb01 + - microchip,mcp48feb11 + - microchip,mcp48feb21 + - microchip,mcp48fvb01 + - microchip,mcp48fvb11 + - microchip,mcp48fvb21 then: properties: lat1-gpios: false @@ -211,6 +293,12 @@ allOf: - microchip,mcp47fvb02 - microchip,mcp47fvb12 - microchip,mcp47fvb22 + - microchip,mcp48feb02 + - microchip,mcp48feb12 + - microchip,mcp48feb22 + - microchip,mcp48fvb02 + - microchip,mcp48fvb12 + - microchip,mcp48fvb22 then: properties: lat1-gpios: false @@ -233,6 +321,12 @@ allOf: - microchip,mcp47feb04 - microchip,mcp47feb14 - microchip,mcp47feb24 + - microchip,mcp48feb04 + - microchip,mcp48feb14 + - microchip,mcp48feb24 + - microchip,mcp48fvb04 + - microchip,mcp48fvb14 + - microchip,mcp48fvb24 then: patternProperties: "^channel@[0-3]$": @@ -251,6 +345,12 @@ allOf: - microchip,mcp47feb08 - microchip,mcp47feb18 - microchip,mcp47feb28 + - microchip,mcp48feb08 + - microchip,mcp48feb18 + - microchip,mcp48feb28 + - microchip,mcp48fvb08 + - microchip,mcp48fvb18 + - microchip,mcp48fvb28 then: patternProperties: "^channel@[0-7]$": @@ -272,7 +372,7 @@ allOf: properties: microchip,vref1-buffered: false -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -299,4 +399,29 @@ examples: }; }; }; + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "microchip,mcp48feb02"; + reg = <0x0>; + vdd-supply = <&vdac_vdd>; + vref-supply = <&vref_reg>; + spi-max-frequency = <10000000>; + + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + label = "Adjustable_voltage_ch0"; + }; + + channel@1 { + reg = <0x1>; + label = "Adjustable_voltage_ch1"; + }; + }; + }; ... diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml new file mode 100644 index 000000000000..4eebbd388b27 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml @@ -0,0 +1,209 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,ad9910.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD9910 Direct Digital Synthesizer + +maintainers: + - Rodrigo Alencar + +description: > + The AD9910 is a 1 GSPS direct digital synthesizer (DDS) with an integrated + 14-bit DAC. It features single tone mode with 8 configurable profiles, + a digital ramp generator, RAM control, OSK, and a parallel data port for + high-speed streaming. + + https://www.analog.com/en/products/ad9910.html + +properties: + compatible: + const: adi,ad9910 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 70000000 + + clocks: + minItems: 1 + items: + - description: Reference clock (REF_CLK). + - description: Optional synchronization clock (SYNC_IN). + + clock-names: + minItems: 1 + items: + - const: ref_clk + - const: sync_in + + '#clock-cells': + const: 1 + + clock-output-names: + items: + - const: sync_clk + - const: pdclk + - const: sync_out + + interrupts: + minItems: 1 + maxItems: 2 + description: + Requires interrupt-names property with the same number of items. The + supported interrupts are 'drover' (digital ramp generator limit) and + 'ram_swp_ovr' (end of RAM sweep). + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: [ drover, ram_swp_ovr ] + + dvdd-io33-supply: + description: 3.3V Digital I/O supply. + + avdd33-supply: + description: 3.3V Analog DAC supply. + + dvdd18-supply: + description: 1.8V Digital Core supply. + + avdd18-supply: + description: 1.8V Analog Core supply. + + reset-gpios: + maxItems: 1 + description: + GPIOs controlling the Main Device reset. + + io-reset-gpios: + maxItems: 1 + description: + GPIO controlling the I/O_RESET pin. + + powerdown-gpios: + maxItems: 1 + description: + GPIO controlling the EXT_PWR_DWN pin. + + update-gpios: + maxItems: 1 + description: + GPIO controlling the I/O_UPDATE pin. + + profile-gpios: + minItems: 3 + maxItems: 3 + description: + GPIOs controlling the PROFILE[2:0] pins for profile selection. + + sync-err-gpios: + maxItems: 1 + description: + GPIO used to read SYNC_SMP_ERR pin status. + + lock-detect-gpios: + maxItems: 1 + description: + GPIO used to read PLL_LOCK pin status. + + pwms: + maxItems: 1 + description: + PWM used to drive the DRCTL pin, which controls the digital ramp + generator slope direction. + + pwm-names: + items: + - const: drctl + + adi,pll-enable: + type: boolean + description: + Enables the internal PLL. Applicable when external loop filter components + are connected to the PLL_LOOP_FILTER pin and low reference clock + frequencies are used, often provided by a crystal or by a single-ended + on-board oscillator. + + adi,charge-pump-current-microamp: + minimum: 212 + maximum: 387 + default: 212 + description: + PLL charge pump current in microamps. Only applicable when the internal + PLL is enabled. The value is rounded to the nearest supported step. This + value depends mostly on the loop filter design. + + adi,refclk-out-drive-strength: + $ref: /schemas/types.yaml#/definitions/string + enum: [ disabled, low, medium, high ] + default: disabled + description: + Reference clock output (DRV0) drive strength. Only applicable when + the internal PLL is enabled. + +dependencies: + adi,charge-pump-current-microamp: [ 'adi,pll-enable' ] + adi,refclk-out-drive-strength: [ 'adi,pll-enable' ] + lock-detect-gpios: [ 'adi,pll-enable' ] + interrupts: [ interrupt-names ] + pwms: [ pwm-names ] + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - dvdd-io33-supply + - avdd33-supply + - dvdd18-supply + - avdd18-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + dds@0 { + compatible = "adi,ad9910"; + reg = <0>; + spi-max-frequency = <1000000>; + + #clock-cells = <1>; + clock-output-names = "sync_clk", "pdclk", "sync_out"; + + clocks = <&ad9910_refclk>; + clock-names = "ref_clk"; + + dvdd-io33-supply = <&vdd_io33>; + avdd33-supply = <&vdd_a33>; + dvdd18-supply = <&vdd_d18>; + avdd18-supply = <&vdd_a18>; + + reset-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>; + io-reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + powerdown-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + update-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>; + profile-gpios = <&gpio 4 GPIO_ACTIVE_HIGH>, + <&gpio 5 GPIO_ACTIVE_HIGH>, + <&gpio 6 GPIO_ACTIVE_HIGH>; + + pwms = <&pwm 0 1000000 0>; + pwm-names = "drctl"; + + adi,pll-enable; + adi,charge-pump-current-microamp = <387>; + adi,refclk-out-drive-strength = "disabled"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 81b6e85decd5..9e22b603d47f 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -26,16 +26,20 @@ description: | properties: compatible: - enum: - - invensense,icm42600 - - invensense,icm42602 - - invensense,icm42605 - - invensense,icm42607 - - invensense,icm42607p - - invensense,icm42622 - - invensense,icm42631 - - invensense,icm42686 - - invensense,icm42688 + oneOf: + - enum: + - invensense,icm42600 + - invensense,icm42602 + - invensense,icm42605 + - invensense,icm42607 + - invensense,icm42607p + - invensense,icm42622 + - invensense,icm42631 + - invensense,icm42686 + - invensense,icm42688 + - items: + - const: invensense,icm42630 + - const: invensense,icm42631 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/light/adux1020.yaml b/Documentation/devicetree/bindings/iio/light/adux1020.yaml index d7d14f2f1c20..83abe984a1da 100644 --- a/Documentation/devicetree/bindings/iio/light/adux1020.yaml +++ b/Documentation/devicetree/bindings/iio/light/adux1020.yaml @@ -40,10 +40,10 @@ examples: #size-cells = <0>; adux1020@64 { - compatible = "adi,adux1020"; - reg = <0x64>; - interrupt-parent = <&msmgpio>; - interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + compatible = "adi,adux1020"; + reg = <0x64>; + interrupt-parent = <&msmgpio>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml index 83b88c6a243d..512625d625a8 100644 --- a/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml +++ b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml @@ -45,6 +45,6 @@ examples: compatible = "amstaos,tsl2591"; reg = <0x29>; interrupts = <20 IRQ_TYPE_EDGE_FALLING>; - }; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/capella,cm32181.yaml b/Documentation/devicetree/bindings/iio/light/capella,cm32181.yaml new file mode 100644 index 000000000000..e64085a9cb2b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/capella,cm32181.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/capella,cm32181.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Capella CM32181 ambient light sensor + +maintainers: + - Kevin Tsai + +properties: + compatible: + const: capella,cm32181 + + reg: + maxItems: 1 + + vdd-supply: true + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@10 { + compatible = "capella,cm32181"; + reg = <0x10>; + vdd-supply = <&vdd_3v3>; + interrupt-parent = <&gpio>; + interrupts = <17 IRQ_TYPE_EDGE_RISING>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/light/isl29018.yaml b/Documentation/devicetree/bindings/iio/light/isl29018.yaml index 0ea278b07d1c..9524f0a1d195 100644 --- a/Documentation/devicetree/bindings/iio/light/isl29018.yaml +++ b/Documentation/devicetree/bindings/iio/light/isl29018.yaml @@ -50,9 +50,9 @@ examples: #size-cells = <0>; sensor@44 { - compatible = "isil,isl29018"; - reg = <0x44>; - interrupts-extended = <&msmgpio 61 IRQ_TYPE_LEVEL_HIGH>; + compatible = "isil,isl29018"; + reg = <0x44>; + interrupts-extended = <&msmgpio 61 IRQ_TYPE_LEVEL_HIGH>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml index c8074f180a79..20abd7da080d 100644 --- a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml @@ -9,15 +9,14 @@ title: LiteON LTR501 I2C Proximity and Light sensor maintainers: - Nikita Travkin -allOf: - - $ref: ../common.yaml# - properties: compatible: enum: + - liteon,ltr301 + - liteon,ltr303 + - liteon,ltr329 - liteon,ltr501 - liteon,ltr559 - - liteon,ltr301 reg: maxItems: 1 @@ -36,6 +35,31 @@ required: - compatible - reg +allOf: + - $ref: ../common.yaml# + + - if: + properties: + compatible: + contains: + enum: + - liteon,ltr329 + then: + properties: + interrupts: false + + - if: + properties: + compatible: + contains: + enum: + - liteon,ltr301 + - liteon,ltr303 + - liteon,ltr329 + then: + properties: + proximity-near-level: false + examples: - | #include diff --git a/Documentation/devicetree/bindings/iio/light/noa1305.yaml b/Documentation/devicetree/bindings/iio/light/noa1305.yaml index fe7bfe1adbda..e1c64c151932 100644 --- a/Documentation/devicetree/bindings/iio/light/noa1305.yaml +++ b/Documentation/devicetree/bindings/iio/light/noa1305.yaml @@ -39,8 +39,8 @@ examples: #size-cells = <0>; light@39 { - compatible = "onnn,noa1305"; - reg = <0x39>; + compatible = "onnn,noa1305"; + reg = <0x39>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/stk33xx.yaml b/Documentation/devicetree/bindings/iio/light/stk33xx.yaml index e4341fdced98..f655a570aac7 100644 --- a/Documentation/devicetree/bindings/iio/light/stk33xx.yaml +++ b/Documentation/devicetree/bindings/iio/light/stk33xx.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/iio/light/stk33xx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: | - Sensortek STK33xx I2C Ambient Light and Proximity sensor +title: Sensortek STK33xx/STK36C61 I2C Ambient Light and Proximity sensor maintainers: - Jonathan Cameron -description: | - Ambient light and proximity sensor over an i2c interface. +description: + Ambient light and proximity sensor over an i2c interface. The STK36C61 + additionally provides RGBC colour channels. allOf: - $ref: ../common.yaml# @@ -26,6 +26,7 @@ properties: - items: - enum: - sensortek,stk3013 + - sensortek,stk36c61 - const: sensortek,stk3310 reg: @@ -52,11 +53,11 @@ examples: #size-cells = <0>; stk3310@48 { - compatible = "sensortek,stk3310"; - reg = <0x48>; - proximity-near-level = <25>; - interrupt-parent = <&gpio1>; - interrupts = <5 IRQ_TYPE_LEVEL_LOW>; + compatible = "sensortek,stk3310"; + reg = <0x48>; + proximity-near-level = <25>; + interrupt-parent = <&gpio1>; + interrupts = <5 IRQ_TYPE_LEVEL_LOW>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/tsl2583.yaml b/Documentation/devicetree/bindings/iio/light/tsl2583.yaml index 7b92ba8cbb9f..aed4ab710e46 100644 --- a/Documentation/devicetree/bindings/iio/light/tsl2583.yaml +++ b/Documentation/devicetree/bindings/iio/light/tsl2583.yaml @@ -41,8 +41,8 @@ examples: #size-cells = <0>; light-sensor@29 { - compatible = "amstaos,tsl2581"; - reg = <0x29>; + compatible = "amstaos,tsl2581"; + reg = <0x29>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/tsl2772.yaml b/Documentation/devicetree/bindings/iio/light/tsl2772.yaml index 9921ccaa64a0..38a3c82cb879 100644 --- a/Documentation/devicetree/bindings/iio/light/tsl2772.yaml +++ b/Documentation/devicetree/bindings/iio/light/tsl2772.yaml @@ -74,13 +74,13 @@ examples: #size-cells = <0>; sensor@39 { - compatible = "amstaos,tsl2772"; - reg = <0x39>; - interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>; - vdd-supply = <&pm8941_l17>; - vddio-supply = <&pm8941_lvs1>; - amstaos,proximity-diodes = <0>; - led-max-microamp = <100000>; + compatible = "amstaos,tsl2772"; + reg = <0x39>; + interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8941_l17>; + vddio-supply = <&pm8941_lvs1>; + amstaos,proximity-diodes = <0>; + led-max-microamp = <100000>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml index 0041e1db6838..2eb1932f7b14 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml @@ -4,7 +4,8 @@ $id: http://devicetree.org/schemas/iio/light/vishay,veml6030.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: VEML3235, VEML3328, VEML6030, VEML6035 and VEML7700 Ambient Light Sensors (ALS) +title: VEML3235, VEML3328, VEML6030, VEML6031x00 series, VEML6035 + and VEML7700 Ambient Light Sensors (ALS) maintainers: - Rishi Gupta @@ -23,6 +24,8 @@ description: | https://www.vishay.com/docs/80131/veml3235.pdf https://www.vishay.com/docs/84968/veml3328.pdf https://www.vishay.com/docs/84366/veml6030.pdf + https://www.vishay.com/docs/80007/veml6031x00.pdf + https://www.vishay.com/docs/80008/veml6031x01.pdf https://www.vishay.com/docs/84889/veml6035.pdf https://www.vishay.com/docs/84286/veml7700.pdf @@ -32,6 +35,10 @@ properties: - vishay,veml3235 - vishay,veml3328 - vishay,veml6030 + - vishay,veml6031x00 + - vishay,veml6031x01 + - vishay,veml60311x00 + - vishay,veml60311x01 - vishay,veml6035 - vishay,veml7700 @@ -69,6 +76,8 @@ allOf: properties: compatible: enum: + - vishay,veml6031x00 + - vishay,veml6031x01 - vishay,veml6035 then: properties: @@ -82,6 +91,8 @@ allOf: enum: - vishay,veml3235 - vishay,veml3328 + - vishay,veml60311x00 + - vishay,veml60311x01 - vishay,veml7700 then: properties: @@ -101,10 +112,10 @@ examples: #size-cells = <0>; light-sensor@10 { - compatible = "vishay,veml6030"; - reg = <0x10>; - interrupts = <12 IRQ_TYPE_LEVEL_LOW>; - vdd-supply = <&vdd>; + compatible = "vishay,veml6030"; + reg = <0x10>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + vdd-supply = <&vdd>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/pressure/infineon,dps310.yaml b/Documentation/devicetree/bindings/iio/pressure/infineon,dps310.yaml index e5d1e6c48939..1912a1b0b4ba 100644 --- a/Documentation/devicetree/bindings/iio/pressure/infineon,dps310.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/infineon,dps310.yaml @@ -45,10 +45,10 @@ examples: #size-cells = <0>; dps: pressure-sensor@76 { - compatible = "infineon,dps310"; - reg = <0x76>; - #io-channel-cells = <0>; - vdd-supply = <&vref1>; - vddio-supply = <&vref2>; + compatible = "infineon,dps310"; + reg = <0x76>; + #io-channel-cells = <0>; + vdd-supply = <&vref1>; + vddio-supply = <&vref2>; }; }; diff --git a/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml b/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml new file mode 100644 index 000000000000..dd739319b9eb --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/pulsedlight,lidar-lite-v2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Pulsedlight/Garmin LIDAR-Lite range-finding sensors + +maintainers: + - Matt Ranostay + - Rodrigo Gobbi + +description: | + This binding covers the I2C interface of the Pulsedlight/Garmin + LIDAR-Lite v2 and v3 laser rangefinders. + + Besides I2C, the mode control pin can also trigger acquisitions and + report the measured distance through a PWM signal, without using I2C + at all. That usage is bidirectional and non-cyclic, and is not + described by this binding. The same pin can also be used, while I2C + remains the active interface, as a plain status output signalling + when an acquisition has completed. + + Datasheets: + https://static.garmin.com/pumac/LIDAR_Lite_v3_Operation_Manual_and_Technical_Specifications.pdf + https://github.com/PulsedLight3D/LIDAR-Lite-Documentation/blob/master/Docs/LIDAR-Lite-v2-Docs.pdf + +properties: + compatible: + oneOf: + - items: + - enum: + - grmn,lidar-lite-v3 + - const: pulsedlight,lidar-lite-v2 + - const: pulsedlight,lidar-lite-v2 + + reg: + maxItems: 1 + + powerdown-gpios: + description: GPIO that can be driven low to shut off power to the device. + maxItems: 1 + + vdd-supply: true + + interrupts: + description: + Mode control pin used as a status output, driven while the device is + busy performing an acquisition. Can be used to signal completion + instead of polling over I2C. + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + lidar@62 { + compatible = "grmn,lidar-lite-v3", "pulsedlight,lidar-lite-v2"; + reg = <0x62>; + vdd-supply = <&vdd_5v0>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml b/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml index fbd3a2e32280..f84aece886ab 100644 --- a/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml @@ -57,8 +57,8 @@ examples: #size-cells = <0>; proximity@13 { - compatible = "vishay,vcnl3020"; - reg = <0x13>; - vishay,led-current-microamp = <200000>; + compatible = "vishay,vcnl3020"; + reg = <0x13>; + vishay,led-current-microamp = <200000>; }; }; diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml index 13e5f29f0588..09b809f01550 100644 --- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -17,6 +17,7 @@ description: | - LTC2986 - LTM2985 + https://www.analog.com/media/en/technical-documentation/data-sheets/adt7604.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/2983fc.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/2984fb.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/29861fa.pdf diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 880a9f624566..4894196c8f9c 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -142,6 +142,13 @@ properties: minItems: 2 maxItems: 4 + spi-device-addr: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Device addresses used when multiple peripherals share a single chip + select. The array allows one logical peripheral to comprise multiple + physical devices, with one address per physical device. + st,spi-midi-ns: deprecated: true description: | diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 2de8eb09cb7d..002543c71edf 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -62,6 +62,8 @@ properties: # Aosong temperature & humidity sensors with I2C interface - aosong,aht10 - aosong,aht20 + - aosong,am2315 + - aosong,am2320 - aosong,dht20 # Arduino microcontroller interface over SPI on UnoQ board - arduino,unoq-mcu @@ -77,8 +79,6 @@ properties: - atmel,atsha204a # BPA-RS600: Power Supply - blutek,bpa-rs600 - # CM32181: Ambient Light Sensor - - capella,cm32181 # CM3232: Ambient Light Sensor - capella,cm3232 # CM3323: Ambient Light Sensor @@ -400,8 +400,6 @@ properties: - onnn,adt7462 # 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch - plx,pex8648 - # Pulsedlight LIDAR range-finding sensor - - pulsedlight,lidar-lite-v2 # Renesas HS3001 Temperature and Relative Humidity Sensors - renesas,hs3001 # Renesas ISL29501 time-of-flight sensor diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst new file mode 100644 index 000000000000..9f5fbcae7173 --- /dev/null +++ b/Documentation/iio/ad7768.rst @@ -0,0 +1,275 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD7768 driver +============= + +ADC driver for Analog Devices Inc. AD7768 and AD7768-4 devices. The module name +is ``ad7768``. + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD7768 `_ - + 8-channel, 24-bit simultaneous sampling ADC +* `AD7768-4 `_ - + 4-channel, 24-bit simultaneous sampling ADC + +Supported features +================== + +Clock sources +------------- + +The master clock source is selected using the ``clock-names`` device tree +property: + +* ``mclk`` selects a CMOS clock on XTAL2/MCLK. CLK_SEL must be low and XTAL1 + must be connected to DGND. +* ``xtal`` selects a crystal connected between XTAL1 and XTAL2. CLK_SEL must + be high. The driver drives FILTER/GPIO4 high and keeps channel 4 on the + AD7768 or channel 2 on the AD7768-4 enabled to maintain crystal excitation. +* ``lvds`` selects a differential clock on XTAL1 and XTAL2. CLK_SEL must be + high. The driver drives FILTER/GPIO4 low before enabling the LVDS input. + +GPIO4 is reserved when ``xtal`` or ``lvds`` is selected. + +Power modes +----------- + +The AD7768 family supports three power and performance modes: + +* **Low power mode** - Optimized for lowest power consumption +* **Median mode** - Balanced power and performance +* **Fast mode** - Highest performance with maximum sampling rates + +The driver initializes the device in fast mode and uses the maximum fast-mode +output data rate as the default sampling frequency. + +When buffered capture starts, the driver selects the lowest-noise mode that can +produce the requested output data rates for all enabled channels. Where output +data rates overlap, fast mode is preferred over median mode, and median mode is +preferred over low power mode. This prioritizes the lower RMS noise and higher +dynamic range offered by a faster mode at the same output data rate. + +Data output configuration +------------------------- + +The devices support flexible serial data output configurations: + +AD7768 data lines +^^^^^^^^^^^^^^^^^ + +* 1 data line (DOUT0) - Standard single-lane output +* 2 data lines (DOUT0, DOUT1) - Dual-lane output for higher throughput +* 8 data lines (DOUT0-DOUT7) - Maximum throughput, one line per channel + +AD7768-4 data lines +^^^^^^^^^^^^^^^^^^^ + +* 1 data line (DOUT0) - Standard single-lane output +* 4 data lines (DOUT0-DOUT3) - Maximum throughput, one line per channel + +The number of data lines can be configured via the ``adi,data-lines-number`` +device tree property. If omitted, the driver uses the maximum supported by the +selected variant: eight lines for AD7768 and four lines for AD7768-4. + +Channel configuration +--------------------- + +Each channel can be individually configured with: + +Channel modes +^^^^^^^^^^^^^ + +* **Mode A** - First set of filter and decimation settings +* **Mode B** - Second set of filter and decimation settings + +The hardware provides two mode profiles (A and B), each holding one +(frequency, filter) combination. When buffered capture is started, +enabled channels are grouped by their configured (frequency, filter) +pair. Up to two distinct groups are supported; the driver automatically +assigns each group to a mode slot and programs the hardware accordingly. +If the enabled channels request more than two distinct pairs, buffer setup +fails and capture does not start. + +Precharge and reference buffers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Per-channel buffer control for optimal signal integrity: + +* Positive input precharge buffer (``adi,prechargebuf-pos-enable``) +* Negative input precharge buffer (``adi,prechargebuf-neg-enable``) +* Positive reference buffer (``adi,refbuf-pos-enable``) +* Negative reference buffer (``adi,refbuf-neg-enable``) + +Common mode voltage +------------------- + +The VCM pin provides a buffered common-mode voltage output used to bias +the analog inputs. The driver exposes this as a standard voltage regulator +provider under a ``regulators`` subnode in the device tree. Supported +output voltage levels are: + +* (AVDD1 - AVSS) / 2 - Mid-supply (hardware default), reported as half the + voltage provided by ``avdd1-supply`` +* 1,650,000 µV - 1.65V +* 2,500,000 µV - 2.5V +* 2,140,000 µV - 2.14V + +The regulator can be enabled and disabled at runtime using the standard +regulator framework interfaces. + +The VCM circuitry is associated with channel 0. When VCM is used externally, +``channel@0`` must be present in the device tree and channel 0 must remain +enabled in the active scan mask. Placing channel 0 in standby disables the VCM +output. + +Filter types +------------ + +Two digital filter types are available: + +* **Wideband** - Optimized for wide bandwidth applications +* **Sinc5** - Fifth-order sinc filter for high rejection of out-of-band noise + +IIO backend support +------------------- + +The driver integrates with IIO backends (e.g., AXI ADC) for high-speed data +capture and DMA operations. Features include: + +* Automatic channel enable/disable based on scan mask +* CRC on data interface. CRC replaces the header every 4th output sample. +* High-throughput buffered data acquisition + +GPIO controller +--------------- + +The AD7768 includes a 5-pin GPIO controller for auxiliary digital I/O +operations. The GPIO pins can be configured as inputs or outputs. + +GPIO4 is reserved when a crystal or LVDS master clock is used. GPIO0 through +GPIO3 remain available in these configurations. + +Device attributes +================= + +The following IIO attributes are available for each enabled channel: + +Voltage scale +------------- + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Attribute + - Description + * - ``in_voltage_scale`` + - Scale in millivolts per least significant bit for signed 24-bit data, + calculated as twice the differential reference voltage used by channel + N divided by 2\ :sup:`24`. + +Sampling frequency +------------------ + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Attribute + - Description + * - ``in_voltage_sampling_frequency`` + - Requested sampling frequency in Hz for channel N. Enabled channels are + grouped into up to two profiles at capture time. + * - ``in_voltage_sampling_frequency_available`` + - Available sampling frequencies in Hz for channel N across all power + modes, based on the master clock frequency. Buffer setup fails if no + single power mode supports the frequencies requested by all enabled + channels. + +Filter configuration +--------------------- + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Attribute + - Description + * - ``in_voltage_filter_type`` + - Requested filter type for channel N: "wideband" or "sinc5". It is + grouped with sampling frequency at capture time. + * - ``in_voltage_filter_type_available`` + - Available filter types for channel N: "wideband sinc5". + +Per-channel calibration +----------------------- + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Attribute + - Description + * - ``in_voltage_calibbias`` + - Signed 24-bit two's complement channel offset register value. + * - ``in_voltage_calibscale`` + - Raw unsigned 24-bit channel gain register value. + * - ``in_voltage_convdelay`` + - Per-channel conversion delay. The driver exposes the sync phase offset + value in seconds with picosecond precision. Resolution and valid range + depend on the decimation ratio in use (see datasheet + Table 32). + +Device buffers +============== + +This driver supports IIO buffered data acquisition through IIO backends. +When used with compatible backends like the AXI ADC, it provides: + +* High-speed simultaneous sampling across all enabled channels +* Hardware-driven data capture +* DMA-based data transfer for minimal CPU overhead +* CRC error detection + +See :doc:`iio_devbuf` for more information about IIO device buffers. + +Example usage +============= + +.. code-block:: bash + + # Read current sampling frequency for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_sampling_frequency + + # Update sampling frequency for channel 0 + echo 8000 > /sys/bus/iio/devices/iio:device0/in_voltage0_sampling_frequency + + # Read current filter type for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type + + # List available filter types for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type_available + + # Update filter type for channel 0 to wideband + echo wideband > /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type + + # Buffer setup fails if enabled channels request more than two distinct + # (sampling frequency, filter type) combinations. + + # Read calibration scale for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_calibscale + + # Read conversion delay for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_convdelay + + +Unimplemented features +====================== + +* CRC message every 16 samples (CRC_SEL configuration) - currently only + supports CRC every 4 samples diff --git a/Documentation/iio/ad9910.rst b/Documentation/iio/ad9910.rst new file mode 100644 index 000000000000..aab569f561db --- /dev/null +++ b/Documentation/iio/ad9910.rst @@ -0,0 +1,792 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD9910 driver +============= + +Direct Digital Synthesizer (DDS) driver for the Analog Devices Inc. AD9910. +The module name is ``ad9910``. + +* `AD9910 `_ + +The AD9910 is a 1 GSPS DDS with a 14-bit DAC, controlled over SPI. The driver +exposes the device through a hierarchy of typed IIO output channels. The root +``phy`` channel controls the system clock and full-scale output current. +Sub-channels provide independent control over single tone profiles, parallel +port modulation, digital ramp generation (DRG), RAM playback and output shift +keying (OSK). + + +Channel hierarchy +================= + +The driver exposes the following IIO output channels, each identified by a +unique channel number and a human-readable label. The ``phy`` channel is the +root of the hierarchy. Changing its ``sampling_frequency`` reconfigures the +system clock (SYSCLK) which affects all other channels. + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Label + - Parent + - Description + + * - ``out_altcurrent100`` + - ``phy`` + - + - Physical output: system clock and full-scale output current (:math:`I_{FS}`). + See `Physical channel`_. + + * - ``out_altcurrent110`` ... ``out_altcurrent117`` + - ``profile0`` ... ``profile7`` + - ``phy`` + - Single tone control: frequency, phase, amplitude, enable. + See `Single Tone mode`_. + + * - ``out_altcurrent120`` + - ``parallel_amplitude`` + - ``phy`` + - Parallel port amplitude channel. + See `Parallel Port mode`_. + + * - ``out_phase120`` + - ``parallel_phase`` + - ``phy`` + - Parallel port phase channel. + + * - ``out_frequency120`` + - ``parallel_frequency`` + - ``phy`` + - Parallel port frequency channel: ``scale`` sets the FM gain + (power-of-2 multiplier), ``offset`` sets the base FTW. + + * - ``out_altcurrent121`` + - ``parallel_polar_amplitude`` + - ``phy`` + - Parallel polar amplitude channel: ``scale`` is the amplitude + resolution, ``offset`` is the amplitude bias (lower 6 bits of ASF). + + * - ``out_phase121`` + - ``parallel_polar_phase`` + - ``phy`` + - Parallel polar phase channel: ``scale`` is the phase resolution, + ``offset`` is the phase bias (lower 8 bits of POW). + + * - ``out_frequency130`` + - ``drg_frequency`` + - ``phy`` + - DRG frequency channel: ``en`` selects and enables the DRG with + frequency as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_frequency131`` + - ``drg_rising_frequency`` + - ``drg_frequency`` + - DRG frequency rising-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_frequency132`` + - ``drg_falling_frequency`` + - ``drg_frequency`` + - DRG frequency falling-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_phase130`` + - ``drg_phase`` + - ``phy`` + - DRG phase channel: ``en`` selects and enables the DRG with phase + as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_phase131`` + - ``drg_rising_phase`` + - ``drg_phase`` + - DRG phase rising-ramp parameters: limit code, dwell enable, ramp clock, + rate of change. + + * - ``out_phase132`` + - ``drg_falling_phase`` + - ``drg_phase`` + - DRG phase falling-ramp parameters: limit code, dwell enable, ramp clock, + rate of change. + + * - ``out_altcurrent130`` + - ``drg_amplitude`` + - ``phy`` + - DRG amplitude channel: ``en`` selects and enables the DRG with + amplitude as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_altcurrent131`` + - ``drg_rising_amplitude`` + - ``drg_amplitude`` + - DRG amplitude rising-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_altcurrent132`` + - ``drg_falling_amplitude`` + - ``drg_amplitude`` + - DRG amplitude falling-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_altcurrent140`` + - ``ram`` + - ``phy`` + - RAM playback: enable, frequency, phase and sampling frequency for + the active profile. See `RAM mode`_. + + * - ``out_altcurrent150`` + - ``osk`` + - ``phy`` + - Output shift keying (OSK): enable, amplitude code, ramp rate, + rate of change. See `Output Shift Keying (OSK)`_. + +DDS modes +========= + +The AD9910 supports multiple modes of operation that can be configured +independently or in combination. Each DDS core parameter (frequency, phase +and amplitude) can come from different sources, but only one is active at a +time. This activation depends on a priority list, which is based on the enable +and destination configurations for such modes. The following tables are +extracted from the AD9910 datasheet and summarize the control parameters for +each mode and their priority when multiple sources are enabled simultaneously: + +.. flat-table:: DDS Frequency Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - RAM + - RAM enabled and data destination is frequency + + * - + - DRG + - DRG enabled and data destination is frequency + + * - + - Parallel data and Frequency Tuning Word, FTW (frequency_offset) + - Parallel data port enabled and data destination is frequency + + * - + - FTW register (frequency) + - RAM enabled and data destination is not frequency + + * - Lowest Priority + - FTW (frequency) in single tone channel for the active profile + - All other cases + +.. flat-table:: DDS Phase Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - RAM + - RAM enabled and data destination is phase or polar + + * - + - DRG + - DRG enabled and data destination is phase + + * - + - Parallel data port + - Parallel data port enabled and data destination is phase + + * - + - Parallel data port and Phase Offset Word, POW register LSBs (phase_offset) + - Parallel data port enabled and data destination is polar + + * - + - POW register (phase) + - RAM enabled and destination is not phase nor polar + + * - Lowest Priority + - POW (phase) in single tone channel for the active profile + - All other cases + +.. flat-table:: DDS Amplitude Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - Amplitude Scale Factor, ASF register and OSK generator + - OSK enabled + + * - + - RAM + - RAM enabled and data destination is amplitude or polar + + * - + - DRG + - DRG enabled and data destination is amplitude + + * - + - Parallel data port + - Parallel data port enabled and data destination is amplitude + + * - + - Parallel data port and ASF register LSBs (scale_offset) + - Parallel data port enabled and data destination is polar + + * - Lowest Priority + - ASF (scale) in single tone channel for the active profile + - (Amplitude scale is already enabled by default) + +While debugging or testing, the debug attributes ``frequency_source``, +``phase_source`` and ``amplitude_source`` can be used to read the label of +the channel that is actively controlling the correspondent DDS parameter, +which reflects the priority list described above. + +Single Tone mode +---------------- + +Single tone is the baseline operating mode. The ``profileY`` channels +provide enable, frequency, phase and amplitude control: + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean (0 or 1) + - Enable/disable profile Y. Only one profile can be active at a + time. When enabling a profile it disables the current active profile. + Disabling an active profile brings the device to a powered down state. + + * - ``frequency`` + - Hz + - Output frequency. Range :math:`[0, f_{SYSCLK} / 2)`. Stored in the + profile's frequency tuning word (FTW). + + * - ``phase`` + - rad + - Phase offset. Range :math:`[0, 2\pi)`. Stored in the profile's phase + offset word (POW). + + * - ``raw`` + - integer + - Amplitude scale factor code. Range :math:`[0, 16383]`. Stored in the + profile's amplitude scale factor (ASF) register. The physical output + amplitude is ``raw * scale`` where ``scale`` is read from the ``phy`` + channel. + +Profile switching is allowed while RAM mode is enabled. In that case single +tone parameters are stored in a shadow register and are not written to +hardware until RAM mode is disabled. + +Usage examples +^^^^^^^^^^^^^^ + +Configure a 100 MHz tone in profile 2 and set it as the active profile: + +.. code-block:: bash + + echo 100000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_frequency + echo 0 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_phase + echo 16383 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_raw + + # Activate profile 2 + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_en + +Read back the current single tone frequency: + +.. code-block:: bash + + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent112_frequency + +Parallel Port mode +------------------ + +The parallel port allows real-time modulation of DDS parameters through a +16-bit external data bus. The driver exposes separate typed channels for each +modulation target. + +Non-polar modulation +^^^^^^^^^^^^^^^^^^^^ + +In non-polar mode each DDS parameter is controlled by an independent 16-bit bus +input. The parallel port channels expose the resolution and base offset of their +respective bus inputs: + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Attribute + - Unit + - Description + + * - ``out_phase120`` + - ``scale`` + - rad + - Phase resolution per parallel bus LSB. Fixed at :math:`\pi / 2^{15}` + rad/LSB (full 16-bit POW resolution). + + * - ``out_frequency120`` + - ``scale`` + - Hz + - Outputs the frequency scale evaluated as + :math:`f_{SYSCLK} \cdot FM / 2^{32}`. Assuming that :math:`f_{SYSCLK}` is + fixed, it is used to configure the modulation gain :math:`FM`, which is a + power-of-2 multiplier in range :math:`[1, 32768]`. Writing to this + attribute rounds up to the nearest :math:`FM` power of 2. + + * - ``out_frequency120`` + - ``offset`` + - Hz + - Outputs the frequency offset in raw units evaluated as :math:`FTW / FM`. + Assuming that :math:`FM` is fixed, it is used to configure the FTW + register, which is a 32-bit unsigned integer. + +Polar modulation +^^^^^^^^^^^^^^^^ + +In polar mode a single 16-bit bus word carries both amplitude (high byte) +and phase (low byte). The ``parallel_polar_amplitude`` and +``parallel_polar_phase`` channels configure the bias applied to the low-order +bits of the ASF and POW registers respectively: + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Attribute + - Unit + - Description + + * - ``out_altcurrent121`` + - ``scale`` + - mA/LSB + - Full-scale amplitude resolution (read-only, fixed at :math:`I_{FS} / 2^{8}`). + + * - ``out_altcurrent121`` + - ``offset`` + - fractional (raw units) + - Amplitude bias. Lower 6 bits of ASF register. Range :math:`[0, 1)`. + + * - ``out_phase121`` + - ``scale`` + - rad/LSB + - Phase resolution (read-only, fixed at :math:`\pi / 2^{7}`). + + * - ``out_phase121`` + - ``offset`` + - fractional (raw units) + - Phase bias. Lower 8 bits of POW register. Range :math:`[0, 1)`. + +Usage examples +^^^^^^^^^^^^^^ + +Set parallel port frequency modulation with a modulation gain of 16 and a 50 MHz +offset: + +.. code-block:: bash + + # f_SYSCLK = 1 GHz, FM = 16 + # frequency scale = f_SYSCLK * FM / 2^32 = 3.725290298 + echo 3.725290298 > /sys/bus/iio/devices/iio\:device0/out_frequency120_scale + # frequency offset = 50 MHz / scale = 50e6 / 13421772.8 + echo 13421772.8 > /sys/bus/iio/devices/iio\:device0/out_frequency120_offset + +One should choose a frequency scale that allows all the desired frequencies +to be represented in the 16-bit bus range, i.e., +:math:`scale = (f_{max} - f_{min}) / 2^{16}`. + + +Digital ramp generator (DRG) +---------------------------- + +The DRG produces linear frequency, phase or amplitude sweeps using dedicated +hardware. The active ramp target (destination) is selected by enabling the +corresponding typed channel at channel number 130: + +- ``out_frequency130`` (label ``drg_frequency``) — ramp targets frequency +- ``out_phase130`` (label ``drg_phase``) — ramp targets phase +- ``out_altcurrent130`` (label ``drg_amplitude``) — ramp targets amplitude + +Writing ``en=1`` to one of these channels enables the DRG and switches its +destination. Writing ``en=0`` disables the DRG if the channel is the current +active destination; writing to an already-inactive destination is a no-op. + +Each destination channel also exposes a read-only ``scale`` attribute +reporting the physical quantity per ramp register LSB, which allows converting +raw limit codes to physical values. + +The six ramp channels: + +- ``out_frequency131`` (label ``drg_rising_frequency``) +- ``out_frequency132`` (label ``drg_falling_frequency``) +- ``out_phase131`` (label ``drg_rising_phase``) +- ``out_phase132`` (label ``drg_falling_phase``) +- ``out_altcurrent131`` (label ``drg_rising_amplitude``) +- ``out_altcurrent132`` (label ``drg_falling_amplitude``) + +configure ascending and descending ramp parameters. + +Destination channel attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean + - Enable the DRG with this channel as the active destination. Only one + destination can be active at a time. + + * - ``scale`` + - Hz/LSB, rad/LSB or mA/LSB + - Read-only. Physical quantity per raw units. Multiply a ramp + rising/falling channel ``raw`` value by this scale to get the physical + ramp target. + +Ramp channel attributes +^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``dwell_en`` + - boolean + - Enable dwell at the ramp limit. When disabled, the ramp + auto-transitions at this limit without waiting for the DRCTL pin. + Disabling both creates a bidirectional continuous ramp (triangular + pattern). Other combinations create single-shot ramps at the DRCTL + pin transition. + + * - ``raw`` + - integer (64-bit) + - Ramp limit expressed as a raw DRG register code in + :math:`[0, 2^{32}-1]`. The physical value is ``raw * scale`` where + ``scale`` is read from the active destination channel. + + * - ``sampling_frequency`` + - Hz + - Ramp clock rate. Controlled by an integer divider; the written value + is adjusted to the nearest supported rate. + + * - ``raw_roc`` + - /s + - Rate of change. Number of register codes advanced per second, computed + from the hardware step size and the current ramp clock. Writing + requires ``sampling_frequency`` to be configured first. + +Usage examples +^^^^^^^^^^^^^^ + +Configure a frequency sweep from 40 MHz to 60 MHz with a rate of change of +25 GHz/s: + +.. code-block:: bash + + # Disable dwell on both limits for a bidirectional continuous ramp + echo 0 > /sys/bus/iio/devices/iio\:device0/out_frequency131_dwell_en + echo 0 > /sys/bus/iio/devices/iio\:device0/out_frequency132_dwell_en + + # Set ramp rate at 250 MHz + echo 250000000 > /sys/bus/iio/devices/iio\:device0/out_frequency131_sampling_frequency + echo 250000000 > /sys/bus/iio/devices/iio\:device0/out_frequency132_sampling_frequency + + # read the frequency scale to convert physical values to raw units + cat /sys/bus/iio/devices/iio\:device0/out_frequency130_scale + 0.232830643650 + + # 40 MHz / 0.232830643650 = 171798692 + echo 171798692 > /sys/bus/iio/devices/iio\:device0/out_frequency131_raw + # 60 MHz / 0.232830643650 = 257698038 + echo 257698038 > /sys/bus/iio/devices/iio\:device0/out_frequency132_raw + + # 25 GHz/s / 0.232830643650 = 107374182402 + echo 107374182402 > /sys/bus/iio/devices/iio\:device0/out_frequency131_raw_roc + echo 107374182402 > /sys/bus/iio/devices/iio\:device0/out_frequency132_raw_roc + + # Enable the DRG with frequency as the destination + echo 1 > /sys/bus/iio/devices/iio\:device0/out_frequency130_en + +RAM mode +-------- + +The AD9910 contains a 1024 x 32-bit RAM that can be loaded with waveform data +and played back to modulate frequency, phase, amplitude, or polar (phase + +amplitude) parameters. + +RAM control channel attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean + - Enable/disable RAM playback. Toggling swaps profile registers between + single tone and RAM configurations across all 8 profiles. + + * - ``frequency`` + - Hz + - Frequency tuning word used as the single tone frequency when + RAM destination is not ``frequency``. Range: :math:`[0, f_{SYSCLK} / 2)`. + + * - ``phase`` + - rad + - Phase offset word used as the single tone phase when RAM destination + is not ``phase``. Range: :math:`[0, 2\pi)`. + + * - ``sampling_frequency`` + - Hz + - RAM playback step rate of the active profile, which controls how fast + the address counter advances. Controlled by an integer divider; the + written value is adjusted to the nearest supported rate. + +Loading RAM data +^^^^^^^^^^^^^^^^ + +RAM data is loaded through the firmware upload framework. The driver registers +a firmware upload sysfs entry named ``iio_deviceX:ram``. The firmware data +follows a binary format (version 1) with an 80-byte header followed by data +words. All fields are big-endian. + +.. flat-table:: RAM firmware header (80 bytes) + :header-rows: 1 + + * - Offset + - Size + - Field + - Description + + * - 0 + - 4 + - ``magic`` + - Magic number: ``0x00AD9910`` + + * - 4 + - 2 + - ``version`` + - Format version: ``0x0001`` + + * - 6 + - 2 + - ``wcount`` + - Number of 32-bit RAM data words (0--1024) + + * - 8 + - 4 + - ``crc`` + - CRC32 checksum over ``cfr1``, ``profiles`` and ``words`` + + * - 12 + - 4 + - ``cfr1`` + - CFR1 register value. Only RAM-relevant bits are used: + bits [30:29] set data destination (00: frequency, 01: phase, + 10: amplitude, 11: polar); bits [20:17] set internal profile + control (see datasheet Table 14) + + * - 16 + - 64 + - ``profiles[0..7]`` + - 8 sets of 8-byte RAM profile configurations (see below) + + * - 80 + - 4 x wcount + - ``words[]`` + - RAM data words in reverse order + +Each 8-byte profile entry contains: + +.. flat-table:: RAM profile entry (8 bytes) + :header-rows: 1 + + * - Bits + - Field + - Description + + * - [55:40] + - Address step rate + - Controls playback speed for this profile + + * - [39:30] + - End address + - Last RAM address for this profile + + * - [23:14] + - Start address + - First RAM address for this profile + + * - [5] + - No-dwell high + - No-dwell at high limit (ramp-up mode) + + * - [3] + - Zero-crossing + - Zero-crossing enable (direct-switch mode) + + * - [2:0] + - Operating mode + - 000: direct switch, 001: ramp-up, 010: bidirectional, + 011: bidirectional continuous, 100: ramp-up continuous + +Usage examples +^^^^^^^^^^^^^^ + +Configure RAM mode with firmware data and enable it: + +.. code-block:: bash + + # Load RAM data via firmware upload + echo 1 > /sys/class/firmware/iio\:device0\:ram/loading + cat ad9910-ram.bin > /sys/class/firmware/iio\:device0\:ram/data + echo 0 > /sys/class/firmware/iio\:device0\:ram/loading + + # Enable RAM mode + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent140_en + +Output Shift Keying (OSK) +------------------------- + +OSK controls the output amplitude envelope, allowing the output to be ramped +on/off rather than switched abruptly. + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean (0 or 1) + - Enable/disable OSK. + + * - ``raw`` + - integer + - Target amplitude code. 14-bit ASF field. Range: :math:`[0, 16383]`. + The physical output amplitude is ``raw * scale`` where ``scale`` is read + from the ``phy`` channel. + + * - ``raw_roc`` + - /s + - Amplitude ramp rate. Writing a non-zero value enables automatic OSK + and selects the closest hardware step size. Writing ``0`` disables + automatic ramping (manual control via ``raw``). Writing the maximum + available value enables pin-controlled immediate transition. + + * - ``raw_roc_available`` + - /s + - Lists the available ``raw_roc`` values based on the current + ``sampling_frequency``. The first value is always ``0`` (disabled) and + the last value corresponds to pin-controlled immediate mode. + + * - ``sampling_frequency`` + - Hz + - OSK ramp clock. Controlled by an integer divider; the written value + is adjusted to the nearest supported rate. + +Usage examples +^^^^^^^^^^^^^^ + +Enable OSK with automatic ramping: + +.. code-block:: bash + + # Set ramp rate 1MHz + echo 1000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_sampling_frequency + + # Check available rate of change values + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_roc_available + 0 1000000 2000000 4000000 8000000 16383000000 + + # Enable automatic OSK with a rate of change of 8000000 raw units/s + echo 8000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_roc + + # Enable OSK + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_en + +Enable pin-controlled immediate OSK: + +.. code-block:: bash + + # Enable OSK in manual mode (no ramp) + echo 0 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_roc + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_en + + # Set target amplitude to full scale + echo 16383 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw + +Physical channel +================ + +The ``phy`` channel provides device-level control: + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``sampling_frequency`` + - Hz + - System clock (SYSCLK) frequency. When the internal PLL is enabled + (via the ``adi,pll-enable`` devicetree property), configures the PLL + multiplier (Range: :math:`[420, 1000]` MHz). Without PLL, the reference + clock can only be divided by 2. + + * - ``scale`` + - mA/LSB + - Full-scale DAC output current per amplitude code LSB, which is evaluated + as :math:`I_{FS} / 2^{14}`. Shared across all ``altcurrent`` channels. + Setting this attribute reconfigures the auxiliary DAC full-scale code and + updates the effective amplitude resolution for single tone profiles, + DRG amplitude ramps and OSK. + + * - ``powerdown`` + - boolean (0 or 1) + - Software power-down. Writing 1 powers down the digital core, DAC, + reference clock input and auxiliary DAC simultaneously. + +Usage examples +-------------- + +Set the system clock to 1 GHz: + +.. code-block:: bash + + echo 1000000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent100_sampling_frequency + +Read current system clock frequency: + +.. code-block:: bash + + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent100_sampling_frequency + +Power down the device: + +.. code-block:: bash + + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent100_powerdown diff --git a/Documentation/iio/ade9000.rst b/Documentation/iio/ade9000.rst index c9ff702a4251..f41660005223 100644 --- a/Documentation/iio/ade9000.rst +++ b/Documentation/iio/ade9000.rst @@ -1,24 +1,30 @@ .. SPDX-License-Identifier: GPL-2.0 -=============== -ADE9000 driver -=============== +====================== +ADE9000/ADE9078 driver +====================== -This driver supports Analog Device's ADE9000 energy measurement IC on SPI bus. +This driver supports Analog Devices' ADE9000 and ADE9078 energy measurement +ICs on SPI bus. 1. Supported devices ==================== * `ADE9000 `_ +* `ADE9078 `_ -The ADE9000 is a highly accurate, fully integrated, multiphase energy and power -quality monitoring device. Superior analog performance and a digital signal -processing (DSP) core enable accurate energy monitoring over a wide dynamic -range. An integrated high end reference ensures low drift over temperature -with a combined drift of less than ±25 ppm/°C maximum for the entire channel -including a programmable gain amplifier (PGA) and an analog-to-digital +The ADE9000 and ADE9078 are highly accurate, fully integrated, multiphase energy +and power quality monitoring devices. Superior analog performance and a digital +signal processing (DSP) core enable accurate energy monitoring over a wide +dynamic range. An integrated high end reference ensures low drift over +temperature with a combined drift of less than ±25 ppm/°C maximum for the entire +channel including a programmable gain amplifier (PGA) and an analog-to-digital converter (ADC). +The ADE9078 does not provide the on-chip dip/swell (sag) detection available on +the ADE9000, so the RMS voltage swell/dip events described below are only +available on the ADE9000. + 2. Device attributes ==================== diff --git a/Documentation/iio/adis16475.rst b/Documentation/iio/adis16475.rst index 89a388490ab7..0e004f016199 100644 --- a/Documentation/iio/adis16475.rst +++ b/Documentation/iio/adis16475.rst @@ -4,7 +4,7 @@ ADIS16475 driver ================ -This driver supports Analog Device's IMUs on SPI bus. +This driver supports Analog Devices' IMUs on SPI bus. 1. Supported devices ==================== diff --git a/Documentation/iio/adis16480.rst b/Documentation/iio/adis16480.rst index cce5f3e01741..7c259070aaaf 100644 --- a/Documentation/iio/adis16480.rst +++ b/Documentation/iio/adis16480.rst @@ -4,7 +4,7 @@ ADIS16480 driver ================ -This driver supports Analog Device's IMUs on SPI bus. +This driver supports Analog Devices' IMUs on SPI bus. 1. Supported devices ==================== diff --git a/Documentation/iio/adis16550.rst b/Documentation/iio/adis16550.rst index c9bbc0a857b0..c0d5a975e6ca 100644 --- a/Documentation/iio/adis16550.rst +++ b/Documentation/iio/adis16550.rst @@ -4,7 +4,7 @@ ADIS16550 driver ================ -This driver supports Analog Device's IMUs on SPI bus. +This driver supports Analog Devices' IMUs on SPI bus. 1. Supported devices ==================== diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst index 3641193bf647..c448f10745e7 100644 --- a/Documentation/iio/adxl313.rst +++ b/Documentation/iio/adxl313.rst @@ -4,7 +4,7 @@ ADXL313 driver =============== -This driver supports Analog Device's ADXL313 on SPI/I2C bus. +This driver supports Analog Devices' ADXL313 on SPI/I2C bus. 1. Supported devices ==================== diff --git a/Documentation/iio/adxl345.rst b/Documentation/iio/adxl345.rst index 0aa33a852412..28c71fbd45e5 100644 --- a/Documentation/iio/adxl345.rst +++ b/Documentation/iio/adxl345.rst @@ -4,7 +4,7 @@ ADXL345 driver =============== -This driver supports Analog Device's ADXL345/375 on SPI/I2C bus. +This driver supports Analog Devices' ADXL345/375 on SPI/I2C bus. 1. Supported Devices ==================== diff --git a/Documentation/iio/adxl380.rst b/Documentation/iio/adxl380.rst index 654d4c0e84a1..969ded211cc9 100644 --- a/Documentation/iio/adxl380.rst +++ b/Documentation/iio/adxl380.rst @@ -4,7 +4,7 @@ ADXL380 driver =============== -This driver supports Analog Device's ADXL380/382 on SPI/I2C bus. +This driver supports Analog Devices' ADXL380/382 on SPI/I2C bus. 1. Supported devices ==================== @@ -39,13 +39,13 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +---------------------------------------------------+----------------------------------------------------------+ | in_accel_scale | Scale for the accelerometer channels. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_filter_high_pass_3db_frequency | Low pass filter bandwidth. | +| in_accel_filter_high_pass_3db_frequency | High pass filter bandwidth. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_filter_high_pass_3db_frequency_available | Available low pass filter bandwidth configurations. | +| in_accel_filter_high_pass_3db_frequency_available | Available high pass filter bandwidth configurations. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_filter_low_pass_3db_frequency | High pass filter bandwidth. | +| in_accel_filter_low_pass_3db_frequency | Low pass filter bandwidth. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_filter_low_pass_3db_frequency_available | Available high pass filter bandwidth configurations. | +| in_accel_filter_low_pass_3db_frequency_available | Available low pass filter bandwidth configurations. | +---------------------------------------------------+----------------------------------------------------------+ | in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. | +---------------------------------------------------+----------------------------------------------------------+ diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index b02b879b053a..d0fb32f4b80f 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -29,7 +29,9 @@ Industrial I/O Kernel Drivers ad7380 ad7606 ad7625 + ad7768 ad7944 + ad9910 ade9000 adf41513 adis16475 diff --git a/MAINTAINERS b/MAINTAINERS index 72294ddfa5b7..3cef938702da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -640,6 +640,7 @@ F: drivers/iio/accel/adxl355_spi.c ADXL367 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Marcelo Schmitt +M: Antoniu Miclaus L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1468,6 +1469,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml +F: Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml F: drivers/iio/dac/ad3530r.c ANALOG DEVICES INC AD3552R DRIVER @@ -1517,6 +1519,7 @@ F: drivers/iio/adc/ad4080.c ANALOG DEVICES INC AD4130 DRIVER M: Marcelo Schmitt +M: Antoniu Miclaus L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1571,6 +1574,14 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml F: drivers/iio/adc/ad4851.c +ANALOG DEVICES INC AD5529R DRIVER +M: Janani Sunil +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml +F: drivers/iio/dac/ad5529r.c + ANALOG DEVICES INC AD5706R DRIVER M: Alexis Czezar Torreno L: linux-iio@vger.kernel.org @@ -1622,6 +1633,7 @@ F: drivers/iio/dac/ad7293.c ANALOG DEVICES INC AD74115 DRIVER M: Marcelo Schmitt +M: Antoniu Miclaus L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1630,6 +1642,7 @@ F: drivers/iio/addac/ad74115.c ANALOG DEVICES INC AD74413R DRIVER M: Marcelo Schmitt +M: Antoniu Miclaus L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1657,6 +1670,15 @@ 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 AD7768 DRIVER +M: Janani Sunil +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml +F: Documentation/iio/ad7768.rst +F: drivers/iio/adc/ad7768.c + ANALOG DEVICES INC AD7780 DRIVER M: Michael Hennerich M: Renato Lui Geh @@ -1702,6 +1724,17 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml F: drivers/iio/dac/ad9739a.c +ANALOG DEVICES INC AD9910 DRIVER +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/ABI/testing/debugfs-iio-ad9910 +F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 +F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml +F: Documentation/iio/ad9910.rst +F: drivers/iio/frequency/ad9910.c + ANALOG DEVICES INC MAX22007 DRIVER M: Janani Sunil L: linux-iio@vger.kernel.org @@ -4439,6 +4472,15 @@ S: Orphan F: Documentation/devicetree/bindings/sound/axentia,* F: sound/soc/atmel/tse850-pcm5142.c +AXIADO SARADC DRIVER +M: Petar Stepanovic +M: Akhila Kavi +M: Prasad Bolisetty +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml +F: drivers/iio/adc/axiado_saradc.c + AXIS ARTPEC ARM64 SoC SUPPORT M: Jesper Nilsson M: Lars Persson @@ -12697,6 +12739,7 @@ R: Nuno Sá R: Andy Shevchenko L: linux-iio@vger.kernel.org S: Maintained +Q: https://patchwork.kernel.org/project/linux-iio/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git F: Documentation/ABI/testing/configfs-iio* F: Documentation/ABI/testing/sysfs-bus-iio* @@ -12709,6 +12752,13 @@ F: include/dt-bindings/iio/ F: include/linux/iio/ F: tools/iio/ +IIO CORE KUNIT TESTS +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/test/iio-test-channel-prefix.c +F: drivers/iio/test/iio-test-format.c + IIO UNIT CONVERTER M: Peter Rosin L: linux-iio@vger.kernel.org @@ -16123,6 +16173,15 @@ L: linux-iio@vger.kernel.org S: Supported F: drivers/iio/temperature/max30208.c +MAXIM MAX40080 CURRENT SENSE AMPLIFIER DRIVER +M: Ciprian Hegbeli +M: Stefan Popa +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml +F: drivers/iio/adc/max40080.c + MAXIM MAX7360 KEYPAD LED MFD DRIVER M: Mathieu Dubois-Briand S: Maintained @@ -16294,7 +16353,7 @@ M: Ariana Lazar L: linux-iio@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml -F: drivers/iio/dac/mcp47feb02.c +F: drivers/iio/dac/mcp47feb02* MCP4821 DAC DRIVER M: Anshul Dalal @@ -27241,6 +27300,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml F: drivers/iio/adc/ti-ads1119.c +TI ADS112C04 ADC DRIVER +M: Kyle Hsieh +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml +F: drivers/iio/adc/ti-ads112c04.c + TI ADS112C14 ADC DRIVER M: David Lechner L: linux-iio@vger.kernel.org @@ -29098,6 +29164,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml F: drivers/iio/light/veml6030.c +VISHAY VEML6031X00 AMBIENT LIGHT SENSOR DRIVER +M: Javier Carrasco +S: Maintained +F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml +F: drivers/iio/light/veml6031x00.c + VISHAY VEML6046X00 RGBIR COLOR SENSOR DRIVER M: Andreas Klinger S: Maintained diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 4094299e2ed8..141ac984247c 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -7,13 +7,14 @@ menu "Accelerometers" config ADIS16201 - tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" + tristate "ADIS16201 Digital Inclinometer and similar" depends on SPI select IIO_ADIS_LIB select IIO_ADIS_LIB_BUFFER if IIO_BUFFER help - Say Y here to build support for Analog Devices adis16201 dual-axis - digital inclinometer and accelerometer. + Say Y here to build support for Analog Devices Digital Inclinometers: + - ADIS16201 + - ADIS16203 To compile this driver as a module, say M here: the module will be called adis16201. diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index ba0f97944c6d..6d5a7b696112 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer + * ADIS16201 Digital Inclinometer and similar * * Copyright 2010 Analog Devices Inc. */ @@ -63,6 +63,7 @@ #define ADIS16201_DIAG_STAT_REG 0x3C #define ADIS16201_DIAG_STAT_ALARM2 BIT(9) #define ADIS16201_DIAG_STAT_ALARM1 BIT(8) +#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 #define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 #define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2 /* Power supply above 3.625 V */ @@ -87,6 +88,21 @@ enum adis16201_scan { ADIS16201_SCAN_TEMP, }; +struct adis16201_chip_info { + const char *name; + const struct iio_chan_spec *arr_channels; + const struct adis_data *data; + unsigned int incli_scale_val2; + unsigned int read_bits_incli; + unsigned int num_channels; + u16 write_mask_incli; +}; + +struct adis16201_state { + struct adis adis; + const struct adis16201_chip_info *info; +}; + static const u8 adis16201_addresses[] = { [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS_REG, [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS_REG, @@ -99,7 +115,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { - struct adis *st = iio_priv(indio_dev); + struct adis16201_state *st = iio_priv(indio_dev); int ret; int bits; u8 addr; @@ -137,7 +153,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_NANO; case IIO_INCLI: *val = 0; - *val2 = 100000; + *val2 = st->info->incli_scale_val2; return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; @@ -157,13 +173,13 @@ static int adis16201_read_raw(struct iio_dev *indio_dev, bits = 12; break; case IIO_INCLI: - bits = 9; + bits = st->info->read_bits_incli; break; default: return -EINVAL; } addr = adis16201_addresses[chan->scan_index]; - ret = adis_read_reg_16(st, addr, &val16); + ret = adis_read_reg_16(&st->adis, addr, &val16); if (ret) return ret; @@ -180,41 +196,49 @@ static int adis16201_write_raw(struct iio_dev *indio_dev, int val2, long mask) { - struct adis *st = iio_priv(indio_dev); - int m; + struct adis16201_state *st = iio_priv(indio_dev); if (mask != IIO_CHAN_INFO_CALIBBIAS) return -EINVAL; switch (chan->type) { case IIO_ACCEL: - m = GENMASK(11, 0); + val &= GENMASK(11, 0); break; case IIO_INCLI: - m = GENMASK(8, 0); + val &= st->info->write_mask_incli; break; default: return -EINVAL; } - return adis_write_reg_16(st, adis16201_addresses[chan->scan_index], - val & m); + return adis_write_reg_16(&st->adis, adis16201_addresses[chan->scan_index], + val); } static const struct iio_chan_spec adis16201_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0, - 12), - ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12), ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT_REG, ADIS16201_SCAN_ACC_X, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT_REG, ADIS16201_SCAN_ACC_Y, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), - ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12), ADIS_INCLI_CHAN(Y, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12), - IIO_CHAN_SOFT_TIMESTAMP(7) + ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0, + 12), + ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), + ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12), + IIO_CHAN_SOFT_TIMESTAMP(7), +}; + +static const struct iio_chan_spec adis16203_channels[] = { + ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0, 12), + ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), + ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12), + IIO_CHAN_SOFT_TIMESTAMP(7), }; static const struct iio_info adis16201_info = { @@ -224,6 +248,7 @@ static const struct iio_info adis16201_info = { }; static const char * const adis16201_status_error_msgs[] = { + [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", [ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed", [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", @@ -254,10 +279,49 @@ static const struct adis_data adis16201_data = { BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), }; +static const struct adis_data adis16203_data = { + .read_delay = 20, + .msc_ctrl_reg = ADIS16201_MSC_CTRL_REG, + .glob_cmd_reg = ADIS16201_GLOB_CMD_REG, + .diag_stat_reg = ADIS16201_DIAG_STAT_REG, + + .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16201_MSC_CTRL_REG, + .self_test_no_autoclear = true, + .timeouts = &adis16201_timeouts, + + .status_error_msgs = adis16201_status_error_msgs, + .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), +}; + +static const struct adis16201_chip_info adis16201_chip_data = { + .arr_channels = adis16201_channels, + .incli_scale_val2 = 100000, + .write_mask_incli = GENMASK(8, 0), + .read_bits_incli = 9, + .num_channels = ARRAY_SIZE(adis16201_channels), + .name = "adis16201", + .data = &adis16201_data, +}; + +static const struct adis16201_chip_info adis16203_chip_data = { + .arr_channels = adis16203_channels, + .incli_scale_val2 = 25000, + .write_mask_incli = GENMASK(13, 0), + .read_bits_incli = 14, + .num_channels = ARRAY_SIZE(adis16203_channels), + .name = "adis16203", + .data = &adis16203_data, +}; + static int adis16201_probe(struct spi_device *spi) { struct iio_dev *indio_dev; - struct adis *st; + struct adis16201_state *st; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -266,38 +330,57 @@ static int adis16201_probe(struct spi_device *spi) st = iio_priv(indio_dev); - indio_dev->name = spi->dev.driver->name; + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODATA; + + indio_dev->name = st->info->name; indio_dev->info = &adis16201_info; - indio_dev->channels = adis16201_channels; - indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); + indio_dev->channels = st->info->arr_channels; + indio_dev->num_channels = st->info->num_channels; indio_dev->modes = INDIO_DIRECT_MODE; - ret = adis_init(st, indio_dev, spi, &adis16201_data); + ret = adis_init(&st->adis, indio_dev, spi, st->info->data); if (ret) return ret; - ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL); + ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); if (ret) return ret; - ret = __adis_initial_startup(st); + ret = __adis_initial_startup(&st->adis); if (ret) return ret; return devm_iio_device_register(&spi->dev, indio_dev); } +static const struct of_device_id adis16201_of_match[] = { + { .compatible = "adi,adis16201", .data = &adis16201_chip_data }, + { .compatible = "adi,adis16203", .data = &adis16203_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(of, adis16201_of_match); + +static const struct spi_device_id adis16201_ids[] = { + { .name = "adis16201", .driver_data = (kernel_ulong_t)&adis16201_chip_data }, + { .name = "adis16203", .driver_data = (kernel_ulong_t)&adis16203_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(spi, adis16201_ids); + static struct spi_driver adis16201_driver = { .driver = { .name = "adis16201", + .of_match_table = adis16201_of_match, }, .probe = adis16201_probe, + .id_table = adis16201_ids, }; module_spi_driver(adis16201_driver); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); +MODULE_DESCRIPTION("Analog Devices ADIS16201 Inclinometer and similar"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:adis16201"); MODULE_IMPORT_NS("IIO_ADISLIB"); diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index a61295de104f..0f0da44461f7 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -79,6 +79,8 @@ static int adxl313_spi_probe(struct spi_device *spi) return ret; chip_data = spi_get_device_match_data(spi); + if (!chip_data) + return -ENODATA; regmap = devm_regmap_init_spi(spi, &adxl31x_spi_regmap_config[chip_data->type]); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 4ff1c7a0988b..026db73057c5 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ #define ADXL367_SAMPLES_L_MASK GENMASK(7, 0) #define ADXL367_REG_INT1_MAP 0x2A +#define ADXL367_REG_INT2_MAP 0x2B #define ADXL367_INT_INACT_MASK BIT(5) #define ADXL367_INT_ACT_MASK BIT(4) #define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2) @@ -168,6 +170,8 @@ struct adxl367_state { enum adxl367_odr odr; enum adxl367_range range; + u8 int_map_reg; + unsigned int act_threshold; unsigned int act_time_ms; unsigned int inact_threshold; @@ -366,8 +370,7 @@ static int adxl367_set_act_interrupt_en(struct adxl367_state *st, { unsigned int mask = adxl367_act_int_mask_tbl[act]; - return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, - mask, en ? mask : 0); + return regmap_assign_bits(st->regmap, st->int_map_reg, mask, en); } static int adxl367_get_act_interrupt_en(struct adxl367_state *st, @@ -378,7 +381,7 @@ static int adxl367_get_act_interrupt_en(struct adxl367_state *st, unsigned int val; int ret; - ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val); + ret = regmap_read(st->regmap, st->int_map_reg, &val); if (ret) return ret; @@ -401,9 +404,8 @@ static int adxl367_set_act_en(struct adxl367_state *st, static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st, bool en) { - return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, - ADXL367_INT_FIFO_WATERMARK_MASK, - en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0); + return regmap_assign_bits(st->regmap, st->int_map_reg, + ADXL367_INT_FIFO_WATERMARK_MASK, en); } static int adxl367_get_fifo_mode(struct adxl367_state *st, @@ -631,8 +633,7 @@ static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, bool en) { - return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, - en ? ADXL367_ADC_EN_MASK : 0); + return regmap_assign_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, en); } static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st, @@ -787,6 +788,13 @@ static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status, if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status)) return false; + if (fifo_entries > ADXL367_FIFO_SIZE) { + dev_err_ratelimited(st->dev, + "FIFO entry count %u exceeds FIFO size %u\n", + fifo_entries, ADXL367_FIFO_SIZE); + return true; + } + fifo_entries -= fifo_entries % st->fifo_set_size; ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries); @@ -1426,6 +1434,36 @@ static int adxl367_setup(struct adxl367_state *st) return adxl367_set_measure_en(st, true); } +static int adxl367_set_int_map_reg(struct adxl367_state *st, int irq) +{ + int ret; + + /* + * Route the mappings to whichever of INT1/INT2 is named in the + * interrupt-names property. -EINVAL (property absent) and -ENODATA + * (name not listed) are not errors; anything else is. + */ + ret = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT1"); + if (ret > 0) { + st->int_map_reg = ADXL367_REG_INT1_MAP; + return ret; + } + if (ret != -ENODATA && ret != -EINVAL) + return ret; + + ret = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT2"); + if (ret > 0) { + st->int_map_reg = ADXL367_REG_INT2_MAP; + return ret; + } + if (ret != -ENODATA && ret != -EINVAL) + return ret; + + /* No INT1/INT2 named: default to INT1 and the bus interrupt. */ + st->int_map_reg = ADXL367_REG_INT1_MAP; + return irq; +} + int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, void *context, struct regmap *regmap, int irq) { @@ -1455,6 +1493,11 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, indio_dev->info = &adxl367_info; indio_dev->modes = INDIO_DIRECT_MODE; + ret = adxl367_set_int_map_reg(st, irq); + if (ret < 0) + return dev_err_probe(st->dev, ret, "Failed to get interrupt\n"); + irq = ret; + ret = devm_regulator_bulk_get_enable(st->dev, ARRAY_SIZE(regulator_names), regulator_names); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 128e148c4e96..d54544d6df6f 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -25,6 +25,8 @@ static int adxl372_spi_probe(struct spi_device *spi) struct regmap *regmap; chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return -ENODATA; regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); if (IS_ERR(regmap)) diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index 7dca5523091f..8518ee23e114 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -966,6 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p) if (ret) return IRQ_HANDLED; + if (fifo_entries > ADXL380_FIFO_SAMPLES) { + dev_err_ratelimited(st->dev, + "FIFO entry count %u exceeds FIFO size %lu\n", + fifo_entries, ADXL380_FIFO_SAMPLES); + return IRQ_HANDLED; + } + fifo_entries = rounddown(fifo_entries, st->fifo_set_size); ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, &st->fifo_buf, sizeof(*st->fifo_buf) * fifo_entries); diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c index ae8467f4e6c0..8ffb3a8690a6 100644 --- a/drivers/iio/accel/adxl380_spi.c +++ b/drivers/iio/accel/adxl380_spi.c @@ -25,6 +25,8 @@ static int adxl380_spi_probe(struct spi_device *spi) struct regmap *regmap; chip_data = spi_get_device_match_data(spi); + if (!chip_data) + return -ENODATA; regmap = devm_regmap_init_spi(spi, &adxl380_spi_regmap_config); if (IS_ERR(regmap)) diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index d6c1087cc3c0..d7a95289df48 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -28,12 +28,13 @@ static const struct spi_device_id bma220_spi_id[] = { { .name = "bma220" }, { } }; +MODULE_DEVICE_TABLE(spi, bma220_spi_id); static const struct acpi_device_id bma220_acpi_id[] = { { "BMA0220", 0 }, { } }; -MODULE_DEVICE_TABLE(spi, bma220_spi_id); +MODULE_DEVICE_TABLE(acpi, bma220_acpi_id); static const struct of_device_id bma220_of_spi_match[] = { { .compatible = "bosch,bma220" }, diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 7e3cff4cca91..6bc64ca87f4b 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -6,8 +6,6 @@ * * TODO: * - Support for power management - * - Support events and interrupts - * - Create channel for step count * - Create channel for sensor time */ diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 02dd1db7a646..a3d80e4e7f1e 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -301,11 +301,10 @@ struct kx022a_data { __le16 *fifo_buffer; /* 3 x 16bit accel data + timestamp */ - __le16 buffer[8] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[3]; aligned_s64 ts; - } scan; + } scan __aligned(IIO_DMA_MINALIGN); }; static const struct iio_mount_matrix * @@ -611,14 +610,15 @@ static int kx022a_get_axis(struct kx022a_data *data, struct iio_chan_spec const *chan, int *val) { + __le16 *buf = &data->scan.channels[0]; int ret; - ret = regmap_bulk_read(data->regmap, chan->address, &data->buffer[0], - sizeof(__le16)); + ret = regmap_bulk_read(data->regmap, chan->address, + buf, sizeof(*buf)); if (ret) return ret; - *val = (s16)le16_to_cpu(data->buffer[0]); + *val = (s16)le16_to_cpup(buf); return IIO_VAL_INT; } @@ -809,7 +809,7 @@ static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, * the timestamps. If we are ran from IRQ, then the * IRQF_ONESHOT has us covered - but if we are ran by the * user-space read we need to disable the IRQ to be on a safe - * side. We do this usng synchronous disable so that if the + * side. We do this using synchronous disable so that if the * IRQ thread is being ran on other CPU we wait for it to be * finished. */ @@ -864,7 +864,8 @@ static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, for_each_set_bit(bit, idev->active_scan_mask, AXIS_MAX) chs[bit] = sam[bit]; - iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp); + iio_push_to_buffers_with_ts(idev, &data->scan, + sizeof(data->scan), tstamp); tstamp += sample_period; } @@ -1029,12 +1030,13 @@ static irqreturn_t kx022a_trigger_handler(int irq, void *p) struct kx022a_data *data = iio_priv(idev); int ret; - ret = regmap_bulk_read(data->regmap, data->chip_info->xout_l, data->buffer, - KX022A_FIFO_SAMPLES_SIZE_BYTES); + ret = regmap_bulk_read(data->regmap, data->chip_info->xout_l, + data->scan.channels, KX022A_FIFO_SAMPLES_SIZE_BYTES); if (ret < 0) goto err_read; - iio_push_to_buffers_with_timestamp(idev, data->buffer, data->timestamp); + iio_push_to_buffers_with_ts(idev, &data->scan, sizeof(data->scan), + data->timestamp); err_read: iio_trigger_notify_done(idev->trig); diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 0ed54f584223..a2d122c1e234 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -65,7 +65,7 @@ #define KX022A_MASK_IEN BIT(5) #define KX022A_MASK_IPOL BIT(4) #define KX022A_IPOL_LOW 0 -#define KX022A_IPOL_HIGH KX022A_MASK_IPOL1 +#define KX022A_IPOL_HIGH KX022A_MASK_IPOL #define KX022A_MASK_ITYP BIT(3) #define KX022A_ITYP_PULSE KX022A_MASK_ITYP #define KX022A_ITYP_LEVEL 0 diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 166fb786425f..c6b030f64be1 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1547,6 +1547,7 @@ static void kxcjk1013_remove(struct i2c_client *client) iio_device_unregister(indio_dev); + pm_runtime_dont_use_autosuspend(&client->dev); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 7d683686dd9d..fe62a903f0e2 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -18,6 +18,8 @@ * TODO: orientation events */ +#include +#include #include #include #include @@ -110,8 +112,7 @@ struct mma8452_data { u8 data_cfg; const struct mma_chip_info *chip_info; int sleep_val; - struct regulator *vdd_reg; - struct regulator *vddio_reg; + struct regulator_bulk_data regs[2]; /* Ensure correct alignment of time stamp when present */ struct { @@ -222,15 +223,15 @@ static int mma8452_drdy(struct mma8452_data *data) static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on) { #ifdef CONFIG_PM + struct device *dev = &client->dev; int ret; if (on) - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(dev); else - ret = pm_runtime_put_autosuspend(&client->dev); + ret = pm_runtime_put_autosuspend(dev); if (ret < 0) { - dev_err(&client->dev, - "failed to change power state to %d\n", on); + dev_err(dev, "failed to change power state to %d\n", on); return ret; } @@ -495,14 +496,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, int i, ret; switch (mask) { - case IIO_CHAN_INFO_RAW: - if (!iio_device_claim_direct(indio_dev)) + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + ret = mma8452_read(data, buffer); - mutex_unlock(&data->lock); - iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -511,6 +512,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, chan->scan_type.realbits - 1); return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK; *val = data->chip_info->mma_scales[i][0]; @@ -599,36 +601,30 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val) int ret; int is_active; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); is_active = mma8452_is_active(data); - if (is_active < 0) { - ret = is_active; - goto fail; - } + if (is_active < 0) + return is_active; /* config can only be changed when in standby */ if (is_active > 0) { ret = mma8452_standby(data); if (ret < 0) - goto fail; + return ret; } ret = i2c_smbus_write_byte_data(data->client, reg, val); if (ret < 0) - goto fail; + return ret; if (is_active > 0) { ret = mma8452_active(data); if (ret < 0) - goto fail; + return ret; } - ret = 0; -fail: - mutex_unlock(&data->lock); - - return ret; + return 0; } static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode) @@ -777,14 +773,11 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int ret; - - if (!iio_device_claim_direct(indio_dev)) + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; - ret = __mma8452_write_raw(indio_dev, chan, val, val2, mask); - iio_device_release_direct(indio_dev); - return ret; + return __mma8452_write_raw(indio_dev, chan, val, val2, mask); } static int mma8452_get_event_regs(struct mma8452_data *data, @@ -1552,7 +1545,7 @@ static int mma8452_probe(struct i2c_client *client) struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -1562,32 +1555,21 @@ static int mma8452_probe(struct i2c_client *client) data->chip_info = i2c_get_match_data(client); if (!data->chip_info) - return dev_err_probe(&client->dev, -ENODEV, - "unknown device model\n"); + return dev_err_probe(dev, -ENODEV, "unknown device model\n"); - ret = iio_read_mount_matrix(&client->dev, &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; - data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(data->vdd_reg)) - return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg), - "failed to get VDD regulator!\n"); - - data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); - if (IS_ERR(data->vddio_reg)) - return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg), - "failed to get VDDIO regulator!\n"); - - ret = regulator_enable(data->vdd_reg); + data->regs[0].supply = "vdd"; + data->regs[1].supply = "vddio"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regs), data->regs); if (ret) - return dev_err_probe(dev, ret, "failed to enable VDD regulator!\n"); + return dev_err_probe(dev, ret, "failed to get regulators\n"); - ret = regulator_enable(data->vddio_reg); - if (ret) { - dev_err_probe(dev, ret, "failed to enable VDDIO regulator!\n"); - goto disable_regulator_vdd; - } + ret = regulator_bulk_enable(ARRAY_SIZE(data->regs), data->regs); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulators\n"); ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); if (ret < 0) @@ -1608,7 +1590,7 @@ static int mma8452_probe(struct i2c_client *client) goto disable_regulators; } - dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", + dev_info(dev, "registering %s accelerometer; ID 0x%x\n", data->chip_info->name, data->chip_info->chip_id); i2c_set_clientdata(client, indio_dev); @@ -1641,10 +1623,10 @@ static int mma8452_probe(struct i2c_client *client) if (client->irq) { int irq2; - irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2"); + irq2 = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); if (irq2 == client->irq) { - dev_dbg(&client->dev, "using interrupt line INT2\n"); + dev_dbg(dev, "using interrupt line INT2\n"); } else { ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG5, @@ -1652,7 +1634,7 @@ static int mma8452_probe(struct i2c_client *client) if (ret < 0) goto disable_regulators; - dev_dbg(&client->dev, "using interrupt line INT1\n"); + dev_dbg(dev, "using interrupt line INT1\n"); } ret = i2c_smbus_write_byte_data(client, @@ -1689,14 +1671,13 @@ static int mma8452_probe(struct i2c_client *client) goto buffer_cleanup; } - ret = pm_runtime_set_active(&client->dev); + ret = pm_runtime_set_active(dev); if (ret < 0) goto free_irq; - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, - MMA8452_AUTO_SUSPEND_DELAY_MS); - pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, MMA8452_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); ret = iio_device_register(indio_dev); if (ret < 0) @@ -1722,10 +1703,7 @@ static int mma8452_probe(struct i2c_client *client) mma8452_trigger_cleanup(indio_dev); disable_regulators: - regulator_disable(data->vddio_reg); - -disable_regulator_vdd: - regulator_disable(data->vdd_reg); + regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs); return ret; } @@ -1734,11 +1712,12 @@ static void mma8452_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma8452_data *data = iio_priv(indio_dev); + struct device *dev = &client->dev; iio_device_unregister(indio_dev); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); if (client->irq) free_irq(client->irq, indio_dev); @@ -1747,8 +1726,7 @@ static void mma8452_remove(struct i2c_client *client) mma8452_trigger_cleanup(indio_dev); mma8452_standby(iio_priv(indio_dev)); - regulator_disable(data->vddio_reg); - regulator_disable(data->vdd_reg); + regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs); } #ifdef CONFIG_PM @@ -1758,23 +1736,16 @@ static int mma8452_runtime_suspend(struct device *dev) struct mma8452_data *data = iio_priv(indio_dev); int ret; - mutex_lock(&data->lock); - ret = mma8452_standby(data); - mutex_unlock(&data->lock); + scoped_guard(mutex, &data->lock) + ret = mma8452_standby(data); if (ret < 0) { - dev_err(&data->client->dev, "powering off device failed\n"); + dev_err(dev, "powering off device failed\n"); return -EAGAIN; } - ret = regulator_disable(data->vddio_reg); + ret = regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs); if (ret) { - dev_err(dev, "failed to disable VDDIO regulator\n"); - return ret; - } - - ret = regulator_disable(data->vdd_reg); - if (ret) { - dev_err(dev, "failed to disable VDD regulator\n"); + dev_err(dev, "failed to disable regulators\n"); return ret; } @@ -1787,16 +1758,9 @@ static int mma8452_runtime_resume(struct device *dev) struct mma8452_data *data = iio_priv(indio_dev); int ret, sleep_val; - ret = regulator_enable(data->vdd_reg); + ret = regulator_bulk_enable(ARRAY_SIZE(data->regs), data->regs); if (ret) { - dev_err(dev, "failed to enable VDD regulator\n"); - return ret; - } - - ret = regulator_enable(data->vddio_reg); - if (ret) { - dev_err(dev, "failed to enable VDDIO regulator\n"); - regulator_disable(data->vdd_reg); + dev_err(dev, "failed to enable regulators\n"); return ret; } @@ -1814,8 +1778,7 @@ static int mma8452_runtime_resume(struct device *dev) return 0; runtime_resume_failed: - regulator_disable(data->vddio_reg); - regulator_disable(data->vdd_reg); + regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs); return ret; } diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 7e01427fd9c4..182f488edab1 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -500,6 +500,7 @@ static void mma9551_remove(struct i2c_client *client) iio_device_unregister(indio_dev); + pm_runtime_dont_use_autosuspend(&client->dev); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 8e7aaac89d47..5a36d415a837 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1136,6 +1136,7 @@ static void mma9553_remove(struct i2c_client *client) iio_device_unregister(indio_dev); + pm_runtime_dont_use_autosuspend(&client->dev); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index a92a563f6a4b..0d5ed244a663 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1462,6 +1462,8 @@ static int sca3000_probe(struct spi_device *spi) st->us = spi; mutex_init(&st->lock); st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODATA; indio_dev->name = st->info->name; indio_dev->info = &sca3000_info; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 415e519ad4eb..a802d8ee4dd9 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -105,7 +105,7 @@ config AD4080 called ad4080. config AD4130 - tristate "Analog Device AD4130 ADC Driver" + tristate "Analog Devices AD4130 ADC Driver" depends on SPI depends on GPIOLIB select IIO_BUFFER @@ -121,7 +121,7 @@ config AD4130 called ad4130. config AD4134 - tristate "Analog Device AD4134 ADC Driver" + tristate "Analog Devices AD4134 ADC Driver" depends on SPI select REGMAP_SPI select CRC8 @@ -133,7 +133,7 @@ config AD4134 called ad4134_spi. config AD4170_4 - tristate "Analog Device AD4170-4 ADC Driver" + tristate "Analog Devices AD4170-4 ADC Driver" depends on SPI select REGMAP_SPI select IIO_BUFFER @@ -164,7 +164,7 @@ config AD4691 called ad4691. config AD4695 - tristate "Analog Device AD4695 ADC Driver" + tristate "Analog Devices AD4695 ADC Driver" depends on SPI select IIO_BUFFER select IIO_BUFFER_DMAENGINE @@ -179,7 +179,7 @@ config AD4695 called ad4695. config AD4851 - tristate "Analog Device AD4851 DAS Driver" + tristate "Analog Devices AD4851 DAS Driver" depends on SPI depends on PWM select REGMAP_SPI @@ -347,7 +347,7 @@ config AD7380 called ad7380. config AD7405 - tristate "Analog Device AD7405 ADC Driver" + tristate "Analog Devices AD7405 ADC Driver" depends on IIO_BACKEND help Say yes here to build support for Analog Devices AD7405, ADUM7701, @@ -431,6 +431,21 @@ config AD7766 To compile this driver as a module, choose M here: the module will be called ad7766. +config AD7768 + tristate "Analog Devices AD7768/AD7768-4 ADC driver" + depends on SPI + select REGULATOR + select AUXILIARY_BUS + select IIO_BUFFER + select IIO_BACKEND + select REGMAP + help + Say yes here to build support for Analog Devices AD7768 and AD7768-4 + SPI analog to digital converters. + + To compile this driver as a module, choose M here: the module will be + called ad7768. + config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI @@ -576,20 +591,22 @@ config AD9467 called ad9467. config ADE9000 - tristate "Analog Devices ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver" + tristate "Analog Devices Multiphase Energy and Power Quality Monitoring IC Driver" depends on SPI select REGMAP_SPI select IIO_BUFFER select IIO_KFIFO_BUF help - Say yes here to build support for the Analog Devices ADE9000, - a highly accurate, multiphase energy and power quality monitoring - integrated circuit. + Say yes here to build support for the following Analog Devices + highly accurate, multiphase energy and power quality monitoring + integrated circuits: + - ADE9000 + - ADE9078 - The device features high-precision analog-to-digital converters + The devices feature high-precision analog-to-digital converters and digital signal processing to compute RMS values, power factor, - frequency, and harmonic analysis. It supports SPI communication - and provides buffered data output through the IIO framework. + frequency, and harmonic analysis. They support SPI communication + and provide buffered data output through the IIO framework. To compile this driver as a module, choose M here: the module will be called ade9000. @@ -653,6 +670,17 @@ config AT91_SAMA5D2_ADC To compile this driver as a module, choose M here: the module will be called at91-sama5d2_adc. +config AXIADO_SARADC + tristate "Axiado SARADC driver" + depends on ARCH_AXIADO || COMPILE_TEST + select REGMAP_MMIO + help + Say yes here to build support for the SARADC found in Axiado + SoCs. + + To compile this driver as a module, choose M here: the module + will be called axiado_saradc. + config AXP20X_ADC tristate "X-Powers AXP20X and AXP22X ADC driver" depends on MFD_AXP20X @@ -1116,6 +1144,17 @@ config MAX34408 To compile this driver as a module, choose M here: the module will be called max34408. +config MAX40080 + tristate "Analog Devices MAX40080 Current Sense Amplifier" + depends on I2C + help + Say yes here to build support for the Analog Devices MAX40080 + bidirectional current-sense amplifier with a 12-bit ADC and an I2C + interface. + + To compile this driver as a module, choose M here: the module will be + called max40080. + config MAX77541_ADC tristate "Analog Devices MAX77541 ADC driver" depends on MFD_MAX77541 @@ -1551,6 +1590,8 @@ config RZT2H_ADC tristate "Renesas RZ/T2H / RZ/N2H ADC driver" depends on ARCH_RENESAS || COMPILE_TEST select IIO_ADC_HELPER + select IIO_BUFFER + select IIO_KFIFO_BUF help Say yes here to build support for the ADC found in Renesas RZ/T2H / RZ/N2H SoCs. @@ -1801,11 +1842,14 @@ config TI_ADS1018 called ti-ads1018. config TI_ADS1100 - tristate "Texas Instruments ADS1100 and ADS1000 ADC" + tristate "Texas Instruments ADS1100 and similar single channel I2C ADC" depends on I2C help - If you say yes here you get support for Texas Instruments ADS1100 and - ADS1000 ADC chips. + If you say yes here you get support for TI single channel I2C Analog + Devices. + * ADS1000 12-Bit, 128 SPS Analog-to-Digital Converter + * ADS1100 16-Bit, 128 SPS Analog-to-Digital Converter + * ADS1110 16-Bit, 240 SPS Analog-to-Digital Converter This driver can also be built as a module. If so, the module will be called ti-ads1100. @@ -1822,6 +1866,16 @@ config TI_ADS1119 This driver can also be built as a module. If so, the module will be called ti-ads1119. +config TI_ADS112C04 + tristate "Texas Instruments ADS112C04 ADC" + depends on I2C + help + If you say yes here you get support for Texas Instruments + ADS112C04 (16-bit) I2C analog to digital converters. + + This driver can also be built as a module. If so, the module will be + called ti-ads112c04. + config TI_ADS112C14 tristate "Texas Instruments ADS112C14/ADS122C14" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index dcec0abb03b7..9e82c21dbd37 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o obj-$(CONFIG_AD7606) += ad7606.o obj-$(CONFIG_AD7625) += ad7625.o obj-$(CONFIG_AD7766) += ad7766.o +obj-$(CONFIG_AD7768) += ad7768.o obj-$(CONFIG_AD7768_1) += ad7768-1.o obj-$(CONFIG_AD7779) += ad7779.o obj-$(CONFIG_AD7780) += ad7780.o @@ -55,6 +56,7 @@ obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o +obj-$(CONFIG_AXIADO_SARADC) += axiado_saradc.o obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o @@ -95,6 +97,7 @@ obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX14001) += max14001.o obj-$(CONFIG_MAX34408) += max34408.o +obj-$(CONFIG_MAX40080) += max40080.o obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o @@ -155,6 +158,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o +obj-$(CONFIG_TI_ADS112C04) += ti-ads112c04.o obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 04cd6628ebff..4ed82aa42400 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -137,6 +137,7 @@ #define AD4880_CHIP_ID 0x0059 #define AD4883_CHIP_ID 0x005B #define AD4884_CHIP_ID 0x005C +#define AD4885_CHIP_ID 0x005D #define AD4080_MAX_CHANNELS 2 @@ -145,6 +146,10 @@ #define AD4080_MAX_SAMP_FREQ 40000000 #define AD4080_MIN_SAMP_FREQ 1250000 +/* debugfs direct_reg_access channel windowing: 0x0RR = ch0, 0x1RR = ch1 */ +#define AD4080_DEBUGFS_REG_CH_MSK GENMASK(15, 8) +#define AD4080_DEBUGFS_REG_OFFSET_MSK GENMASK(7, 0) + enum ad4080_filter_type { FILTER_NONE, SINC_1, @@ -210,11 +215,16 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { struct ad4080_state *st = iio_priv(indio_dev); + unsigned int ch = FIELD_GET(AD4080_DEBUGFS_REG_CH_MSK, reg); + unsigned int offset = FIELD_GET(AD4080_DEBUGFS_REG_OFFSET_MSK, reg); + + if (ch >= st->info->num_channels) + return -EINVAL; if (readval) - return regmap_read(st->regmap[0], reg, readval); + return regmap_read(st->regmap[ch], offset, readval); - return regmap_write(st->regmap[0], reg, writeval); + return regmap_write(st->regmap[ch], offset, writeval); } static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) @@ -552,6 +562,11 @@ static const struct iio_chan_spec ad4884_channels[] = { AD4880_CHANNEL_DEFINE(16, 16, 1), }; +static const struct iio_chan_spec ad4885_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", .product_id = AD4080_CHIP_ID, @@ -672,6 +687,16 @@ static const struct ad4080_chip_info ad4884_chip_info = { .lvds_cnv_clk_cnt_max = 2, }; +static const struct ad4080_chip_info ad4885_chip_info = { + .name = "ad4885", + .product_id = AD4885_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4885_channels, + .lvds_cnv_clk_cnt_max = 8, +}; + static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) { struct device *dev = regmap_get_device(st->regmap[ch]); @@ -685,7 +710,8 @@ static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) return ret; ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, - AD4080_INTERFACE_CONFIG_A_SDO_ENABLE); + AD4080_INTERFACE_CONFIG_A_SDO_ENABLE | + AD4080_INTERFACE_CONFIG_A_ADDR_ASC); if (ret) return ret; @@ -881,6 +907,7 @@ static const struct spi_device_id ad4080_id[] = { { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info }, { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info }, { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info }, + { .name = "ad4885", .driver_data = (kernel_ulong_t)&ad4885_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -898,6 +925,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4880", &ad4880_chip_info }, { .compatible = "adi,ad4883", &ad4883_chip_info }, { .compatible = "adi,ad4884", &ad4884_chip_info }, + { .compatible = "adi,ad4885", &ad4885_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index 0490218bb0e9..70f80bf1825f 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -496,4 +496,3 @@ module_spi_driver(ad4134_driver); MODULE_AUTHOR("Marcelo Schmitt "); MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("IIO_AD4134"); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index eb47175a0528..5dd82ada79bf 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1801,8 +1801,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) indio_dev->num_channels = num_channels; st->num_channels = num_channels; - chan_arr = devm_kcalloc(dev, sizeof(*indio_dev->channels), - st->num_channels, GFP_KERNEL); + chan_arr = devm_kcalloc(dev, sizeof(*chan_arr), st->num_channels, GFP_KERNEL); if (!chan_arr) return -ENOMEM; diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index d8f5886fcf69..8bd810546311 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1406,6 +1406,9 @@ static int ad7192_probe(struct spi_device *spi) st->int_vref_mv = ret == -ENODEV ? avdd_mv : ret / MILLI; st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODATA; + indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = st->chip_info->info; diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 1ebdfb8580ab..8c479fa50a23 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -456,7 +456,11 @@ static const struct ad7606_bus_info ad7616_bus_info = { static int ad7606_spi_probe(struct spi_device *spi) { - const struct ad7606_bus_info *bus_info = spi_get_device_match_data(spi); + const struct ad7606_bus_info *bus_info; + + bus_info = spi_get_device_match_data(spi); + if (!bus_info) + return -ENODATA; return ad7606_probe(&spi->dev, spi->irq, NULL, bus_info->chip_info, bus_info->bops); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index e9060c1bbe6f..3972821c295a 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -1829,6 +1829,9 @@ static int ad7768_probe(struct spi_device *spi) } st->chip = spi_get_device_match_data(spi); + if (!st->chip) + return -ENODATA; + st->spi = spi; st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config); diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c new file mode 100644 index 000000000000..aa2db87a1b9f --- /dev/null +++ b/drivers/iio/adc/ad7768.c @@ -0,0 +1,1921 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768 ADC driver + * + * Copyright 2018-2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD7768_REG_CH_STANDBY 0x00 + +#define AD7768_REG_CH_MODE(x) (0x01 + (x)) +#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3) +#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0) + +#define AD7768_REG_CH_MODE_SEL 0x03 + +#define AD7768_REG_POWER_MODE 0x04 +#define AD7768_SLEEP_MODE_MSK BIT(7) +#define AD7768_POWER_MODE_POWER_MODE_MSK GENMASK(5, 4) +#define AD7768_POWER_MODE_POWER_MODE_LOW 0x0 +#define AD7768_POWER_MODE_POWER_MODE_MEDIAN 0x2 +#define AD7768_POWER_MODE_POWER_MODE_FAST 0x3 +#define AD7768_POWER_MODE_LVDS_ENABLE BIT(3) +#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0) + +#define AD7768_REG_GENERAL_CONFIG 0x05 +#define AD7768_GEN_CONFIG_VCM_SEL_MSK GENMASK(1, 0) +#define AD7768_GEN_CONFIG_VCM_PD BIT(4) + +#define AD7768_REG_DATA_CONTROL 0x06 +#define AD7768_DATA_CONTROL_SPI_RESET_1 0x03 +#define AD7768_DATA_CONTROL_SPI_RESET_2 0x02 +#define AD7768_DATA_CONTROL_SPI_SYNC BIT(7) + +#define AD7768_REG_INTERFACE_CFG 0x07 +#define AD7768_INTERFACE_CFG_CRC_SELECT_MSK GENMASK(3, 2) +#define AD7768_INTERFACE_CFG_CRC_SELECT_4 0x1 +#define AD7768_INTERFACE_CFG_DCLK_DIV_MSK GENMASK(1, 0) +#define AD7768_INTERFACE_CFG_DCLK_DIV_8 0x0 +#define AD7768_INTERFACE_CFG_DCLK_DIV_4 0x1 +#define AD7768_INTERFACE_CFG_DCLK_DIV_2 0x2 +#define AD7768_INTERFACE_CFG_DCLK_DIV_1 0x3 + +#define AD7768_REG_REV_ID 0x0A +#define AD7768_REV_ID_VAL 0x06 + +#define AD7768_REG_GPIO_CONTROL 0x0E +#define AD7768_GPIO_UGPIO_ENABLE BIT(7) +#define AD7768_GPIO4_OUTPUT_ENABLE BIT(4) + +#define AD7768_REG_GPIO_WRITE 0x0F + +#define AD7768_REG_PRECHARGE_BUF1 0x11 +#define AD7768_REG_PRECHARGE_BUF2 0x12 +#define AD7768_PREBUF_POS_EN(ch) BIT((ch) * 2 + 0) +#define AD7768_PREBUF_NEG_EN(ch) BIT((ch) * 2 + 1) + +#define AD7768_REG_REFP_BUF 0x13 +#define AD7768_REG_REFN_BUF 0x14 + +#define AD7768_REG_OFFSET_BASE 0x1E +#define AD7768_REG_GAIN_BASE 0x36 +#define AD7768_REG_PHASE_BASE 0x4E +#define AD7768_REG_OFFSET(ch) (AD7768_REG_OFFSET_BASE + 3 * (ch)) +#define AD7768_REG_GAIN(ch) (AD7768_REG_GAIN_BASE + 3 * (ch)) +#define AD7768_REG_PHASE(ch) (AD7768_REG_PHASE_BASE + (ch)) + +#define AD7768_REG_DIAGNOSTIC_RX 0x56 + +#define AD7768_REG_CHOP_CTRL 0x59 + +#define AD7768_SPI_READ_CMD BIT(15) +#define AD7768_SPI_REG_MASK GENMASK(14, 8) +#define AD7768_SPI_DATA_MASK GENMASK(7, 0) + +#define AD7768_SAMPLE_SIZE 32 +#define AD7768_MAX_DCLK_DIV 8 +#define AD7768_MIN_MCLK_FREQ_HZ (1150 * HZ_PER_KHZ) +#define AD7768_MIN_XTAL_FREQ_HZ (8 * HZ_PER_MHZ) +#define AD7768_MAX_MCLK_FREQ_HZ (34 * HZ_PER_MHZ) +#define AD7768_MAX_FREQ_PER_MODE 6 +#define AD7768_MAX_CHANNEL 8 +#define AD7768_NUM_CHANNEL_MODES 2 +#define AD7768_CALIB_SIGN_BIT 23 +#define AD7768_CALIB_REG_MSK GENMASK(AD7768_CALIB_SIGN_BIT, 0) +#define AD7768_WIDEBAND_SETTLING_SAMPLES 68 +#define AD7768_SINC5_SETTLING_SAMPLES 7 + +enum ad7768_filter_type { + AD7768_FILTER_TYPE_WIDEBAND, + AD7768_FILTER_TYPE_SINC5, +}; + +enum ad7768_clock_source { + AD7768_CLOCK_SOURCE_MCLK, + AD7768_CLOCK_SOURCE_XTAL, + AD7768_CLOCK_SOURCE_LVDS, +}; + +struct ad7768_power_mode_info { + unsigned int mode; + unsigned int mclk_div; +}; + +static const struct ad7768_power_mode_info ad7768_power_modes[] = { + { .mode = AD7768_POWER_MODE_POWER_MODE_LOW, .mclk_div = 32 }, + { .mode = AD7768_POWER_MODE_POWER_MODE_MEDIAN, .mclk_div = 8 }, + { .mode = AD7768_POWER_MODE_POWER_MODE_FAST, .mclk_div = 4 }, +}; + +#define AD7768_MAX_FREQS \ + (AD7768_MAX_FREQ_PER_MODE + ARRAY_SIZE(ad7768_power_modes)) + +struct ad7768_precharge_config { + bool prebufp_en; + bool prebufn_en; + bool refbufp; + bool refbufn; +}; + +struct ad7768_freq_config { + unsigned int freq_hz; + unsigned int dec_rate; +}; + +struct ad7768_convdelay_params { + unsigned int shift; + unsigned int max_raw; + u64 step_ps; +}; + +struct ad7768_avail_freq { + unsigned int n_freqs; + struct ad7768_freq_config freq_cfg[AD7768_MAX_FREQ_PER_MODE]; +}; + +struct ad7768_chip_info { + const char *name; + const struct regmap_config *regmap_config; + const unsigned int *available_datalines; + unsigned int num_datalines; + unsigned int num_channels; + const u8 *chan_map; + u8 prebuf_split; +}; + +struct ad7768_state { + struct regmap *regmap; + /* Protects device register access and configuration. */ + struct mutex lock; + struct clk *mclk; + unsigned int datalines; + enum ad7768_clock_source clock_source; + const struct ad7768_chip_info *chip_info; + struct ad7768_avail_freq avail_freq[ARRAY_SIZE(ad7768_power_modes)]; + unsigned int n_freqs; + int freqs[AD7768_MAX_FREQS]; + u64 ch_convdelay_ps[AD7768_MAX_CHANNEL]; + unsigned int ch_freq[AD7768_MAX_CHANNEL]; + unsigned int power_mode_idx; + enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL]; + struct iio_backend *back; + struct regulator_dev *vcm_rdev; + unsigned int avdd1_uV; + unsigned int vref_uV[2]; + + /* Used only in regmap_read() callback, hence guarded by regmap lock. */ + __be16 d16 __aligned(IIO_DMA_MINALIGN); +}; + +static const unsigned int ad7768_vcm_voltage_table[] = { + 0, 1650000, 2500000, 2140000, +}; + +static int ad7768_vcm_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + + if (selector >= ARRAY_SIZE(ad7768_vcm_voltage_table)) + return -EINVAL; + + if (!selector) + return DIV_ROUND_CLOSEST(st->avdd1_uV, 2); + + return ad7768_vcm_voltage_table[selector]; +} + +static int ad7768_vcm_enable(struct regulator_dev *rdev) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + int ret; + + ret = pm_runtime_resume_and_get(regmap_get_device(st->regmap)); + if (ret < 0) + return ret; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_PD); + if (ret) + pm_runtime_put_autosuspend(regmap_get_device(st->regmap)); + + return ret; +} + +static int ad7768_vcm_disable(struct regulator_dev *rdev) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + int ret; + + ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_PD); + if (ret) + return ret; + + pm_runtime_put_autosuspend(regmap_get_device(st->regmap)); + + return 0; +} + +static int ad7768_vcm_is_enabled(struct regulator_dev *rdev) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_test_bits(st->regmap, AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_PD); + if (ret < 0) + return ret; + + return !ret; +} + +static int ad7768_vcm_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_SEL_MSK, + FIELD_PREP(AD7768_GEN_CONFIG_VCM_SEL_MSK, + selector)); +} + +static int ad7768_vcm_get_voltage_sel(struct regulator_dev *rdev) +{ + struct ad7768_state *st = rdev_get_drvdata(rdev); + unsigned int val; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD7768_REG_GENERAL_CONFIG, &val); + if (ret) + return ret; + + return FIELD_GET(AD7768_GEN_CONFIG_VCM_SEL_MSK, val); +} + +static const struct regulator_ops ad7768_vcm_ops = { + .enable = ad7768_vcm_enable, + .disable = ad7768_vcm_disable, + .is_enabled = ad7768_vcm_is_enabled, + .list_voltage = ad7768_vcm_list_voltage, + .set_voltage_sel = ad7768_vcm_set_voltage_sel, + .get_voltage_sel = ad7768_vcm_get_voltage_sel, +}; + +static const struct regulator_desc ad7768_vcm_desc = { + .name = "vcm", + .of_match = "vcm-output", + .regulators_node = "regulators", + .n_voltages = ARRAY_SIZE(ad7768_vcm_voltage_table), + .ops = &ad7768_vcm_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int ad7768_register_vcm_regulator(struct device *dev, + struct ad7768_state *st) +{ + struct regulator_config config = { + .dev = dev, + .driver_data = st, + }; + int ret; + + /* + * Start with VCM disabled so each enabled state is paired with the + * runtime PM reference acquired by ad7768_vcm_enable(). + */ + ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_PD); + if (ret) + return ret; + + st->vcm_rdev = devm_regulator_register(dev, &ad7768_vcm_desc, &config); + return PTR_ERR_OR_ZERO(st->vcm_rdev); +} + +static const unsigned int ad7768_dec_rate[AD7768_MAX_FREQ_PER_MODE] = { + 32, 64, 128, 256, 512, 1024, +}; + +static const unsigned int ad7768_available_datalines[] = { + 1, 2, 8, +}; + +static const unsigned int ad7768_4_available_datalines[] = { + 1, 4, +}; + +static const u8 ad7768_chan_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, +}; + +static const u8 ad7768_4_chan_map[] = { + 0, 1, 4, 5, +}; + +static const char * const ad7768_supply_names[] = { + "avdd2", "iovdd", +}; + +static const char * const ad7768_vref_supply_names[][2] = { + { "ref1p", "ref1n" }, + { "ref2p", "ref2n" }, +}; + +static const char * const ad7768_clock_names[] = { + [AD7768_CLOCK_SOURCE_MCLK] = "mclk", + [AD7768_CLOCK_SOURCE_XTAL] = "xtal", + [AD7768_CLOCK_SOURCE_LVDS] = "lvds", +}; + +static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch) +{ + return BIT(st->chip_info->chan_map[ch]); +} + +static u8 ad7768_channel_mode_mask(const struct ad7768_state *st, u8 ch) +{ + /* + * AD7768-4 channels 2 and 3 use bits 4 and 5 for CH_x_MODE, while + * their CH_x_MDE_EN fields are in bits 2 and 3 and must be programmed + * to the same value. On AD7768, chan_map[] is an identity map, so the + * OR produces a single bit. + */ + return BIT(ch) | ad7768_channel_mask(st, ch); +} + +static u8 ad7768_all_standby_mask(const struct ad7768_state *st) +{ + return GENMASK(st->chip_info->num_channels - 1, 0); +} + +static void ad7768_keep_xtal_channel_active(const struct ad7768_state *st, + unsigned long *standby_mask) +{ + /* + * Crystal excitation requires channel 4 on AD7768 or channel 2 on + * AD7768-4 to remain active. + */ + if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL) + __clear_bit(st->chip_info->num_channels / 2, standby_mask); +} + +static unsigned int ad7768_offset_reg(const struct ad7768_state *st, + unsigned int ch) +{ + return AD7768_REG_OFFSET(st->chip_info->chan_map[ch]); +} + +static unsigned int ad7768_gain_reg(const struct ad7768_state *st, + unsigned int ch) +{ + return AD7768_REG_GAIN(st->chip_info->chan_map[ch]); +} + +static unsigned int ad7768_phase_reg(const struct ad7768_state *st, + unsigned int ch) +{ + return AD7768_REG_PHASE(st->chip_info->chan_map[ch]); +} + +static u8 ad7768_precharge_buf1_mask(const struct ad7768_state *st, u16 val) +{ + return val & GENMASK(st->chip_info->prebuf_split - 1, 0); +} + +static u8 ad7768_precharge_buf2_mask(const struct ad7768_state *st, u16 val) +{ + unsigned int split = st->chip_info->prebuf_split; + + return field_get(GENMASK(2 * split - 1, split), val); +} + +static int ad7768_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct ad7768_state *st = spi_get_drvdata(context); + struct spi_device *spi = context; + struct spi_transfer t[] = { + { + .tx_buf = &st->d16, + .len = sizeof(st->d16), + .cs_change = 1, + }, { + /* + * Register responses are delayed by one CS frame. While + * receiving the response to this read, the device also + * decodes another command on SDI. Repeat the read + * command to avoid sending an unspecified dummy + * command. + */ + .tx_buf = &st->d16, + .rx_buf = &st->d16, + .len = sizeof(st->d16), + }, + }; + u8 *data_val = val_buf; + unsigned int reg; + int ret; + + reg = *(const u8 *)reg_buf; + + st->d16 = be16_replace_bits(cpu_to_be16(AD7768_SPI_READ_CMD), reg, + AD7768_SPI_REG_MASK); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret) + return ret; + + *data_val = be16_get_bits(st->d16, AD7768_SPI_DATA_MASK); + + return 0; +} + +static int ad7768_regmap_write(void *spi, const void *data, size_t count) +{ + return spi_write(spi, data, count); +} + +static const struct regmap_bus ad7768_regmap_bus = { + .read = ad7768_regmap_read, + .write = ad7768_regmap_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static bool ad7768_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7768_REG_CH_STANDBY ... AD7768_REG_REV_ID: + case AD7768_REG_GPIO_CONTROL ... AD7768_REG_REFN_BUF: + case AD7768_REG_OFFSET(0) ... AD7768_REG_OFFSET(7) + 2: + case AD7768_REG_GAIN(0) ... AD7768_REG_GAIN(7) + 2: + case AD7768_REG_PHASE(0) ... AD7768_REG_PHASE(7): + case AD7768_REG_DIAGNOSTIC_RX ... AD7768_REG_CHOP_CTRL: + return true; + default: + return false; + } +} + +static bool ad7768_4_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7768_REG_CH_STANDBY ... AD7768_REG_REV_ID: + case AD7768_REG_GPIO_CONTROL ... AD7768_REG_REFN_BUF: + case AD7768_REG_OFFSET(0) ... AD7768_REG_OFFSET(1) + 2: + case AD7768_REG_OFFSET(4) ... AD7768_REG_OFFSET(5) + 2: + case AD7768_REG_GAIN(0) ... AD7768_REG_GAIN(1) + 2: + case AD7768_REG_GAIN(4) ... AD7768_REG_GAIN(5) + 2: + case AD7768_REG_PHASE(0) ... AD7768_REG_PHASE(1): + case AD7768_REG_PHASE(4) ... AD7768_REG_PHASE(5): + case AD7768_REG_DIAGNOSTIC_RX ... AD7768_REG_CHOP_CTRL: + return true; + default: + return false; + } +} + +static const struct regmap_config ad7768_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AD7768_REG_CHOP_CTRL, + /* + * Regmap bulk transfers use one starting address, while each register + * access requires a separate 16-bit SPI frame and reads use an off-frame + * response. Split bulk transfers into individual register accesses. + */ + .use_single_read = true, + .use_single_write = true, + .readable_reg = ad7768_readable_reg, +}; + +static const struct regmap_config ad7768_4_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AD7768_REG_CHOP_CTRL, + /* + * Regmap bulk transfers use one starting address, while each register + * access requires a separate 16-bit SPI frame and reads use an off-frame + * response. Split bulk transfers into individual register accesses. Do + * not use a register cache because some readable registers report live + * hardware state. + */ + .use_single_read = true, + .use_single_write = true, + .readable_reg = ad7768_4_readable_reg, +}; + +static unsigned int ad7768_get_calib_reg_base(struct ad7768_state *st, + const struct iio_chan_spec *chan, + bool is_gain) +{ + if (is_gain) + return ad7768_gain_reg(st, chan->channel); + + return ad7768_offset_reg(st, chan->channel); +} + +static int ad7768_read_calib_value(struct ad7768_state *st, + unsigned int base_reg, bool is_signed, + int *val) +{ + unsigned int regval; + u8 data[3]; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + ret = regmap_bulk_read(st->regmap, base_reg, data, sizeof(data)); + if (ret) + return ret; + + regval = get_unaligned_be24(data); + *val = is_signed ? sign_extend32(regval, AD7768_CALIB_SIGN_BIT) : regval; + + return 0; +} + +static int ad7768_write_calib_value(struct ad7768_state *st, + unsigned int base_reg, bool is_signed, + int val) +{ + u8 data[3]; + int ret; + + if (is_signed && + val != sign_extend32(val, AD7768_CALIB_SIGN_BIT)) + return -EINVAL; + + if (!is_signed && (val < 0 || val > AD7768_CALIB_REG_MSK)) + return -EINVAL; + + put_unaligned_be24(val, data); + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return regmap_bulk_write(st->regmap, base_reg, data, sizeof(data)); +} + +static int ad7768_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad7768_sync(struct ad7768_state *st) +{ + int ret; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_SYNC); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_SYNC); +} + +static int ad7768_set_power_mode(struct ad7768_state *st, + unsigned int mode_idx) +{ + const struct ad7768_power_mode_info *mode_info; + int ret; + + mode_info = &ad7768_power_modes[mode_idx]; + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_POWER_MODE_POWER_MODE_MSK | + AD7768_POWER_MODE_MCLK_DIV_MSK, + FIELD_PREP(AD7768_POWER_MODE_POWER_MODE_MSK, + mode_info->mode) | + FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK, + mode_info->mode)); + if (ret) + return ret; + + ret = ad7768_sync(st); + if (ret) + return ret; + + st->power_mode_idx = mode_idx; + + return 0; +} + +static const struct ad7768_freq_config * +ad7768_find_freq_config(const struct ad7768_state *st, + unsigned int mode_idx, unsigned int freq) +{ + const struct ad7768_avail_freq *avail_freq = &st->avail_freq[mode_idx]; + + for (unsigned int i = 0; i < avail_freq->n_freqs; i++) { + if (freq == avail_freq->freq_cfg[i].freq_hz) + return &avail_freq->freq_cfg[i]; + } + + return NULL; +} + +static int ad7768_set_clk_divs(struct ad7768_state *st, unsigned int freq) +{ + const struct ad7768_freq_config *freq_cfg; + unsigned int mclk, dclk, dclk_div; + unsigned int chan_per_doutx; + unsigned int dclk_div_reg; + + freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx, freq); + if (!freq_cfg) + return -EINVAL; + + mclk = clk_get_rate(st->mclk); + chan_per_doutx = st->chip_info->num_channels / st->datalines; + if (!chan_per_doutx) + return -EINVAL; + + dclk = freq_cfg->freq_hz * AD7768_SAMPLE_SIZE * chan_per_doutx; + if (dclk > mclk) + return -EINVAL; + + dclk_div = DIV_ROUND_CLOSEST(mclk, dclk); + + /* Set the divider to the next-lowest supported power of two. */ + dclk_div = rounddown_pow_of_two(min(dclk_div, AD7768_MAX_DCLK_DIV)); + + switch (dclk_div) { + case 1: + dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV_1; + break; + case 2: + dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV_2; + break; + case 4: + dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV_4; + break; + case 8: + dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV_8; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG, + AD7768_INTERFACE_CFG_DCLK_DIV_MSK, + FIELD_PREP(AD7768_INTERFACE_CFG_DCLK_DIV_MSK, + dclk_div_reg)); +} + +static int ad7768_set_mode_decimation(struct ad7768_state *st, + unsigned int freq, unsigned int mode) +{ + const struct ad7768_freq_config *freq_cfg; + + freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx, freq); + if (!freq_cfg) + return -EINVAL; + + return regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(mode), + AD7768_CH_MODE_DEC_RATE_MSK, + FIELD_PREP(AD7768_CH_MODE_DEC_RATE_MSK, + freq_cfg->dec_rate)); +} + +static int ad7768_set_lowest_noise_mode(struct ad7768_state *st, + const unsigned long *scan_mask) +{ + unsigned int mode_idx = ARRAY_SIZE(ad7768_power_modes); + + /* + * The output data rate ranges overlap between the power modes. At a + * common ODR, the faster mode has lower noise, so prefer the fastest + * mode that supports every enabled channel. + */ + while (mode_idx--) { + unsigned int channel; + + for_each_set_bit(channel, scan_mask, st->chip_info->num_channels) { + if (!ad7768_find_freq_config(st, mode_idx, + st->ch_freq[channel])) + break; + } + + /* All enabled channels support their requested frequency. */ + if (channel == st->chip_info->num_channels) + return ad7768_set_power_mode(st, mode_idx); + } + + return -EINVAL; +} + +static bool ad7768_freq_supported_in_any_mode(const struct ad7768_state *st, + unsigned int freq) +{ + for (unsigned int mode_idx = 0; + mode_idx < ARRAY_SIZE(ad7768_power_modes); mode_idx++) { + if (ad7768_find_freq_config(st, mode_idx, freq)) + return true; + } + + return false; +} + +static int ad7768_set_sampling_freq(struct iio_dev *indio_dev, + unsigned int freq, unsigned int channel) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + if (!freq || !ad7768_freq_supported_in_any_mode(st, freq)) + return -EINVAL; + + guard(mutex)(&st->lock); + st->ch_freq[channel] = freq; + + return 0; +} + +static int ad7768_set_filter_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + guard(mutex)(&st->lock); + st->ch_filter[chan->channel] = mode; + + return 0; +} + +static int ad7768_get_filter_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + return st->ch_filter[chan->channel]; +} + +static const char *const ad7768_filter_types[] = { + [AD7768_FILTER_TYPE_WIDEBAND] = "wideband", + [AD7768_FILTER_TYPE_SINC5] = "sinc5", +}; + +static const struct iio_enum ad7768_filter_types_enum = { + .items = ad7768_filter_types, + .num_items = ARRAY_SIZE(ad7768_filter_types), + .set = ad7768_set_filter_mode, + .get = ad7768_get_filter_mode, +}; + +static struct iio_chan_spec_ext_info ad7768_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7768_filter_types_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ad7768_filter_types_enum), + { } +}; + +static int ad7768_find_matching_mode(const bool *mode_used, + const unsigned int *mode_freq, + const enum ad7768_filter_type *mode_filter, + unsigned int freq, + enum ad7768_filter_type filter) +{ + for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) { + if (!mode_used[mode] || + (mode_freq[mode] == freq && mode_filter[mode] == filter)) + return mode; + } + + return -EINVAL; +} + +static int ad7768_get_convdelay_params(struct ad7768_state *st, unsigned int ch, + struct ad7768_convdelay_params *params) +{ + const struct ad7768_freq_config *freq_cfg; + unsigned int dec_rate; + unsigned int mclk_div; + unsigned long mclk; + u64 mult; + + freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx, + st->ch_freq[ch]); + if (!freq_cfg) + return -EINVAL; + + dec_rate = ad7768_dec_rate[freq_cfg->dec_rate]; + switch (dec_rate) { + case 32: + params->shift = 3; + params->max_raw = 31; + mult = 1; + break; + case 64: + params->shift = 2; + params->max_raw = 63; + mult = 1; + break; + case 128: + params->shift = 1; + params->max_raw = 127; + mult = 1; + break; + case 256: + params->shift = 0; + params->max_raw = 255; + mult = 1; + break; + case 512: + params->shift = 0; + params->max_raw = 255; + mult = 2; + break; + case 1024: + params->shift = 0; + params->max_raw = 255; + mult = 4; + break; + default: + return -EINVAL; + } + + mclk = clk_get_rate(st->mclk); + if (!mclk || mclk > U32_MAX) + return -EINVAL; + + mclk_div = ad7768_power_modes[st->power_mode_idx].mclk_div; + params->step_ps = + DIV_ROUND_CLOSEST_ULL(mult * PSEC_PER_SEC * mclk_div, (u32)mclk); + if (params->step_ps > U32_MAX) + return -EINVAL; + + return 0; +} + +static int ad7768_set_channel_convdelay(struct ad7768_state *st, + unsigned int ch) +{ + u64 delay_ps = st->ch_convdelay_ps[ch]; + struct ad7768_convdelay_params params; + u64 max_delay_ps; + u64 raw; + int ret; + + ret = ad7768_get_convdelay_params(st, ch, ¶ms); + if (ret) + return ret; + + /* + * Validate the requested delay before rounding so an out-of-range value + * cannot round down into the valid range. Then verify that the rounded + * value fits in the hardware register. + */ + max_delay_ps = (u64)params.max_raw * params.step_ps; + if (delay_ps > max_delay_ps) + return -EINVAL; + + raw = DIV_ROUND_CLOSEST_ULL(delay_ps, (u32)params.step_ps); + if (raw > params.max_raw) + return -EINVAL; + + return regmap_write(st->regmap, ad7768_phase_reg(st, ch), + raw << params.shift); +} + +static void ad7768_filter_wait(const unsigned int *mode_freq, + const enum ad7768_filter_type *mode_filter, + const bool *mode_used) +{ + unsigned int t_settle_us = 0; + + for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) { + unsigned int t_mode_us; + u8 settling_samples; + + if (!mode_used[mode] || !mode_freq[mode]) + continue; + + if (mode_filter[mode] == AD7768_FILTER_TYPE_SINC5) + settling_samples = AD7768_SINC5_SETTLING_SAMPLES; + else + settling_samples = AD7768_WIDEBAND_SETTLING_SAMPLES; + + t_mode_us = DIV_ROUND_UP(settling_samples * USEC_PER_SEC, + mode_freq[mode]); + t_settle_us = max(t_settle_us, t_mode_us); + } + + if (t_settle_us) + fsleep(t_settle_us); +} + +static int ad7768_apply_channel_modes(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + enum ad7768_filter_type mode_filter[AD7768_NUM_CHANNEL_MODES] = { }; + unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES] = { }; + bool mode_used[AD7768_NUM_CHANNEL_MODES] = { }; + struct ad7768_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + unsigned long channel_mask; + unsigned long standby_mask; + unsigned int max_freq; + unsigned int c; + int ret; + + guard(mutex)(&st->lock); + + ret = ad7768_set_lowest_noise_mode(st, scan_mask); + if (ret == -EINVAL) + return dev_err_probe(dev, ret, + "No power mode supports all ODRs\n"); + if (ret) + return ret; + + channel_mask = ad7768_all_standby_mask(st); + standby_mask = channel_mask & ~*scan_mask; + ad7768_keep_xtal_channel_active(st, &standby_mask); + + for_each_set_bit(c, scan_mask, st->chip_info->num_channels) { + unsigned int mask; + int mode; + + mode = ad7768_find_matching_mode(mode_used, mode_freq, + mode_filter, st->ch_freq[c], + st->ch_filter[c]); + if (mode < 0) + return dev_err_probe(dev, -EINVAL, + "Over %d channel modes required\n", + AD7768_NUM_CHANNEL_MODES); + + mode_freq[mode] = st->ch_freq[c]; + mode_filter[mode] = st->ch_filter[c]; + mode_used[mode] = true; + + mask = ad7768_channel_mode_mask(st, c); + ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE_SEL, + mask, mode); + if (ret) + return ret; + } + + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY, + channel_mask, standby_mask); + if (ret) + return ret; + + max_freq = 0; + for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) { + if (!mode_used[mode]) + continue; + + ret = ad7768_set_mode_decimation(st, mode_freq[mode], mode); + if (ret) + return ret; + + ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE(mode), + AD7768_CH_MODE_FILTER_TYPE_MSK, + mode_filter[mode]); + if (ret) + return ret; + + max_freq = max(max_freq, mode_freq[mode]); + } + + for_each_set_bit(c, scan_mask, st->chip_info->num_channels) { + ret = ad7768_set_channel_convdelay(st, c); + if (ret == -EINVAL) + return dev_err_probe(regmap_get_device(st->regmap), ret, + "Invalid conversion delay for channel %u\n", + c); + if (ret) + return ret; + } + + ret = ad7768_set_clk_divs(st, max_freq); + if (ret) + return ret; + + ret = ad7768_sync(st); + if (ret) + return ret; + + /* Apply a filter settling time (datasheet Tables 28 and 29). */ + ad7768_filter_wait(mode_freq, mode_filter, mode_used); + + return 0; +} + +static int ad7768_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7768_apply_channel_modes(indio_dev, scan_mask); + if (ret) + return ret; + + for (unsigned int ch = 0; ch < st->chip_info->num_channels; ch++) { + if (test_bit(ch, scan_mask)) + ret = iio_backend_chan_enable(st->back, ch); + else + ret = iio_backend_chan_disable(st->back, ch); + if (ret) + return ret; + } + + return 0; +} + +static const struct ad7768_chip_info ad7768_chip_info = { + .name = "ad7768", + .num_channels = 8, + .regmap_config = &ad7768_regmap_config, + .available_datalines = ad7768_available_datalines, + .num_datalines = ARRAY_SIZE(ad7768_available_datalines), + .chan_map = ad7768_chan_map, + .prebuf_split = 8, +}; + +static const struct ad7768_chip_info ad7768_4_chip_info = { + .name = "ad7768-4", + .num_channels = 4, + .regmap_config = &ad7768_4_regmap_config, + .available_datalines = ad7768_4_available_datalines, + .num_datalines = ARRAY_SIZE(ad7768_4_available_datalines), + .chan_map = ad7768_4_chan_map, + .prebuf_split = 4, +}; + +static int ad7768_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + return pm_runtime_resume_and_get(regmap_get_device(st->regmap)); +} + +static int ad7768_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + pm_runtime_put_autosuspend(regmap_get_device(st->regmap)); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7768_buffer_ops = { + .preenable = ad7768_buffer_preenable, + .postdisable = ad7768_buffer_postdisable, +}; + +static int ad7768_read_scale(struct ad7768_state *st, + const struct iio_chan_spec *chan, + int *val, int *val2) +{ + unsigned int vref_idx; + + vref_idx = chan->channel >= st->chip_info->num_channels / 2; + *val = 2 * st->vref_uV[vref_idx] / (MICRO / MILLI); + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad7768_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad7768_read_scale(st, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&st->lock); + *val = st->ch_freq[chan->channel]; + + return IIO_VAL_INT; + } + + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: { + bool is_gain = info == IIO_CHAN_INFO_CALIBSCALE; + unsigned int base_reg; + + base_reg = ad7768_get_calib_reg_base(st, chan, is_gain); + ret = ad7768_read_calib_value(st, base_reg, !is_gain, val); + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_CONVDELAY: { + guard(mutex)(&st->lock); + iio_val_s64_decompose(st->ch_convdelay_ps[chan->channel], + val, val2); + + return IIO_VAL_DECIMAL64_PICO; + } + default: + return -EINVAL; + } +} + +static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; + case IIO_CHAN_INFO_CONVDELAY: + return IIO_VAL_DECIMAL64_PICO; + 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); + unsigned int base_reg; + s64 delay_ps; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7768_set_sampling_freq(indio_dev, val, chan->channel); + case IIO_CHAN_INFO_CALIBBIAS: + base_reg = ad7768_get_calib_reg_base(st, chan, false); + return ad7768_write_calib_value(st, base_reg, true, val); + case IIO_CHAN_INFO_CALIBSCALE: + base_reg = ad7768_get_calib_reg_base(st, chan, true); + return ad7768_write_calib_value(st, base_reg, false, val); + case IIO_CHAN_INFO_CONVDELAY: { + delay_ps = iio_val_s64_compose(val, val2); + if (delay_ps < 0) + return -EINVAL; + + guard(mutex)(&st->lock); + st->ch_convdelay_ps[chan->channel] = delay_ps; + return 0; + } + default: + return -EINVAL; + } +} + +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); + + if (info != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + *vals = st->freqs; + *type = IIO_VAL_INT; + *length = st->n_freqs; + + return IIO_AVAIL_LIST; +} + +static const struct iio_info ad7768_info = { + .debugfs_reg_access = ad7768_reg_access, + .read_raw = ad7768_read_raw, + .write_raw_get_fmt = ad7768_write_raw_get_fmt, + .write_raw = ad7768_write_raw, + .read_avail = ad7768_read_avail, + .update_scan_mode = ad7768_update_scan_mode, +}; + +static int ad7768_gpio_adev_init(struct ad7768_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct spi_device *spi = to_spi_device(dev); + struct auxiliary_device *adev; + int id; + + if (!device_property_read_bool(dev, "gpio-controller")) + return 0; + + /* + * Use the SPI bus number and chip select to derive a stable per-device + * ID. + */ + id = (spi->controller->bus_num << 8) | spi_get_chipselect(spi, 0); + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio", + NULL, id); + if (!adev) + return dev_err_probe(dev, -ENODEV, + "Failed to create GPIO auxiliary device\n"); + + return 0; +} + +static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev, + struct ad7768_precharge_config *precharge_cfg) +{ + struct ad7768_state *st = iio_priv(indio_dev); + u8 prebuf1_val, prebuf2_val; + u16 prebuf_mask = 0; + u8 refbufp_val = 0; + u8 refbufn_val = 0; + int ret; + + for (unsigned int ch = 0; ch < indio_dev->num_channels; ch++) { + u8 channel = indio_dev->channels[ch].channel; + + if (precharge_cfg[channel].prebufp_en) + prebuf_mask |= AD7768_PREBUF_POS_EN(channel); + + if (precharge_cfg[channel].prebufn_en) + prebuf_mask |= AD7768_PREBUF_NEG_EN(channel); + + if (precharge_cfg[channel].refbufp) + refbufp_val |= ad7768_channel_mask(st, channel); + + if (precharge_cfg[channel].refbufn) + refbufn_val |= ad7768_channel_mask(st, channel); + } + + prebuf1_val = ad7768_precharge_buf1_mask(st, ~prebuf_mask); + prebuf2_val = ad7768_precharge_buf2_mask(st, ~prebuf_mask); + + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF1, prebuf1_val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF2, prebuf2_val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_REFP_BUF, refbufp_val); + if (ret) + return ret; + + return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val); +} + +static void ad7768_set_available_sampling_freqs(struct ad7768_state *st) +{ + unsigned int n_power_modes = ARRAY_SIZE(ad7768_power_modes); + unsigned int mclk = clk_get_rate(st->mclk); + + for (unsigned int mode_idx = 0; mode_idx < n_power_modes; mode_idx++) { + unsigned int dec = ARRAY_SIZE(ad7768_dec_rate); + struct ad7768_avail_freq *avail_freq; + unsigned int div; + + avail_freq = &st->avail_freq[mode_idx]; + div = ad7768_power_modes[mode_idx].mclk_div; + while (dec--) { + struct ad7768_freq_config *freq_cfg; + + freq_cfg = &avail_freq->freq_cfg[avail_freq->n_freqs++]; + freq_cfg->dec_rate = dec; + freq_cfg->freq_hz = mclk / (ad7768_dec_rate[dec] * div); + } + } + + /* One DOUT line cannot carry the AD7768 fast-mode x32 output rate. */ + if (st->datalines == 1 && + st->chip_info->num_channels == AD7768_MAX_CHANNEL) + st->avail_freq[n_power_modes - 1].n_freqs--; + + for (unsigned int mode_idx = 0; mode_idx < n_power_modes; mode_idx++) { + const struct ad7768_avail_freq *avail_freq; + + avail_freq = &st->avail_freq[mode_idx]; + for (unsigned int i = 0; i < avail_freq->n_freqs; i++) { + unsigned int freq = avail_freq->freq_cfg[i].freq_hz; + + if (st->n_freqs && st->freqs[st->n_freqs - 1] >= freq) + continue; + + st->freqs[st->n_freqs++] = freq; + } + } +} + +static int ad7768_init_sampling_freqs(struct ad7768_state *st) +{ + const struct ad7768_avail_freq *avail_freq; + unsigned int default_freq; + int ret; + + ret = ad7768_set_power_mode(st, ARRAY_SIZE(ad7768_power_modes) - 1); + if (ret) + return ret; + + avail_freq = &st->avail_freq[st->power_mode_idx]; + default_freq = avail_freq->freq_cfg[avail_freq->n_freqs - 1].freq_hz; + for (unsigned int channel = 0; + channel < st->chip_info->num_channels; channel++) { + st->ch_freq[channel] = default_freq; + st->ch_filter[channel] = AD7768_FILTER_TYPE_WIDEBAND; + } + + return 0; +} + +static int ad7768_parse_config(struct iio_dev *indio_dev, + struct device *dev) +{ + struct ad7768_precharge_config precharge_cfg[AD7768_MAX_CHANNEL] = { }; + struct ad7768_state *st = iio_priv(indio_dev); + const unsigned int *available_datalines; + const char *propname; + bool datalines_valid = false; + struct iio_chan_spec *chan; + unsigned int num_channels; + unsigned long standby_mask; + unsigned int len; + int chan_idx = 0; + int ret; + + propname = "adi,data-lines-number"; + num_channels = device_get_named_child_node_count(dev, "channel"); + if (num_channels == 0) + return dev_err_probe(dev, -ENOENT, "No channel specified\n"); + if (num_channels > st->chip_info->num_channels) + return dev_err_probe(dev, -ENOSPC, "Invalid number of channels\n"); + + chan = devm_kcalloc(dev, num_channels, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + indio_dev->channels = chan; + indio_dev->num_channels = num_channels; + + standby_mask = ad7768_all_standby_mask(st); + ad7768_keep_xtal_channel_active(st, &standby_mask); + + ret = regmap_write(st->regmap, AD7768_REG_CH_STANDBY, standby_mask); + if (ret) + return ret; + + device_for_each_named_child_node_scoped(dev, child, "channel") { + u32 channel; + + ret = fwnode_property_read_u32(child, "reg", &channel); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse reg of %pfwP\n", + child); + + if (channel >= st->chip_info->num_channels) + return dev_err_probe(dev, -ECHRNG, + "Invalid channel %u in firmware\n", + channel); + + ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_STANDBY, + BIT(channel)); + if (ret) + return ret; + + precharge_cfg[channel].prebufp_en = + fwnode_property_read_bool(child, "adi,prechargebuf-pos-enable"); + precharge_cfg[channel].prebufn_en = + fwnode_property_read_bool(child, "adi,prechargebuf-neg-enable"); + precharge_cfg[channel].refbufp = + fwnode_property_read_bool(child, "adi,refbuf-pos-enable"); + precharge_cfg[channel].refbufn = + fwnode_property_read_bool(child, "adi,refbuf-neg-enable"); + + chan[chan_idx++] = (struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CONVDELAY) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .indexed = 1, + .channel = channel, + .scan_index = channel, + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + }, + .ext_info = ad7768_ext_info, + }; + } + + ret = ad7768_configure_precharge_buffers(indio_dev, precharge_cfg); + if (ret) + return ret; + + available_datalines = st->chip_info->available_datalines; + len = st->chip_info->num_datalines; + if (device_property_present(dev, propname)) { + ret = device_property_read_u32(dev, propname, &st->datalines); + if (ret) + return dev_err_probe(dev, ret, "Invalid %s property\n", + propname); + } else { + st->datalines = available_datalines[len - 1]; + } + + for (unsigned int i = 0; i < len; i++) { + if (available_datalines[i] == st->datalines) { + datalines_valid = true; + break; + } + } + + if (!datalines_valid) + return dev_err_probe(dev, -EINVAL, + "Invalid %s %u for %s\n", propname, + st->datalines, st->chip_info->name); + + ad7768_set_available_sampling_freqs(st); + + ret = ad7768_init_sampling_freqs(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to set power mode\n"); + + return 0; +} + +static int ad7768_reset(struct ad7768_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct reset_control *reset_ctrl; + unsigned long reset_low_us; + unsigned long mclk; + int ret; + + reset_ctrl = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(reset_ctrl)) + return PTR_ERR(reset_ctrl); + + if (reset_ctrl) { + mclk = clk_get_rate(st->mclk); + if (!mclk) + return -EINVAL; + + /* + * Minimum RESET low pulse width: 2 x tMCLK + * (datasheet Table 1). + */ + reset_low_us = DIV_ROUND_UP_ULL(2ULL * USEC_PER_SEC, mclk); + + ret = reset_control_assert(reset_ctrl); + if (ret) + return ret; + + fsleep(max(1UL, reset_low_us)); + + ret = reset_control_deassert(reset_ctrl); + if (ret) + return ret; + } else { + ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_RESET_1); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_RESET_2); + if (ret) + return ret; + } + + /* ADC start-up time after reset: 1.66 ms max (datasheet Table 1) */ + fsleep(1660); + + return 0; +} + +static void ad7768_disable_clk(void *clk) +{ + clk_disable_unprepare(clk); +} + +static int ad7768_configure_xtal_clock(struct ad7768_state *st) +{ + int ret; + + ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_WRITE, BIT(4)); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + AD7768_GPIO_UGPIO_ENABLE | + AD7768_GPIO4_OUTPUT_ENABLE); +} + +static int ad7768_enable_lvds_clock(struct ad7768_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + int ret; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_GPIO_WRITE, BIT(4)); + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + AD7768_GPIO_UGPIO_ENABLE | + AD7768_GPIO4_OUTPUT_ENABLE); + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_POWER_MODE_LVDS_ENABLE); + if (ret) + return ret; + + ret = clk_prepare_enable(st->mclk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ad7768_disable_clk, st->mclk); +} + +static int ad7768_get_enable_vref(struct device *dev, unsigned int index) +{ + const char * const *supply = ad7768_vref_supply_names[index]; + int refp_uV; + int refn_uV; + + refp_uV = devm_regulator_get_enable_read_voltage(dev, supply[0]); + if (refp_uV < 0) + return dev_err_probe(dev, refp_uV, + "Failed to get %s supply voltage\n", supply[0]); + + refn_uV = devm_regulator_get_enable_read_voltage(dev, supply[1]); + if (refn_uV == -ENODEV) + refn_uV = 0; + else if (refn_uV < 0) + return dev_err_probe(dev, refn_uV, + "Failed to get %s supply voltage\n", supply[1]); + + if (refp_uV <= refn_uV) + return dev_err_probe(dev, -EINVAL, + "Invalid reference %u voltage\n", index + 1); + + return refp_uV - refn_uV; +} + +static int ad7768_enter_sleep(struct ad7768_state *st) +{ + return regmap_set_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_SLEEP_MODE_MSK); +} + +static void ad7768_power_off(void *data) +{ + struct ad7768_state *st = data; + struct device *dev = regmap_get_device(st->regmap); + int ret; + + ret = ad7768_enter_sleep(st); + if (ret) + dev_err(dev, "Failed to put device to sleep\n"); +} + +static int ad7768_validate_mclk_rate(struct device *dev, + const struct ad7768_state *st) +{ + unsigned long min_rate = AD7768_MIN_MCLK_FREQ_HZ; + unsigned long rate = clk_get_rate(st->mclk); + + if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL) + min_rate = AD7768_MIN_XTAL_FREQ_HZ; + + if (rate < min_rate || rate > AD7768_MAX_MCLK_FREQ_HZ) + return dev_err_probe(dev, -EINVAL, + "MCLK rate %lu Hz outside %lu-%lu Hz\n", + rate, min_rate, AD7768_MAX_MCLK_FREQ_HZ); + + return 0; +} + +static int ad7768_probe(struct spi_device *spi) +{ + unsigned int spi_readback, rev_id; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad7768_state *st; + const char *clock_name; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, st); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return dev_err_probe(dev, -ENODATA, "Failed to get match data\n"); + + ret = devm_regulator_get_enable_optional(dev, "avss"); + if (ret == -ENODEV) { + /* AVSS may be connected directly to ground instead of a regulator. */ + } else if (ret) { + return dev_err_probe(dev, ret, "Failed to enable AVSS supply\n"); + } + + ret = devm_regulator_get_enable_read_voltage(dev, "avdd1"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to enable AVDD1 supply\n"); + + st->avdd1_uV = ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7768_supply_names), + ad7768_supply_names); + if (ret) + return ret; + + for (unsigned int i = 0; i < ARRAY_SIZE(ad7768_vref_supply_names); i++) { + ret = ad7768_get_enable_vref(dev, i); + if (ret < 0) + return ret; + + st->vref_uV[i] = ret; + } + + ret = device_property_match_property_string(dev, "clock-names", + ad7768_clock_names, + ARRAY_SIZE(ad7768_clock_names)); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid clock source\n"); + + st->clock_source = ret; + clock_name = ad7768_clock_names[st->clock_source]; + + /* + * The device must start on its internal clock. Keep the LVDS clock + * disabled until GPIO4 is driven low and the LVDS input is enabled in + * the power mode register. + */ + switch (st->clock_source) { + case AD7768_CLOCK_SOURCE_MCLK: + case AD7768_CLOCK_SOURCE_XTAL: + st->mclk = devm_clk_get_enabled(dev, clock_name); + break; + case AD7768_CLOCK_SOURCE_LVDS: + st->mclk = devm_clk_get(dev, clock_name); + break; + } + + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get master clock\n"); + + ret = ad7768_validate_mclk_rate(dev, st); + if (ret) + return ret; + + st->regmap = devm_regmap_init(dev, &ad7768_regmap_bus, spi, + st->chip_info->regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ad7768_reset(st); + if (ret) + return ret; + + /* Discard the reset response with a dummy SPI register read. */ + ret = regmap_read(st->regmap, AD7768_REG_REV_ID, &spi_readback); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD7768_REG_REV_ID, &rev_id); + if (ret) + return ret; + + if (rev_id != AD7768_REV_ID_VAL) + dev_info(dev, "Unexpected revision ID 0x%02x\n", rev_id); + + switch (st->clock_source) { + case AD7768_CLOCK_SOURCE_MCLK: + break; + case AD7768_CLOCK_SOURCE_XTAL: + ret = ad7768_configure_xtal_clock(st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to configure crystal clock\n"); + break; + case AD7768_CLOCK_SOURCE_LVDS: + ret = ad7768_enable_lvds_clock(st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable LVDS clock\n"); + break; + } + + ret = devm_add_action_or_reset(dev, ad7768_power_off, st); + if (ret) + return ret; + + ret = ad7768_parse_config(indio_dev, dev); + if (ret) + return ret; + + /* + * Hardware supports CRC every 4 or 16 samples; the backend supports only + * 4-sample CRC. + */ + ret = regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG, + AD7768_INTERFACE_CFG_CRC_SELECT_MSK, + FIELD_PREP(AD7768_INTERFACE_CFG_CRC_SELECT_MSK, + AD7768_INTERFACE_CFG_CRC_SELECT_4)); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad7768_info; + indio_dev->setup_ops = &ad7768_buffer_ops; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = iio_backend_num_lanes_set(st->back, st->datalines); + if (ret) + return ret; + + ret = iio_backend_crc_enable(st->back); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + ret = ad7768_register_vcm_regulator(dev, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register VCM regulator\n"); + + ret = ad7768_gpio_adev_init(st); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int ad7768_runtime_suspend(struct device *dev) +{ + struct ad7768_state *st = dev_get_drvdata(dev); + + return ad7768_enter_sleep(st); +} + +static int ad7768_runtime_resume(struct device *dev) +{ + struct ad7768_state *st = dev_get_drvdata(dev); + int ret; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_SLEEP_MODE_MSK); + if (ret) + return ret; + + /* + * The datasheet does not specify a wake-up time. Allow 20 ms for the + * ADC and digital clocks to restart. + */ + fsleep(20 * USEC_PER_MSEC); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ad7768_pm_ops, ad7768_runtime_suspend, + ad7768_runtime_resume, NULL); + +static const struct of_device_id ad7768_of_match[] = { + { .compatible = "adi,ad7768", .data = &ad7768_chip_info }, + { .compatible = "adi,ad7768-4", .data = &ad7768_4_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7768_of_match); + +static const struct spi_device_id ad7768_spi_id[] = { + { .name = "ad7768", .driver_data = (kernel_ulong_t)&ad7768_chip_info }, + { .name = "ad7768-4", .driver_data = (kernel_ulong_t)&ad7768_4_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7768_spi_id); + +static struct spi_driver ad7768_driver = { + .probe = ad7768_probe, + .driver = { + .name = "ad7768", + .of_match_table = ad7768_of_match, + .pm = pm_ptr(&ad7768_pm_ops), + }, + .id_table = ad7768_spi_id, +}; +module_spi_driver(ad7768_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_AUTHOR("Janani Sunil "); +MODULE_DESCRIPTION("Analog Devices AD7768 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c6c3ea953fea..fd1362a98052 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -242,14 +242,6 @@ #define ADE9000_LAST_PAGE_BIT BIT(15) #define ADE9000_MIDDLE_PAGE_BIT BIT(7) -/* - * Full scale Codes referred from Datasheet. Respective digital codes are - * produced when ADC inputs are at full scale. - */ -#define ADE9000_RMS_FULL_SCALE_CODES 52866837 -#define ADE9000_WATT_FULL_SCALE_CODES 20694066 -#define ADE9000_PCF_FULL_SCALE_CODES 74770000 - /* Phase and channel definitions */ #define ADE9000_PHASE_A_NR 0 #define ADE9000_PHASE_B_NR 1 @@ -290,7 +282,29 @@ enum ade9000_wfb_cfg { #define ADE9000_ADDR_ADJUST(addr, chan) \ (((chan) == 0 ? 0 : (chan) == 1 ? 2 : 4) << 4 | (addr)) +/** + * struct ade9000_chip_info - part-specific configuration + * @name: IIO device name reported to userspace + * @channels: channel specification for this part + * @num_channels: number of entries in @channels + * @rms_full_scale_codes: digital code produced at full-scale RMS input + * @watt_full_scale_codes: digital code produced at full-scale power input + * @pcf_full_scale_codes: digital code produced at full-scale xI_PCF/xV_PCF input + * + * The full-scale codes are taken from the respective datasheets and are used to + * derive the IIO scale of the raw measurement channels. + */ +struct ade9000_chip_info { + const char *name; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int rms_full_scale_codes; + unsigned int watt_full_scale_codes; + unsigned int pcf_full_scale_codes; +}; + struct ade9000_state { + const struct ade9000_chip_info *info; struct completion reset_completion; struct mutex lock; /* Protects SPI transactions */ u8 wf_src; @@ -488,6 +502,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { .scan_index = -1 \ } +/* With swell/dip (sag) events - ADE9000 only */ #define ADE9000_ALTVOLTAGE_RMS_CHANNEL(num) { \ .type = IIO_ALTVOLTAGE, \ .channel = num, \ @@ -503,6 +518,20 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { .scan_index = -1 \ } +/* Without swell/dip events - parts lacking DIP_LVL/SWELL_LVL hardware */ +#define ADE9000_ALTVOLTAGE_RMS_CHANNEL_NO_EVENTS(num) { \ + .type = IIO_ALTVOLTAGE, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMS, num), \ + .channel2 = IIO_MOD_RMS, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .scan_index = -1 \ +} + #define ADE9000_POWER_ACTIVE_CHANNEL(num) { \ .type = IIO_POWER, \ .channel = num, \ @@ -584,43 +613,82 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { .scan_index = -1 \ } -static const struct iio_chan_spec ade9000_channels[] = { - /* Phase A channels */ - ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR), - ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO), - ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO), - ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO), - ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR), - /* Phase B channels */ - ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR), - ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BWATTHR_LO), - ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BVAHR_LO), - ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BFVARHR_LO), - ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR), - /* Phase C channels */ - ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR), - ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CWATTHR_LO), - ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CVAHR_LO), - ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CFVARHR_LO), - ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR), +/* + * Declare a full channel array. @altvoltage_rms picks the RMS voltage channel + * variant so parts without dip/swell hardware (e.g. ADE9078) omit those events. + */ +#define ADE9000_DECLARE_CHANNELS(_name, altvoltage_rms) \ + static const struct iio_chan_spec _name[] = { \ + /* Phase A channels */ \ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR), \ + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR), \ + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR), \ + altvoltage_rms(ADE9000_PHASE_A_NR), \ + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR), \ + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR), \ + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR), \ + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, \ + ADE9000_REG_AWATTHR_LO), \ + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, \ + ADE9000_REG_AVAHR_LO), \ + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, \ + ADE9000_REG_AFVARHR_LO), \ + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR), \ + /* Phase B channels */ \ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR), \ + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR), \ + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR), \ + altvoltage_rms(ADE9000_PHASE_B_NR), \ + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR), \ + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR), \ + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR), \ + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, \ + ADE9000_REG_BWATTHR_LO), \ + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, \ + ADE9000_REG_BVAHR_LO), \ + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, \ + ADE9000_REG_BFVARHR_LO), \ + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR), \ + /* Phase C channels */ \ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR), \ + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR), \ + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR), \ + altvoltage_rms(ADE9000_PHASE_C_NR), \ + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR), \ + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR), \ + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR), \ + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, \ + ADE9000_REG_CWATTHR_LO), \ + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, \ + ADE9000_REG_CVAHR_LO), \ + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, \ + ADE9000_REG_CFVARHR_LO), \ + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR), \ + } + +ADE9000_DECLARE_CHANNELS(ade9000_channels, ADE9000_ALTVOLTAGE_RMS_CHANNEL); +ADE9000_DECLARE_CHANNELS(ade9078_channels, ADE9000_ALTVOLTAGE_RMS_CHANNEL_NO_EVENTS); + +/* + * Full-scale codes referred from the respective datasheets. These are the + * digital codes produced when the ADC inputs are at full scale. + */ +static const struct ade9000_chip_info ade9000_chip_info = { + .name = "ade9000", + .channels = ade9000_channels, + .num_channels = ARRAY_SIZE(ade9000_channels), + .rms_full_scale_codes = 52866837, + .watt_full_scale_codes = 20694066, + .pcf_full_scale_codes = 74770000, +}; + +static const struct ade9000_chip_info ade9078_chip_info = { + .name = "ade9078", + .channels = ade9078_channels, + .num_channels = ARRAY_SIZE(ade9078_channels), + .rms_full_scale_codes = 52866837, + .watt_full_scale_codes = 20823646, + .pcf_full_scale_codes = 74680000, }; static const struct reg_sequence ade9000_initialization_sequence[] = { @@ -1064,7 +1132,7 @@ static int ade9000_read_raw(struct iio_dev *indio_dev, case ADE9000_REG_CI_PCF: case ADE9000_REG_CV_PCF: *val = 1; - *val2 = ADE9000_PCF_FULL_SCALE_CODES; + *val2 = st->info->pcf_full_scale_codes; return IIO_VAL_FRACTIONAL; case ADE9000_REG_AIRMS: case ADE9000_REG_AVRMS: @@ -1073,14 +1141,14 @@ static int ade9000_read_raw(struct iio_dev *indio_dev, case ADE9000_REG_CIRMS: case ADE9000_REG_CVRMS: *val = 1; - *val2 = ADE9000_RMS_FULL_SCALE_CODES; + *val2 = st->info->rms_full_scale_codes; return IIO_VAL_FRACTIONAL; default: return -EINVAL; } case IIO_POWER: *val = 1; - *val2 = ADE9000_WATT_FULL_SCALE_CODES; + *val2 = st->info->watt_full_scale_codes; return IIO_VAL_FRACTIONAL; default: break; @@ -1692,6 +1760,10 @@ static int ade9000_probe(struct spi_device *spi) st = iio_priv(indio_dev); + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + regmap = devm_regmap_init(dev, NULL, st, &ade9000_regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Unable to allocate ADE9000 regmap"); @@ -1726,7 +1798,7 @@ static int ade9000_probe(struct spi_device *spi) if (ret) return ret; - indio_dev->name = "ade9000"; + indio_dev->name = st->info->name; indio_dev->info = &ade9000_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->setup_ops = &ade9000_buffer_ops; @@ -1736,8 +1808,8 @@ static int ade9000_probe(struct spi_device *spi) return dev_err_probe(&spi->dev, ret, "Failed to get and enable vdd regulator\n"); - indio_dev->channels = ade9000_channels; - indio_dev->num_channels = ARRAY_SIZE(ade9000_channels); + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &ade9000_buffer_ops); @@ -1768,13 +1840,15 @@ static int ade9000_probe(struct spi_device *spi) }; static const struct spi_device_id ade9000_id[] = { - { .name = "ade9000" }, + { .name = "ade9000", .driver_data = (kernel_ulong_t)&ade9000_chip_info }, + { .name = "ade9078", .driver_data = (kernel_ulong_t)&ade9078_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ade9000_id); static const struct of_device_id ade9000_of_match[] = { - { .compatible = "adi,ade9000" }, + { .compatible = "adi,ade9000", .data = &ade9000_chip_info }, + { .compatible = "adi,ade9078", .data = &ade9078_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ade9000_of_match); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 589f03618ecf..8b7c45d51dbe 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -47,6 +47,9 @@ #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) #define ADI_AXI_ADC_REG_CNTRL_3 0x004c +/* Bit 8 is common to all AXI ADC cores; bits 7-0 are per-HDL-project. */ +#define ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK BIT(8) + #define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2) #define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0) #define AXI_AD485X_PACKET_FORMAT_20BIT 0x0 @@ -596,6 +599,22 @@ static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg, return regmap_write(st->regmap, reg, writeval); } +static int axi_adc_crc_enable(struct iio_backend *back) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK); +} + +static int axi_adc_crc_disable(struct iio_backend *back) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK); +} + static const struct regmap_config axi_adc_regmap_config = { .val_bits = 32, .reg_bits = 32, @@ -644,6 +663,8 @@ static const struct iio_backend_ops adi_axi_adc_ops = { .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), + .crc_enable = axi_adc_crc_enable, + .crc_disable = axi_adc_crc_disable, }; static const struct iio_backend_info adi_axi_adc_generic = { @@ -714,6 +735,10 @@ static int adi_axi_adc_probe(struct platform_device *pdev) if (!st) return -ENOMEM; + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index e8a5285bb6d4..78081cbfde5e 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2016,6 +2016,8 @@ static void at91_adc_dma_init(struct at91_adc_state *st) unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * sample_size * 2, PAGE_SIZE); + struct device *dma_dev; + if (st->dma_st.dma_chan) return; @@ -2026,7 +2028,8 @@ static void at91_adc_dma_init(struct at91_adc_state *st) goto dma_exit; } - st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev, + dma_dev = dmaengine_get_dma_device(st->dma_st.dma_chan); + st->dma_st.rx_buf = dma_alloc_coherent(dma_dev, pages * PAGE_SIZE, &st->dma_st.rx_dma_buf, GFP_KERNEL); @@ -2054,7 +2057,7 @@ static void at91_adc_dma_init(struct at91_adc_state *st) return; dma_free_area: - dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + dma_free_coherent(dma_dev, pages * PAGE_SIZE, st->dma_st.rx_buf, st->dma_st.rx_dma_buf); dma_chan_disable: dma_release_channel(st->dma_st.dma_chan); @@ -2078,7 +2081,7 @@ static void at91_adc_dma_disable(struct at91_adc_state *st) /* wait for all transactions to be terminated first*/ dmaengine_terminate_sync(st->dma_st.dma_chan); - dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + dma_free_coherent(dmaengine_get_dma_device(st->dma_st.dma_chan), pages * PAGE_SIZE, st->dma_st.rx_buf, st->dma_st.rx_dma_buf); dma_release_channel(st->dma_st.dma_chan); st->dma_st.dma_chan = NULL; @@ -2487,6 +2490,7 @@ static void at91_adc_remove(struct platform_device *pdev) pm_runtime_disable(st->dev); pm_runtime_set_suspended(st->dev); + pm_runtime_dont_use_autosuspend(st->dev); clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); diff --git a/drivers/iio/adc/axiado_saradc.c b/drivers/iio/adc/axiado_saradc.c new file mode 100644 index 000000000000..699ee31616fc --- /dev/null +++ b/drivers/iio/adc/axiado_saradc.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021-2026 Axiado Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Register offsets */ +#define AX_SARADC_GLOBAL_CTRL_REG 0x0004 +#define AX_SARADC_MANUAL_CTRL_REG 0x0008 +#define AX_SARADC_DOUT_REG 0x001C + +/* GLOBAL_CTRL register fields */ +#define AX_SARADC_GLOBAL_CTRL_CH_EN_MASK GENMASK(31, 16) +#define AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK GENMASK(6, 5) +#define AX_SARADC_GLOBAL_CTRL_MODE_MASK GENMASK(4, 3) +#define AX_SARADC_GLOBAL_CTRL_PD BIT(2) +#define AX_SARADC_GLOBAL_CTRL_ENABLE BIT(0) + +/* GLOBAL_CTRL SAMPLE_MASK field value: 0 selects 16 samples. */ +#define AX_SARADC_GLOBAL_CTRL_SAMPLE_16 0 + +/* GLOBAL_CTRL MODE_MASK field value: 1 selects manual mode. */ +#define AX_SARADC_GLOBAL_CTRL_MODE_MANUAL 1 + +/* MANUAL_CTRL register fields */ +#define AX_SARADC_MANUAL_CTRL_ENABLE BIT(0) +#define AX_SARADC_MANUAL_CTRL_CH_SEL_MASK GENMASK(4, 1) + +#define AX_RESOLUTION_BITS 10 +#define AX_SARADC_CONV_CYCLES 13 +#define AX_SARADC_CONV_DELAY_MARGIN_US 10 + +struct axiado_saradc { + struct regmap *regmap; + struct mutex lock; /* Serializes ADC conversions. */ + unsigned long clk_rate; + int vref_uV; +}; + +/* + * Registers contain transient control, status, and conversion data, + * so accesses must always go directly to hardware. + */ +static const struct regmap_config axiado_saradc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = AX_SARADC_DOUT_REG, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int axiado_saradc_conversion(struct axiado_saradc *info, + struct iio_chan_spec const *chan, int *val) +{ + unsigned long usecs; + unsigned int regval; + int ret; + + guard(mutex)(&info->lock); + + /* Select the channel to be used and trigger conversion. */ + ret = regmap_write(info->regmap, AX_SARADC_MANUAL_CTRL_REG, + AX_SARADC_MANUAL_CTRL_ENABLE | + FIELD_PREP(AX_SARADC_MANUAL_CTRL_CH_SEL_MASK, chan->channel)); + if (ret) + return ret; + + /* Hardware requires 13 conversion cycles at clk_rate. */ + usecs = DIV_ROUND_UP(AX_SARADC_CONV_CYCLES * USEC_PER_SEC, info->clk_rate); + fsleep(usecs + AX_SARADC_CONV_DELAY_MARGIN_US); + + ret = regmap_read(info->regmap, AX_SARADC_DOUT_REG, ®val); + + /* Best effort to stop manual conversion. */ + regmap_write(info->regmap, AX_SARADC_MANUAL_CTRL_REG, 0); + + if (ret) + return ret; + + *val = regval & GENMASK(AX_RESOLUTION_BITS - 1, 0); + + return 0; +} + +static int axiado_saradc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct axiado_saradc *info = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = axiado_saradc_conversion(info, chan, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = info->vref_uV / (MICRO / MILLI); + *val2 = AX_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info axiado_saradc_iio_info = { + .read_raw = axiado_saradc_read_raw, +}; + +struct axiado_saradc_soc_data { + const char *name; + unsigned int num_channels; +}; + +static const struct axiado_saradc_soc_data ax3000_saradc_data = { + .name = "ax3000_saradc", + .num_channels = 16, +}; + +static const struct axiado_saradc_soc_data ax3005_saradc_data = { + .name = "ax3005_saradc", + .num_channels = 8, +}; + +#define AX_SARADC_CH(_index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "adc" #_index, \ + } + +static const struct iio_chan_spec axiado_saradc_iio_channels[] = { + AX_SARADC_CH(0), + AX_SARADC_CH(1), + AX_SARADC_CH(2), + AX_SARADC_CH(3), + AX_SARADC_CH(4), + AX_SARADC_CH(5), + AX_SARADC_CH(6), + AX_SARADC_CH(7), + AX_SARADC_CH(8), + AX_SARADC_CH(9), + AX_SARADC_CH(10), + AX_SARADC_CH(11), + AX_SARADC_CH(12), + AX_SARADC_CH(13), + AX_SARADC_CH(14), + AX_SARADC_CH(15), +}; + +static void axiado_saradc_disable(void *map) +{ + regmap_write(map, AX_SARADC_GLOBAL_CTRL_REG, AX_SARADC_GLOBAL_CTRL_PD); +} + +static int axiado_saradc_probe(struct platform_device *pdev) +{ + const struct axiado_saradc_soc_data *soc_data; + struct device *dev = &pdev->dev; + struct axiado_saradc *info; + struct iio_dev *indio_dev; + void __iomem *regs; + struct regmap *map; + struct clk *clk; + u32 regval; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + map = devm_regmap_init_mmio(dev, regs, &axiado_saradc_regmap_config); + if (IS_ERR(map)) + return PTR_ERR(map); + info->regmap = map; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + info->clk_rate = clk_get_rate(clk); + if (!info->clk_rate) + return dev_err_probe(dev, -EINVAL, "invalid clock rate\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get vref voltage\n"); + + info->vref_uV = ret; + + soc_data = device_get_match_data(dev); + if (!soc_data) + return dev_err_probe(dev, -ENODATA, "failed to get match data\n"); + + ret = devm_mutex_init(dev, &info->lock); + if (ret) + return ret; + + regval = FIELD_PREP(AX_SARADC_GLOBAL_CTRL_CH_EN_MASK, + GENMASK(soc_data->num_channels - 1, 0)) | + FIELD_PREP(AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK, + AX_SARADC_GLOBAL_CTRL_SAMPLE_16) | + FIELD_PREP(AX_SARADC_GLOBAL_CTRL_MODE_MASK, + AX_SARADC_GLOBAL_CTRL_MODE_MANUAL) | + AX_SARADC_GLOBAL_CTRL_ENABLE; + + ret = regmap_write(map, AX_SARADC_GLOBAL_CTRL_REG, regval); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, axiado_saradc_disable, map); + if (ret) + return ret; + + indio_dev->name = soc_data->name; + indio_dev->info = &axiado_saradc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = axiado_saradc_iio_channels; + indio_dev->num_channels = soc_data->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id axiado_saradc_match[] = { + { + .compatible = "axiado,ax3000-saradc", + .data = &ax3000_saradc_data, + }, + { + .compatible = "axiado,ax3005-saradc", + .data = &ax3005_saradc_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, axiado_saradc_match); + +static struct platform_driver axiado_saradc_driver = { + .driver = { + .name = "axiado-saradc", + .of_match_table = axiado_saradc_match, + }, + .probe = axiado_saradc_probe, +}; +module_platform_driver(axiado_saradc_driver); + +MODULE_AUTHOR("Axiado Corporation"); +MODULE_DESCRIPTION("Axiado SARADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c index cf4738b16e62..ca81e0edd24d 100644 --- a/drivers/iio/adc/bcm_iproc_adc.c +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -3,14 +3,18 @@ * Copyright 2016 Broadcom */ -#include -#include +#include +#include #include -#include -#include +#include #include #include +#include +#include +#include #include +#include +#include #include @@ -106,7 +110,6 @@ do { \ struct iproc_adc_priv { struct regmap *regmap; - struct clk *adc_clk; struct mutex mutex; int irqno; int chan_val; @@ -448,6 +451,11 @@ static void iproc_adc_disable(struct iio_dev *indio_dev) } } +static void iproc_adc_disable_action(void *indio_dev) +{ + iproc_adc_disable(indio_dev); +} + static int iproc_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -506,35 +514,31 @@ static int iproc_adc_probe(struct platform_device *pdev) { struct iproc_adc_priv *adc_priv; struct iio_dev *indio_dev = NULL; + struct device *dev = &pdev->dev; + struct clk *adc_clk; int ret; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(*adc_priv)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc_priv)); if (!indio_dev) return -ENOMEM; adc_priv = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); - mutex_init(&adc_priv->mutex); + ret = devm_mutex_init(dev, &adc_priv->mutex); + if (ret) + return ret; init_completion(&adc_priv->completion); adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "adc-syscon"); - if (IS_ERR(adc_priv->regmap)) { - dev_err(&pdev->dev, "failed to get handle for tsc syscon\n"); - ret = PTR_ERR(adc_priv->regmap); - return ret; - } + if (IS_ERR(adc_priv->regmap)) + return dev_err_probe(dev, PTR_ERR(adc_priv->regmap), + "failed to get handle for tsc syscon\n"); - adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); - if (IS_ERR(adc_priv->adc_clk)) { - dev_err(&pdev->dev, - "failed getting clock tsc_clk\n"); - ret = PTR_ERR(adc_priv->adc_clk); - return ret; - } + adc_clk = devm_clk_get_enabled(dev, "tsc_clk"); + if (IS_ERR(adc_clk)) + return dev_err_probe(dev, PTR_ERR(adc_clk), "failed to enable clock\n"); adc_priv->irqno = platform_get_irq(pdev, 0); if (adc_priv->irqno < 0) @@ -542,32 +546,23 @@ static int iproc_adc_probe(struct platform_device *pdev) ret = regmap_clear_bits(adc_priv->regmap, IPROC_REGCTL2, IPROC_ADC_AUXIN_SCAN_ENA); - if (ret) { - dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to write IPROC_REGCTL2\n"); - ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno, + ret = devm_request_threaded_irq(dev, adc_priv->irqno, iproc_adc_interrupt_handler, iproc_adc_interrupt_thread, IRQF_SHARED, "iproc-adc", indio_dev); - if (ret) { - dev_err(&pdev->dev, "request_irq error %d\n", ret); + if (ret) return ret; - } - - ret = clk_prepare_enable(adc_priv->adc_clk); - if (ret) { - dev_err(&pdev->dev, - "clk_prepare_enable failed %d\n", ret); - return ret; - } ret = iproc_adc_enable(indio_dev); - if (ret) { - dev_err(&pdev->dev, "failed to enable adc %d\n", ret); - goto err_adc_enable; - } + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, iproc_adc_disable_action, indio_dev); + if (ret) + return ret; indio_dev->name = "iproc-static-adc"; indio_dev->info = &iproc_adc_iio_info; @@ -575,30 +570,7 @@ static int iproc_adc_probe(struct platform_device *pdev) indio_dev->channels = iproc_adc_iio_channels; indio_dev->num_channels = ARRAY_SIZE(iproc_adc_iio_channels); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "iio_device_register failed:err %d\n", ret); - goto err_clk; - } - - return 0; - -err_clk: - iproc_adc_disable(indio_dev); -err_adc_enable: - clk_disable_unprepare(adc_priv->adc_clk); - - return ret; -} - -static void iproc_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iproc_adc_disable(indio_dev); - clk_disable_unprepare(adc_priv->adc_clk); + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id iproc_adc_of_match[] = { @@ -609,7 +581,6 @@ MODULE_DEVICE_TABLE(of, iproc_adc_of_match); static struct platform_driver iproc_adc_driver = { .probe = iproc_adc_probe, - .remove = iproc_adc_remove, .driver = { .name = "iproc-static-adc", .of_match_table = iproc_adc_of_match, diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index 2dc5c7044269..a6e34da83b81 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include "ltc2497.h" @@ -19,24 +21,54 @@ #define LTC2497_DIFF 0 #define LTC2497_SIGN BIT(3) -static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata) +/* + * Output-rate modes, indexed by ltc2497core_driverdata.speed_2x + * (0 = 1x, the power-on default; 1 = 2x, LTC2499 only). The advertised + * sampling_frequency and the conversion-time budget are two views of the same + * mode, so they are kept in lock-step here. + */ +static const int ltc2497core_samp_freq_avail[] = { + 6, 800000, /* 1x: ~6.8 Hz (1 / t_CONV_1 typ 146.9ms) */ + 13, 600000, /* 2x: ~13.6 Hz (1 / t_CONV_2 typ 73.6ms) */ +}; + +static const unsigned int ltc2497core_conv_time_ms_tbl[] = { + LTC2497_CONV_TIME_1X_MS, /* 1x */ + LTC2499_CONV_TIME_2X_MS, /* 2x */ +}; + +static unsigned int ltc2497core_conv_time_ms(struct ltc2497core_driverdata *ddata, + u8 address) +{ + /* + * SPD is ignored by the part during a temperature measurement: it + * always converts at 1x, so budget the 1x time regardless of the + * selected voltage-channel mode. + */ + if (address == LTC2497_TEMP_ADDR) + return ltc2497core_conv_time_ms_tbl[0]; + + return ltc2497core_conv_time_ms_tbl[ddata->speed_2x]; +} + +static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata, + unsigned int conv_time_ms) { s64 time_elapsed; time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev); - if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + if (time_elapsed < conv_time_ms) { /* delay if conversion time not passed * since last read or write */ - if (msleep_interruptible( - LTC2497_CONVERSION_TIME_MS - time_elapsed)) + if (msleep_interruptible(conv_time_ms - time_elapsed)) return -ERESTARTSYS; return 0; } - if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + if (time_elapsed - conv_time_ms <= 0) { /* We're in automatic mode - * so the last reading is still not outdated */ @@ -48,9 +80,18 @@ static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata) static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val) { + unsigned int conv_time_ms = ltc2497core_conv_time_ms(ddata, address); int ret; - ret = ltc2497core_wait_conv(ddata); + /* + * Wait for the conversion currently in flight, whose duration was fixed + * by the mode active when it was started (ddata->conv_time_prev). This + * can be longer than the freshly selected mode's time - e.g. a 1x + * conversion is still running when the first 2x read arrives after a + * sampling_frequency change - and reprogramming the device before it + * finishes would be NACKed (-EIO). + */ + ret = ltc2497core_wait_conv(ddata, ddata->conv_time_prev); if (ret < 0) return ret; @@ -60,7 +101,17 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in return ret; ddata->addr_prev = address; - if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + /* + * The reprogram above starts a conversion in the new mode. + * Record its start time and duration before sleeping, so that if + * msleep_interruptible() is interrupted the conversion state is + * already consistent: the next retry then waits only the time + * remaining from the real start instead of from a stale + * time_prev, which would let it reprogram/read too early. + */ + ddata->time_prev = ktime_get(); + ddata->conv_time_prev = conv_time_ms; + if (msleep_interruptible(conv_time_ms)) return -ERESTARTSYS; } @@ -69,6 +120,8 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in return ret; ddata->time_prev = ktime_get(); + /* The read above auto-starts the next conversion in the current mode. */ + ddata->conv_time_prev = conv_time_ms; return ret; } @@ -95,74 +148,209 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - *val = ret / 1000; - *val2 = ddata->chip_info->resolution + 1; + switch (chan->type) { + case IIO_TEMP: + /* + * raw is normalised to 2^(resolution + 1), i.e. + * raw = 2 * DATAOUT24, so the PTAT scale (datasheet + * Vref / 1570 per Kelvin) doubles its denominator and, + * in m°C, becomes Vref_uV / 3140000. + */ + *val = ret; + *val2 = 3140000; + return IIO_VAL_FRACTIONAL; + case IIO_VOLTAGE: + *val = ret / (MICRO / MILLI); + *val2 = ddata->chip_info->resolution + 1; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } - return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + ret = regulator_get_voltage(ddata->ref); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + /* + * 0 °C == 273.15 K must map to raw + offset such that + * (raw + offset) * scale == 0 m°C, i.e. + * offset = -273150 / scale + * = -273150 * 3140000 / Vref_uV + * Computed in 64-bit to avoid overflow. + */ + *val = div_s64(ABSOLUTE_ZERO_MILLICELSIUS * 3140000LL, ret); + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * Only advertised on the voltage channels of parts with a speed + * mode; the sampling frequency is a property of the selected 1x/2x + * mode, not of an individual conversion. + */ + mutex_lock(&ddata->lock); + *val = ltc2497core_samp_freq_avail[ddata->speed_2x * 2]; + *val2 = ltc2497core_samp_freq_avail[ddata->speed_2x * 2 + 1]; + mutex_unlock(&ddata->lock); + + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } } -#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ +static int ltc2497core_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = ltc2497core_samp_freq_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(ltc2497core_samp_freq_avail); + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int ltc2497core_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + unsigned int i; + bool speed_2x; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + /* Match the (val, val2) pair against the advertised rates. */ + for (i = 0; i < ARRAY_SIZE(ltc2497core_samp_freq_avail); i += 2) { + if (val == ltc2497core_samp_freq_avail[i] && + val2 == ltc2497core_samp_freq_avail[i + 1]) + break; + } + if (i == ARRAY_SIZE(ltc2497core_samp_freq_avail)) + return -EINVAL; + + speed_2x = i / 2; + + mutex_lock(&ddata->lock); + ddata->speed_2x = speed_2x; + /* + * The new speed only takes effect once the second command byte + * is reprogrammed, so force the next read to reprogram rather + * than reuse the value already latched for this address. + * LTC2497_CONFIG_DEFAULT is not a valid channel/temperature + * address, so it is a safe re-arm sentinel (as used at probe). + * + * A conversion started under the old speed may still be in + * flight; its own duration (conv_time_prev), not the new mode's, + * still gates the next reprogram, so the timing state is left + * untouched here. + */ + ddata->addr_prev = LTC2497_CONFIG_DEFAULT; + mutex_unlock(&ddata->lock); + + return 0; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr, _ds_name, _extra_mask) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = (_chan), \ .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_extra_mask), \ + .info_mask_shared_by_type_available = (_extra_mask), \ .datasheet_name = (_ds_name), \ } -#define LTC2497_CHAN_DIFF(_chan, _addr) { \ +#define LTC2497_CHAN_DIFF(_chan, _addr, _extra_mask) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ .address = (_addr | _chan), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_extra_mask), \ + .info_mask_shared_by_type_available = (_extra_mask), \ .differential = 1, \ } -static const struct iio_chan_spec ltc2497core_channel[] = { - LTC2497_CHAN(0, LTC2497_SGL, "CH0"), - LTC2497_CHAN(1, LTC2497_SGL, "CH1"), - LTC2497_CHAN(2, LTC2497_SGL, "CH2"), - LTC2497_CHAN(3, LTC2497_SGL, "CH3"), - LTC2497_CHAN(4, LTC2497_SGL, "CH4"), - LTC2497_CHAN(5, LTC2497_SGL, "CH5"), - LTC2497_CHAN(6, LTC2497_SGL, "CH6"), - LTC2497_CHAN(7, LTC2497_SGL, "CH7"), - LTC2497_CHAN(8, LTC2497_SGL, "CH8"), - LTC2497_CHAN(9, LTC2497_SGL, "CH9"), - LTC2497_CHAN(10, LTC2497_SGL, "CH10"), - LTC2497_CHAN(11, LTC2497_SGL, "CH11"), - LTC2497_CHAN(12, LTC2497_SGL, "CH12"), - LTC2497_CHAN(13, LTC2497_SGL, "CH13"), - LTC2497_CHAN(14, LTC2497_SGL, "CH14"), - LTC2497_CHAN(15, LTC2497_SGL, "CH15"), - LTC2497_CHAN_DIFF(0, LTC2497_DIFF), - LTC2497_CHAN_DIFF(1, LTC2497_DIFF), - LTC2497_CHAN_DIFF(2, LTC2497_DIFF), - LTC2497_CHAN_DIFF(3, LTC2497_DIFF), - LTC2497_CHAN_DIFF(4, LTC2497_DIFF), - LTC2497_CHAN_DIFF(5, LTC2497_DIFF), - LTC2497_CHAN_DIFF(6, LTC2497_DIFF), - LTC2497_CHAN_DIFF(7, LTC2497_DIFF), - LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), -}; +/* + * SPD is ignored by the part during a temperature measurement, so the + * temperature channel carries no sampling-frequency attribute. + */ +#define LTC2497_TEMP_CHANNEL { \ + .type = IIO_TEMP, \ + .address = LTC2497_TEMP_ADDR, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +/* + * Parts with a speed mode advertise sampling_frequency on their voltage + * channels; the two tables are otherwise identical. + */ +#define LTC2497_DEFINE_CHANNELS(_name, _extra_mask) \ +static const struct iio_chan_spec _name[] = { \ + LTC2497_CHAN(0, LTC2497_SGL, "CH0", _extra_mask), \ + LTC2497_CHAN(1, LTC2497_SGL, "CH1", _extra_mask), \ + LTC2497_CHAN(2, LTC2497_SGL, "CH2", _extra_mask), \ + LTC2497_CHAN(3, LTC2497_SGL, "CH3", _extra_mask), \ + LTC2497_CHAN(4, LTC2497_SGL, "CH4", _extra_mask), \ + LTC2497_CHAN(5, LTC2497_SGL, "CH5", _extra_mask), \ + LTC2497_CHAN(6, LTC2497_SGL, "CH6", _extra_mask), \ + LTC2497_CHAN(7, LTC2497_SGL, "CH7", _extra_mask), \ + LTC2497_CHAN(8, LTC2497_SGL, "CH8", _extra_mask), \ + LTC2497_CHAN(9, LTC2497_SGL, "CH9", _extra_mask), \ + LTC2497_CHAN(10, LTC2497_SGL, "CH10", _extra_mask), \ + LTC2497_CHAN(11, LTC2497_SGL, "CH11", _extra_mask), \ + LTC2497_CHAN(12, LTC2497_SGL, "CH12", _extra_mask), \ + LTC2497_CHAN(13, LTC2497_SGL, "CH13", _extra_mask), \ + LTC2497_CHAN(14, LTC2497_SGL, "CH14", _extra_mask), \ + LTC2497_CHAN(15, LTC2497_SGL, "CH15", _extra_mask), \ + LTC2497_CHAN_DIFF(0, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(1, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(2, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(3, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(4, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(5, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(6, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(7, LTC2497_DIFF, _extra_mask), \ + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN, _extra_mask), \ + LTC2497_TEMP_CHANNEL, \ +} + +LTC2497_DEFINE_CHANNELS(ltc2497core_channel, 0); +LTC2497_DEFINE_CHANNELS(ltc2497core_channel_samp_freq, BIT(IIO_CHAN_INFO_SAMP_FREQ)); static const struct iio_info ltc2497core_info = { .read_raw = ltc2497core_read_raw, + .read_avail = ltc2497core_read_avail, + .write_raw = ltc2497core_write_raw, }; int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) @@ -182,8 +370,15 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) indio_dev->info = <c2497core_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ltc2497core_channel; - indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel); + if (ddata->chip_info->has_speed_mode) + indio_dev->channels = ltc2497core_channel_samp_freq; + else + indio_dev->channels = ltc2497core_channel; + /* Only the ltc2499 has a temperature channel; it is the last entry. */ + if (ddata->chip_info->has_temp) + indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel); + else + indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel) - 1; ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL); if (ret < 0) @@ -209,6 +404,8 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) ddata->addr_prev = LTC2497_CONFIG_DEFAULT; ddata->time_prev = ktime_get(); + /* Power-on default mode is 1x; a conversion is already in flight. */ + ddata->conv_time_prev = LTC2497_CONV_TIME_1X_MS; mutex_init(&ddata->lock); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index c1668b5a351e..15458d4e21a1 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -84,6 +84,46 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, return 0; } + /* + * Parts with a second config byte (LTC2499: internal PTAT sensor and/or + * the 2x speed mode) latch their converter configuration from that byte + * and only re-evaluate it when EN2 is set; a single byte, or a second + * byte with EN2 = 0, means "keep previous". A one-byte channel select + * therefore cannot pull the device back out of temperature mode, so a + * voltage read after a temperature read would keep returning the PTAT + * result. Always drive the second byte with EN2 set on these parts: + * - temperature read: IM = 1 (SPD is ignored by the part in + * temperature mode and is left 0 here); + * - voltage read: IM = 0 (external input), plus SPD when 2x is + * selected. + * FA = FB = 0 keeps the power-on simultaneous 50/60Hz rejection, whose + * worst-case conversion time the driver's wait already covers. + * + * The byte could be skipped while the latched configuration is already + * the one wanted; it is sent on every conversion for simplicity. + */ + if (ddata->chip_info->has_temp || ddata->chip_info->has_speed_mode) { + u8 cmd[2]; + + if (address == LTC2497_TEMP_ADDR) { + cmd[0] = LTC2497_ENABLE | LTC2497_CONFIG_DEFAULT; + cmd[1] = LTC2499_EN2 | LTC2499_IM; + } else { + cmd[0] = LTC2497_ENABLE | address; + cmd[1] = LTC2499_EN2; + if (ddata->speed_2x) + cmd[1] |= LTC2499_SPD; + } + + ret = i2c_master_send(st->client, cmd, sizeof(cmd)); + if (ret < 0) { + dev_err(&st->client->dev, "i2c transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + return 0; + } + ret = i2c_smbus_write_byte(st->client, LTC2497_ENABLE | address); if (ret) @@ -137,6 +177,8 @@ static const struct ltc2497_chip_info ltc2497_info[] = { [TYPE_LTC2499] = { .resolution = 24, .name = "ltc2499", + .has_temp = true, + .has_speed_mode = true, }, }; diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h index 64e81c95a3dd..cee9a2f50b82 100644 --- a/drivers/iio/adc/ltc2497.h +++ b/drivers/iio/adc/ltc2497.h @@ -2,11 +2,35 @@ #define LTC2497_ENABLE 0xA0 #define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE -#define LTC2497_CONVERSION_TIME_MS 150ULL + +/* + * Conversion-time bounds used to gate reads. Each value is the datasheet + * t_CONV maximum, rounded UP to the next whole millisecond. + * + * The 2x mode (LTC2499_SPD, LTC2499 only) disables the offset auto-calibration + * to roughly double the output rate; adding the 2x wait time is what makes the + * SPD control actually faster. + */ +#define LTC2497_CONV_TIME_1X_MS 150ULL /* ceil(t_CONV_1 simult. max 149.9) */ +#define LTC2499_CONV_TIME_2X_MS 76ULL /* ceil(t_CONV_2 simult. max 75.1) */ + +/* + * Sentinel passed as `address` to result_and_measure() to request a + * temperature conversion instead of a voltage channel. Valid channel + * addresses fit in 5 bits (0x00–0x1F), so 0xFF is unambiguous. + */ +#define LTC2497_TEMP_ADDR 0xFF + +/* Second config-byte bits (LTC2499 / LTC2493 only) */ +#define LTC2499_EN2 BIT(7) /* enable second config byte */ +#define LTC2499_IM BIT(6) /* 1 = measure internal temp sensor */ +#define LTC2499_SPD BIT(3) /* 1 = 2x output rate (offset cal off) */ struct ltc2497_chip_info { - u32 resolution; const char *name; + u32 resolution; + bool has_temp; + bool has_speed_mode; /* SPD bit in the 2nd config byte (LTC2499/LTC2493) */ }; struct ltc2497core_driverdata { @@ -16,6 +40,9 @@ struct ltc2497core_driverdata { struct mutex lock; const struct ltc2497_chip_info *chip_info; u8 addr_prev; + bool speed_2x; /* SPD: false = 1x (default), true = 2x */ + /* Conversion time (ms) of the conversion currently in flight. */ + unsigned int conv_time_prev; int (*result_and_measure)(struct ltc2497core_driverdata *ddata, u8 address, int *val); }; diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c index 63b6561dcf47..53ac2f4f383e 100644 --- a/drivers/iio/adc/max11205.c +++ b/drivers/iio/adc/max11205.c @@ -117,6 +117,8 @@ static int max11205_probe(struct spi_device *spi) ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info); st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODATA; indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c index 58adeac62dde..7694fbb6a9fd 100644 --- a/drivers/iio/adc/max14001.c +++ b/drivers/iio/adc/max14001.c @@ -366,6 +366,7 @@ static const struct spi_device_id max14001_id_table[] = { { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info }, { } }; +MODULE_DEVICE_TABLE(spi, max14001_id_table); static const struct of_device_id max14001_of_match[] = { { .compatible = "adi,max14001", .data = &max14001_chip_info }, diff --git a/drivers/iio/adc/max40080.c b/drivers/iio/adc/max40080.c new file mode 100644 index 000000000000..c6946eb6ff5b --- /dev/null +++ b/drivers/iio/adc/max40080.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MAX40080 Digital Current-Sense Amplifier driver + * + * Copyright 2026 Analog Devices, Inc. + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX40080.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define MAX40080_REG_CFG 0x00 +#define MAX40080_CFG_MODE_MSK GENMASK(2, 0) +#define MAX40080_CFG_PEC_EN_MSK BIT(5) +#define MAX40080_CFG_RANGE_MSK BIT(6) +#define MAX40080_CFG_FILTER_MSK GENMASK(14, 12) + +#define MAX40080_REG_FIFO_CFG 0x0A +#define MAX40080_FIFO_CFG_STORE_IV_MSK GENMASK(1, 0) + +#define MAX40080_REG_IV 0x10 +/* Current is a 13-bit two's-complement value (magnitude + sign bit). */ +#define MAX40080_IV_I_MSK GENMASK(12, 0) +#define MAX40080_IV_I_SIGN_BIT 12 +#define MAX40080_IV_V_MAG_MSK GENMASK(27, 16) +#define MAX40080_IV_VALID_MSK BIT(31) + +/* CFG.mode field values. */ +#define MAX40080_CFG_MODE_STDBY 0x00 +#define MAX40080_CFG_MODE_SINGLE 0x02 + +/* CFG.range field values. */ +#define MAX40080_CFG_RANGE_50mV 0 +#define MAX40080_CFG_RANGE_10mV 1 + +/* FIFO_CFG.store_iv field values. */ +#define MAX40080_FIFO_CFG_STORE_IV 0x02 + +#define MAX40080_ADC_RES_BITS 12 +#define MAX40080_INTER_VREF_mV 1250 +#define MAX40080_V_BUFF_GAIN 30 +#define MAX40080_CSA_50mV_GAIN 25 +#define MAX40080_CSA_10mV_GAIN 125 + +/* + * The RANGE field (CFG bit 6) selects one of two current-sense full-scale + * ranges (the MAX40080 supports exactly two: +/-50 mV and +/-10 mV). Indexed + * by the CFG.range field value. + */ +static const int max40080_csa_gain[] = { + [MAX40080_CFG_RANGE_50mV] = MAX40080_CSA_50mV_GAIN, + [MAX40080_CFG_RANGE_10mV] = MAX40080_CSA_10mV_GAIN, +}; + +struct max40080_state { + struct i2c_client *client; + /* Serializes read-modify-write access to the CFG register. */ + struct mutex lock; + u32 shunt_resistor_uOhm; + /* Cached configuration: the selected RANGE index and oversampling ratio. */ + unsigned int range; + int oversampling_ratio; + /* + * Precomputed current scale (mA per code) for each RANGE setting, as + * {integer, nano} pairs for IIO_VAL_INT_PLUS_NANO. The range is + * selected by writing the corresponding scale. + */ + int current_scale[2][2]; +}; + +static const int max40080_oversampling_avail[] = { 1, 8, 16, 32, 64, 128 }; + +static int max40080_update_bits(struct max40080_state *st, u8 reg, + u16 mask, u16 val) +{ + int tmp; + + tmp = i2c_smbus_read_word_data(st->client, reg); + if (tmp < 0) + return tmp; + + tmp = (tmp & ~mask) | (val & mask); + + return i2c_smbus_write_word_data(st->client, reg, tmp); +} + +/* + * In single-measurement mode the device sits idle until it receives an SMBus + * Quick Command, then performs exactly one current and one voltage conversion + * and returns to idle. Triggering on demand this way (rather than running the + * FIFO continuously in active mode) means each read returns a fresh, coherent + * current/voltage pair instead of the oldest queued FIFO entry. + */ +static int max40080_trigger_measurement(struct max40080_state *st) +{ + return i2c_smbus_xfer(st->client->adapter, st->client->addr, + st->client->flags, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL); +} + +/* + * A single measurement holds the matched current/voltage pair in one 32-bit + * word (MAX40080_REG_IV). Reading all four bytes in one transaction returns + * both from the same conversion; reading the separate current (0x0C) and + * voltage (0x0E) registers would decorrelate the two channels. + * + * Unlike the word accesses used elsewhere, this is a plain I2C block read: the + * SMBus layer does not append or verify a PEC byte for it even when PEC is + * otherwise enabled for the device, so this transfer is not PEC protected. + */ +static int max40080_read_iv_once(struct max40080_state *st, u32 *iv) +{ + __le32 buf = 0; + int ret; + + ret = i2c_smbus_read_i2c_block_data(st->client, MAX40080_REG_IV, + sizeof(buf), (u8 *)&buf); + if (ret < 0) + return ret; + + *iv = le32_to_cpu(buf); + + return 0; +} + +static int max40080_read_iv(struct max40080_state *st, u32 *iv) +{ + u32 tmp = 0; + int ret, io_ret; + + guard(mutex)(&st->lock); + + ret = max40080_trigger_measurement(st); + if (ret < 0) + return ret; + + /* + * Wait for the conversion to complete by polling the FIFO valid bit + * (or bail out on an I2C error). Polling the device's own status makes + * this independent of the actual conversion time, which varies with the + * oversampling ratio and the bus speed. The timeout is only a safety + * ceiling: the worst case is the maximum 128x averaging on both the + * current and voltage channels at the slowest 15 ksps base rate plus + * the inter-channel switching time, i.e. roughly 20 ms; 50 ms leaves + * ample margin. + */ + ret = read_poll_timeout(max40080_read_iv_once, io_ret, + io_ret || (tmp & MAX40080_IV_VALID_MSK), + 500, 50 * USEC_PER_MSEC, false, st, &tmp); + if (io_ret) + return io_ret; + + /* + * Propagate the last-read value even on timeout so the caller can + * inspect it for debugging. + */ + *iv = tmp; + + return ret; +} + +static int max40080_get_current(struct max40080_state *st, int *val) +{ + u32 iv; + int ret; + + ret = max40080_read_iv(st, &iv); + if (ret) + return ret; + + *val = sign_extend32(FIELD_GET(MAX40080_IV_I_MSK, iv), + MAX40080_IV_I_SIGN_BIT); + + return 0; +} + +static int max40080_get_voltage(struct max40080_state *st, int *val) +{ + u32 iv; + int ret; + + ret = max40080_read_iv(st, &iv); + if (ret) + return ret; + + *val = FIELD_GET(MAX40080_IV_V_MAG_MSK, iv); + + return 0; +} + +static int max40080_set_range(struct max40080_state *st, unsigned int range) +{ + int ret; + + ret = max40080_update_bits(st, MAX40080_REG_CFG, MAX40080_CFG_RANGE_MSK, + FIELD_PREP(MAX40080_CFG_RANGE_MSK, range)); + if (ret) + return ret; + + WRITE_ONCE(st->range, range); + + return 0; +} + +/* + * Precompute the current scale (mA per code) for each RANGE setting as + * {integer, nano} pairs. The shunt drop for a full-scale code is + * Vref[mV] / (BIT(ADC_RES_BITS) * gain) + * and current = Vshunt / Rshunt, so with Rshunt in micro-ohms the scale in + * mA/code is + * Vref[mV] * NANO * MICRO / (BIT(ADC_RES_BITS) * gain * Rshunt[uOhm]) + * expressed as an integer part plus a nano fractional part. + */ +static void max40080_calc_current_scale(struct max40080_state *st) +{ + u64 numerator, denominator; + u32 rem; + + for (unsigned int i = 0; i < ARRAY_SIZE(max40080_csa_gain); i++) { + numerator = (u64)MAX40080_INTER_VREF_mV * NANO * MICRO; + denominator = BIT_ULL(MAX40080_ADC_RES_BITS) * max40080_csa_gain[i] * + st->shunt_resistor_uOhm; + numerator = div64_u64(numerator, denominator); + st->current_scale[i][0] = div_u64_rem(numerator, NANO, &rem); + st->current_scale[i][1] = rem; + } +} + +/* + * max40080_oversampling_avail[] is ordered so that its index is the FILTER + * field value (index 0 = no averaging, index 1 = 8x, ...). Return that index + * for an exact match, or -EINVAL for a value that is not on the list. + */ +static int max40080_oversampling_to_filter(int val) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(max40080_oversampling_avail); i++) { + if (max40080_oversampling_avail[i] == val) + return i; + } + + return -EINVAL; +} + +static int max40080_set_oversampling_ratio(struct max40080_state *st, int val) +{ + int filter; + int ret; + + filter = max40080_oversampling_to_filter(val); + if (filter < 0) + return filter; + + ret = max40080_update_bits(st, MAX40080_REG_CFG, MAX40080_CFG_FILTER_MSK, + FIELD_PREP(MAX40080_CFG_FILTER_MSK, filter)); + if (ret) + return ret; + + WRITE_ONCE(st->oversampling_ratio, val); + + return 0; +} + +static int max40080_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max40080_state *st = iio_priv(indio_dev); + unsigned int range; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_CURRENT) { + ret = max40080_get_current(st, val); + if (ret) + return ret; + } else if (chan->type == IIO_VOLTAGE) { + ret = max40080_get_voltage(st, val); + if (ret) + return ret; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_CURRENT) { + /* + * The selectable current-sense range is exposed through + * scale: each RANGE setting has its own precomputed + * mA-per-code value. Userspace picks the range by + * writing the matching scale. + * + * Use READ_ONCE to ensure the compiler reads st->range + * exactly once, so val and val2 come from the same + * setting even if a concurrent write changes st->range. + */ + range = READ_ONCE(st->range); + *val = st->current_scale[range][0]; + *val2 = st->current_scale[range][1]; + return IIO_VAL_INT_PLUS_NANO; + } + /* voltage[mV] = raw * Vref[mV] * buffer_gain / BIT(ADC_RES_BITS) */ + *val = MAX40080_INTER_VREF_mV * MAX40080_V_BUFF_GAIN; + *val2 = MAX40080_ADC_RES_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = READ_ONCE(st->oversampling_ratio); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int max40080_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max40080_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* Only the current channel has a selectable range/scale. */ + if (chan->type != IIO_CURRENT) + return -EINVAL; + + for (unsigned int i = 0; i < ARRAY_SIZE(max40080_csa_gain); i++) { + if (val == st->current_scale[i][0] && + val2 == st->current_scale[i][1]) + return max40080_set_range(st, i); + } + + return -EINVAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return max40080_set_oversampling_ratio(st, val); + default: + return -EINVAL; + } +} + +static int max40080_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT; + } +} + +static int max40080_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct max40080_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_CURRENT) + return -EINVAL; + + *vals = (int *)st->current_scale; + *length = ARRAY_SIZE(max40080_csa_gain) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = max40080_oversampling_avail; + *length = ARRAY_SIZE(max40080_oversampling_avail); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int max40080_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int write_val, unsigned int *read_val) +{ + struct max40080_state *st = iio_priv(indio_dev); + int val; + + if (read_val) { + val = i2c_smbus_read_word_data(st->client, reg); + if (val < 0) + return val; + + *read_val = val; + + return 0; + } + + return i2c_smbus_write_word_data(st->client, reg, write_val); +} + +static const struct iio_info max40080_info = { + .read_raw = max40080_read_raw, + .write_raw = max40080_write_raw, + .write_raw_get_fmt = max40080_write_raw_get_fmt, + .read_avail = max40080_read_avail, + .debugfs_reg_access = &max40080_reg_access, +}; + +static const struct iio_chan_spec max40080_channels[] = { + { + .type = IIO_CURRENT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, +}; + +/* + * Configure the device from the cached state. The device powers up in standby + * with PEC enabled (CFG POR = 0x0060), so PEC is kept enabled throughout. + */ +static int max40080_init(struct max40080_state *st) +{ + u16 fifo_cfg, cfg; + int ret, filter; + + filter = max40080_oversampling_to_filter(st->oversampling_ratio); + if (filter < 0) + return filter; + + /* + * Put the device in standby before (re)configuring the FIFO: the FIFO + * configuration register can only be written while the device is not + * converting. + */ + cfg = FIELD_PREP(MAX40080_CFG_MODE_MSK, MAX40080_CFG_MODE_STDBY) | + FIELD_PREP(MAX40080_CFG_PEC_EN_MSK, 1); + ret = i2c_smbus_write_word_data(st->client, MAX40080_REG_CFG, cfg); + if (ret) + return ret; + + /* Store a matched current+voltage pair per conversion. */ + fifo_cfg = FIELD_PREP(MAX40080_FIFO_CFG_STORE_IV_MSK, MAX40080_FIFO_CFG_STORE_IV); + ret = i2c_smbus_write_word_data(st->client, MAX40080_REG_FIFO_CFG, + fifo_cfg); + if (ret) + return ret; + + /* + * Use single-measurement mode: the device stays idle and converts once + * per SMBus Quick Command (see max40080_trigger_measurement()), so each + * read returns a fresh sample rather than a queued FIFO entry. + */ + cfg = FIELD_PREP(MAX40080_CFG_MODE_MSK, MAX40080_CFG_MODE_SINGLE) | + FIELD_PREP(MAX40080_CFG_PEC_EN_MSK, 1) | + FIELD_PREP(MAX40080_CFG_RANGE_MSK, st->range) | + FIELD_PREP(MAX40080_CFG_FILTER_MSK, filter); + + return i2c_smbus_write_word_data(st->client, MAX40080_REG_CFG, cfg); +} + +static int max40080_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const char *propname; + struct iio_dev *indio_dev; + struct max40080_state *st; + int ret; + + /* + * The device powers up with PEC enabled (CFG POR = 0x0060) and rejects + * unprotected transactions, so PEC support is mandatory, along with + * word access, the I2C block read used for the current/voltage pair, + * and the Quick Command used to trigger a conversion. + */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_PEC)) + return -EOPNOTSUPP; + + client->flags |= I2C_CLIENT_PEC; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->client = client; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + propname = "shunt-resistor-micro-ohms"; + ret = device_property_read_u32(dev, propname, &st->shunt_resistor_uOhm); + if (ret) + return dev_err_probe(dev, ret, "can't read %s\n", propname); + if (!st->shunt_resistor_uOhm) + return dev_err_probe(dev, -EINVAL, "%s must be non-zero\n", propname); + + max40080_calc_current_scale(st); + + /* Defaults: 50 mV range, no averaging. */ + st->range = MAX40080_CFG_RANGE_50mV; + st->oversampling_ratio = 1; + + indio_dev->name = "max40080"; + indio_dev->info = &max40080_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max40080_channels; + indio_dev->num_channels = ARRAY_SIZE(max40080_channels); + + ret = max40080_init(st); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id max40080_i2c_ids[] = { + { .name = "max40080" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max40080_i2c_ids); + +static const struct of_device_id max40080_of_match[] = { + { .compatible = "adi,max40080" }, + { } +}; +MODULE_DEVICE_TABLE(of, max40080_of_match); + +static struct i2c_driver max40080_driver = { + .driver = { + .name = "max40080", + .of_match_table = max40080_of_match, + }, + .probe = max40080_probe, + .id_table = max40080_i2c_ids, +}; +module_i2c_driver(max40080_driver); + +MODULE_AUTHOR("Ciprian Hegbeli "); +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices MAX40080 current-sense amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 797424f59f74..90167930f913 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -721,6 +721,8 @@ static int mcp3911_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; adc->chip = spi_get_device_match_data(spi); + if (!adc->chip) + return -ENODATA; ret = devm_regulator_get_enable_read_voltage(dev, "vref"); if (ret < 0 && ret != -ENODEV) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 000e39ca5c62..2b003fd5afeb 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -739,7 +739,7 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); struct device *dev = indio_dev->dev.parent; - struct clk_init_data init; + struct clk_init_data init = { }; const char *clk_parents[1]; init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_div", dev_name(dev)); diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 894d3211b283..2f716a2f97b8 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -604,7 +604,7 @@ static void nxp_sar_adc_dma_cb(void *data) dma_buf = &info->dma_buf; dma_samples = (u32 *)dma_buf->buf; - dev_dma = info->dma_chan->device->dev; + dev_dma = dmaengine_get_dma_device(info->dma_chan); /* * DMA in some corner cases might have already be charged for diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 23055405a6e0..2d934452eb11 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* @@ -494,11 +495,13 @@ static ssize_t pac1934_shunt_value_store(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct pac1934_chip_info *info = iio_priv(indio_dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int sh_val; + unsigned int sh_val; + int ret; - if (kstrtouint(buf, 10, &sh_val)) { + ret = kstrtouint(buf, 10, &sh_val); + if (ret) { dev_err(dev, "Shunt value is not valid\n"); - return -EINVAL; + return ret; } scoped_guard(mutex, &info->lock) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 0f0bf2906af0..5c45fae11890 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -348,6 +348,19 @@ static const struct rockchip_saradc_data rk3588_saradc_data = { .read = rockchip_saradc_read_v2, }; +static const struct iio_chan_spec rockchip_rv1106_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), +}; + +static const struct rockchip_saradc_data rv1106_saradc_data = { + .channels = rockchip_rv1106_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rv1106_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + static const struct of_device_id rockchip_saradc_match[] = { { .compatible = "rockchip,saradc", @@ -370,6 +383,9 @@ static const struct of_device_id rockchip_saradc_match[] = { }, { .compatible = "rockchip,rk3588-saradc", .data = &rk3588_saradc_data, + }, { + .compatible = "rockchip,rv1106-saradc", + .data = &rv1106_saradc_data, }, { } }; diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index 408fbf8c29cc..3b95f47445a8 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -62,7 +62,7 @@ * @adsmp_mask: ADC sampling period mask (see ADM3 register) * @adint_inten_mask: conversion end interrupt mask (see ADINT register) * @default_adcmp: default ADC cmp (see ADM3 register) - * @num_channels: number of supported channels + * @max_channels: maximum number of channels * @adivc: specifies if ADVIC register is available */ struct rzg2l_adc_hw_params { @@ -70,7 +70,7 @@ struct rzg2l_adc_hw_params { u16 adsmp_mask; u16 adint_inten_mask; u8 default_adcmp; - u8 num_channels; + u8 max_channels; bool adivc; }; @@ -196,7 +196,7 @@ static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) /* Select analog input channel subjected to conversion. */ reg = rzg2l_adc_readl(adc, RZG2L_ADM(2)); - reg &= ~GENMASK(hw_params->num_channels - 1, 0); + reg &= ~GENMASK(hw_params->max_channels - 1, 0); reg |= BIT(ch); rzg2l_adc_writel(adc, RZG2L_ADM(2), reg); @@ -307,11 +307,11 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } - intst = reg & GENMASK(hw_params->num_channels - 1, 0); + intst = reg & GENMASK(hw_params->max_channels - 1, 0); if (!intst) return IRQ_NONE; - for_each_set_bit(ch, &intst, hw_params->num_channels) + for_each_set_bit(ch, &intst, hw_params->max_channels) adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK; /* clear the channel interrupt */ @@ -341,12 +341,12 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l num_channels = devm_iio_adc_device_alloc_chaninfo_se(&pdev->dev, &rzg2l_adc_chan_template, - hw_params->num_channels - 1, + hw_params->max_channels - 1, &chan_array); if (num_channels < 0) return num_channels; - if (num_channels > hw_params->num_channels) + if (num_channels > hw_params->max_channels) return dev_err_probe(&pdev->dev, -EINVAL, "num of channel children out of range\n"); @@ -429,7 +429,7 @@ static int rzg2l_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); adc->hw_params = device_get_match_data(dev); - if (!adc->hw_params || adc->hw_params->num_channels > RZG2L_ADC_MAX_CHANNELS) + if (!adc->hw_params || adc->hw_params->max_channels > RZG2L_ADC_MAX_CHANNELS) return -EINVAL; ret = rzg2l_adc_parse_properties(pdev, adc); @@ -484,7 +484,7 @@ static int rzg2l_adc_probe(struct platform_device *pdev) } static const struct rzg2l_adc_hw_params rzg2l_hw_params = { - .num_channels = 8, + .max_channels = 8, .default_adcmp = 0xe, .default_adsmp = { 0x578 }, .adsmp_mask = GENMASK(15, 0), @@ -493,15 +493,24 @@ static const struct rzg2l_adc_hw_params rzg2l_hw_params = { }; static const struct rzg2l_adc_hw_params rzg3s_hw_params = { - .num_channels = 9, + .max_channels = 9, .default_adcmp = 0x1d, .default_adsmp = { 0x7f, 0xff }, .adsmp_mask = GENMASK(7, 0), .adint_inten_mask = GENMASK(11, 0), }; +static const struct rzg2l_adc_hw_params rzg3l_adc1_hw_params = { + .max_channels = 9, + .default_adcmp = 0x1d, + .default_adsmp = { 0x7f, 0xff }, + .adsmp_mask = GENMASK(7, 0), + .adint_inten_mask = BIT(8), +}; + static const struct of_device_id rzg2l_adc_match[] = { { .compatible = "renesas,r9a08g045-adc", .data = &rzg3s_hw_params }, + { .compatible = "renesas,r9a08g046-adc1", .data = &rzg3l_adc1_hw_params }, { .compatible = "renesas,rzg2l-adc", .data = &rzg2l_hw_params }, { } }; diff --git a/drivers/iio/adc/rzt2h_adc.c b/drivers/iio/adc/rzt2h_adc.c index 4e0eb02d3d14..8ba7b0fde8a1 100644 --- a/drivers/iio/adc/rzt2h_adc.c +++ b/drivers/iio/adc/rzt2h_adc.c @@ -2,13 +2,19 @@ #include #include +#include #include #include +#include +#include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -18,6 +24,7 @@ #define RZT2H_ADCSR_ADIE_MASK BIT(12) #define RZT2H_ADCSR_ADCS_MASK GENMASK(14, 13) #define RZT2H_ADCSR_ADCS_SINGLE 0b00 +#define RZT2H_ADCSR_ADCS_CONTINUOUS 0b10 #define RZT2H_ADCSR_ADST_MASK BIT(15) #define RZT2H_ADANSA0_REG 0x04 @@ -25,24 +32,71 @@ #define RZT2H_ADDR_REG(x) (0x20 + 0x2 * (x)) +#define RZT2H_ADSSTRn(n) (0xe0 + 0x1 * (n)) + +/* + * Each A/D channel conversion takes a fixed base of 13 ADCLK cycles plus the + * sample time programmed in ADSSTRn, giving a conversion rate of + * ADCLK / (13 + ADSSTRn). + */ +#define RZT2H_ADC_CONV_CYCLES_BASE 13 +#define RZT2H_ADC_SST_MIN 0x7 +#define RZT2H_ADC_SST_MAX 0xff +#define RZT2H_ADC_SST_DEFAULT 0xb + #define RZT2H_ADCALCTL_REG 0x1f0 #define RZT2H_ADCALCTL_CAL_MASK BIT(0) #define RZT2H_ADCALCTL_CAL_RDY_MASK BIT(1) #define RZT2H_ADCALCTL_CAL_ERR_MASK BIT(2) #define RZT2H_ADC_MAX_CHANNELS 16 +#define RZT2H_ADC_CHANNEL_BYTES sizeof(u16) +#define RZT2H_ADC_DMA_PERIOD_SAMPLES 128 +#define RZT2H_ADC_DMA_PERIODS 64 +#define RZT2H_ADC_DMA_BUFFER_SAMPLES (RZT2H_ADC_DMA_PERIODS * \ + RZT2H_ADC_DMA_PERIOD_SAMPLES) +#define RZT2H_ADC_DMA_BUFFER_SIZE (RZT2H_ADC_DMA_BUFFER_SAMPLES * \ + RZT2H_ADC_MAX_CHANNELS * \ + RZT2H_ADC_CHANNEL_BYTES) + +struct rzt2h_adc_dma { + struct dma_chan *chan; + u16 *buf; + dma_addr_t addr; + + unsigned int period_index; + unsigned int period_bytes; + unsigned int first_chan; + unsigned int sample_chans; + + u8 gather[RZT2H_ADC_MAX_CHANNELS]; + unsigned int gather_len; + + atomic_t pending_periods; + + wait_queue_head_t wq; + struct task_struct *thread; +}; struct rzt2h_adc { void __iomem *base; struct device *dev; + phys_addr_t phys_base; + struct rzt2h_adc_dma dma; struct completion completion; /* lock to protect against multiple access to the device */ struct mutex lock; const struct iio_chan_spec *channels; unsigned int num_channels; - unsigned int max_channels; + u16 buf[RZT2H_ADC_MAX_CHANNELS]; + + unsigned long adclk_rate; + int samp_freq_avail[3]; + u8 sst[RZT2H_ADC_MAX_CHANNELS]; + + int irq; }; static void rzt2h_adc_start(struct rzt2h_adc *adc, unsigned int conversion_type) @@ -72,6 +126,11 @@ static void rzt2h_adc_stop(struct rzt2h_adc *adc) writew(reg, adc->base + RZT2H_ADCSR_REG); } +static void rzt2h_adc_set_sst(struct rzt2h_adc *adc, unsigned int ch, u8 sst) +{ + writeb(sst, adc->base + RZT2H_ADSSTRn(ch)); +} + static int rzt2h_adc_read_single(struct rzt2h_adc *adc, unsigned int ch, int *val) { int ret; @@ -87,13 +146,18 @@ static int rzt2h_adc_read_single(struct rzt2h_adc *adc, unsigned int ch, int *va /* Enable a single channel */ writew(RZT2H_ADANSA0_CH_MASK(ch), adc->base + RZT2H_ADANSA0_REG); + rzt2h_adc_set_sst(adc, ch, adc->sst[ch]); + rzt2h_adc_start(adc, RZT2H_ADCSR_ADCS_SINGLE); /* - * Datasheet Page 2770, Table 41.1: - * 0.32us per channel when sample-and-hold circuits are not in use. + * Conversion can take up to ~4.3us at the maximum configurable ADSSTRn + * (ADSSTRn + 13 cycles at 62.5 MHz), which rounds up to 1 jiffy. A bare + * 1-jiffy timeout can expire almost immediately if it's armed right + * before a tick, so add one more jiffy to guarantee the conversion time + * actually elapses. */ - ret = wait_for_completion_timeout(&adc->completion, usecs_to_jiffies(1)); + ret = wait_for_completion_timeout(&adc->completion, usecs_to_jiffies(5) + 1); if (!ret) { ret = -ETIMEDOUT; goto disable; @@ -149,6 +213,281 @@ static int rzt2h_adc_calibrate(struct rzt2h_adc *adc) return 0; } +static void rzt2h_adc_push_period(struct iio_dev *indio_dev, u16 *period, + dma_addr_t addr) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + u16 *dst = adc->buf; + u16 *src = period; + + dma_sync_single_for_cpu(adc->dev, addr, adc->dma.period_bytes, + DMA_FROM_DEVICE); + + for (unsigned int sample = 0; sample < RZT2H_ADC_DMA_PERIOD_SAMPLES; sample++) { + for (unsigned int i = 0; i < adc->dma.gather_len; i++) + dst[i] = src[adc->dma.gather[i]]; + + src += adc->dma.sample_chans; + + iio_push_to_buffers(indio_dev, adc->buf); + } +} + +static void rzt2h_adc_advance_period_index(struct rzt2h_adc *adc, unsigned int i) +{ + adc->dma.period_index += i; + adc->dma.period_index %= RZT2H_ADC_DMA_PERIODS; +} + +static void rzt2h_adc_dma_thread_loop(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + int pending, drop; + dma_addr_t addr; + u16 *period; + + pending = atomic_xchg(&adc->dma.pending_periods, 0); + + if (pending >= RZT2H_ADC_DMA_PERIODS) { + /* + * The consumer fell a full buffer behind and the oldest periods + * have already been overwritten by the cyclic DMA transfer. + * Drop them and jump straight to the oldest period that has + * not been overwritten. + */ + drop = pending - RZT2H_ADC_DMA_PERIODS + 1; + + rzt2h_adc_advance_period_index(adc, drop); + pending -= drop; + } + + for (unsigned int i = 0; i < pending; i++) { + unsigned int backlog = atomic_read(&adc->dma.pending_periods) + + pending - i; + + /* + * Bail if enough new periods have completed since reading the + * pending_periods that the next period about to be read is at + * risk of being overwritten. + */ + if (backlog >= RZT2H_ADC_DMA_PERIODS) { + rzt2h_adc_advance_period_index(adc, pending - i); + break; + } + + period = adc->dma.buf + adc->dma.period_index * + RZT2H_ADC_DMA_PERIOD_SAMPLES * adc->dma.sample_chans; + addr = adc->dma.addr + adc->dma.period_index * + adc->dma.period_bytes; + + rzt2h_adc_push_period(indio_dev, period, addr); + rzt2h_adc_advance_period_index(adc, 1); + } +} + +static int rzt2h_adc_dma_thread(void *data) +{ + struct iio_dev *indio_dev = data; + struct rzt2h_adc *adc = iio_priv(indio_dev); + + while (!kthread_should_stop()) { + wait_event_interruptible(adc->dma.wq, + atomic_read(&adc->dma.pending_periods) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + rzt2h_adc_dma_thread_loop(indio_dev); + } + + return 0; +} + +static void rzt2h_adc_dma_callback(void *data) +{ + struct iio_dev *indio_dev = data; + struct rzt2h_adc *adc = iio_priv(indio_dev); + + atomic_inc(&adc->dma.pending_periods); + wake_up(&adc->dma.wq); +} + +static void rzt2h_adc_dma_calc_layout(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + unsigned int hi = 0, lo = RZT2H_ADC_MAX_CHANNELS - 1; + const struct iio_chan_spec *chan; + unsigned int sample_chans; + unsigned int first_chan; + unsigned int scan_index; + unsigned int swap; + unsigned int idx; + + /* Find the lowest and highest enabled channel. */ + iio_for_each_active_channel(indio_dev, scan_index) { + chan = &indio_dev->channels[scan_index]; + + lo = min_t(unsigned int, lo, chan->channel); + hi = max_t(unsigned int, hi, chan->channel); + } + + /* + * The DMA has no scatter-gather and transfers must have a power-of-two + * width, so pick the smallest power-of-two-aligned block of channels + * that covers all enabled channels. + */ + for (sample_chans = 1; sample_chans < RZT2H_ADC_MAX_CHANNELS; sample_chans <<= 1) { + first_chan = round_down(lo, sample_chans); + + if (first_chan + sample_chans > hi) + break; + } + + /* + * Build a table to map each enabled channel to its position in the + * transferred block, it will be used later to extract only the enabled + * channels out of it. + * The DMA moves data in 32-bit words, which swaps each pair of adjacent + * 16-bit channels. Undo it. + */ + adc->dma.gather_len = 0; + swap = sample_chans > 1; + iio_for_each_active_channel(indio_dev, scan_index) { + chan = &indio_dev->channels[scan_index]; + idx = chan->channel - first_chan; + + adc->dma.gather[adc->dma.gather_len++] = idx ^ swap; + } + + adc->dma.first_chan = first_chan; + adc->dma.sample_chans = sample_chans; + adc->dma.period_bytes = RZT2H_ADC_DMA_PERIOD_SAMPLES * sample_chans * + RZT2H_ADC_CHANNEL_BYTES; +} + +static int rzt2h_adc_start_dma(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + struct dma_slave_config config; + unsigned int buffer_bytes; + dma_cookie_t cookie; + int ret; + + rzt2h_adc_dma_calc_layout(indio_dev); + + config = (struct dma_slave_config) { + .src_addr = adc->phys_base + RZT2H_ADDR_REG(adc->dma.first_chan), + .src_addr_width = adc->dma.sample_chans * RZT2H_ADC_CHANNEL_BYTES, + }; + + buffer_bytes = RZT2H_ADC_DMA_PERIODS * adc->dma.period_bytes; + + ret = dmaengine_slave_config(adc->dma.chan, &config); + if (ret) + return ret; + + desc = dmaengine_prep_dma_cyclic(adc->dma.chan, adc->dma.addr, + buffer_bytes, adc->dma.period_bytes, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = rzt2h_adc_dma_callback; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(adc->dma.chan); + return ret; + } + + adc->dma.thread = kthread_run(rzt2h_adc_dma_thread, indio_dev, + "rzt2h-adc-dma"); + if (IS_ERR(adc->dma.thread)) { + dmaengine_terminate_sync(adc->dma.chan); + return PTR_ERR(adc->dma.thread); + } + + disable_irq(adc->irq); + + dma_async_issue_pending(adc->dma.chan); + + return 0; +} + +static int rzt2h_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + struct device *dev = adc->dev; + unsigned int scan_index; + u16 val = 0; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + iio_for_each_active_channel(indio_dev, scan_index) { + chan = &indio_dev->channels[scan_index]; + val |= RZT2H_ADANSA0_CH_MASK(chan->channel); + rzt2h_adc_set_sst(adc, chan->channel, adc->sst[chan->channel]); + } + + writew(val, adc->base + RZT2H_ADANSA0_REG); + + adc->dma.period_index = 0; + atomic_set(&adc->dma.pending_periods, 0); + + ret = rzt2h_adc_start_dma(indio_dev); + if (ret) { + pm_runtime_put_autosuspend(dev); + return ret; + } + + rzt2h_adc_start(adc, RZT2H_ADCSR_ADCS_CONTINUOUS); + + return 0; +} + +static int rzt2h_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + rzt2h_adc_stop(adc); + + dmaengine_terminate_sync(adc->dma.chan); + + kthread_stop(adc->dma.thread); + + enable_irq(adc->irq); + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int rzt2h_adc_sst_to_freq(struct rzt2h_adc *adc, u8 sst) +{ + return adc->adclk_rate / (RZT2H_ADC_CONV_CYCLES_BASE + sst); +} + +static u8 rzt2h_adc_freq_to_sst(struct rzt2h_adc *adc, int freq) +{ + unsigned int cycles; + + cycles = DIV_ROUND_CLOSEST(adc->adclk_rate, freq); + cycles = clamp_val(cycles, + RZT2H_ADC_CONV_CYCLES_BASE + RZT2H_ADC_SST_MIN, + RZT2H_ADC_CONV_CYCLES_BASE + RZT2H_ADC_SST_MAX); + + return cycles - RZT2H_ADC_CONV_CYCLES_BASE; +} + static int rzt2h_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -156,19 +495,76 @@ static int rzt2h_adc_read_raw(struct iio_dev *indio_dev, struct rzt2h_adc *adc = iio_priv(indio_dev); switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + return rzt2h_adc_read_single(adc, chan->channel, val); + } case IIO_CHAN_INFO_SCALE: *val = 1800; *val2 = 12; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = rzt2h_adc_sst_to_freq(adc, adc->sst[chan->channel]); + return IIO_VAL_INT; default: return -EINVAL; } } +static int rzt2h_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + if (val <= 0) + return -EINVAL; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + adc->sst[chan->channel] = rzt2h_adc_freq_to_sst(adc, val); + + return 0; + } + default: + return -EINVAL; + } +} + +static int rzt2h_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = adc->samp_freq_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(adc->samp_freq_avail); + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_buffer_setup_ops rzt2h_adc_buffer_setup_ops = { + .postenable = rzt2h_adc_buffer_postenable, + .predisable = rzt2h_adc_buffer_predisable, +}; + static const struct iio_info rzt2h_adc_iio_info = { .read_raw = rzt2h_adc_read_raw, + .write_raw = rzt2h_adc_write_raw, + .read_avail = rzt2h_adc_read_avail, }; static irqreturn_t rzt2h_adc_isr(int irq, void *private) @@ -183,14 +579,21 @@ static irqreturn_t rzt2h_adc_isr(int irq, void *private) static const struct iio_chan_spec rzt2h_adc_chan_template = { .indexed = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .type = IIO_VOLTAGE, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_CPU, + }, }; static int rzt2h_adc_parse_properties(struct rzt2h_adc *adc) { struct iio_chan_spec *chan_array; - unsigned int i; int ret; ret = devm_iio_adc_device_alloc_chaninfo_se(adc->dev, @@ -203,19 +606,64 @@ static int rzt2h_adc_parse_properties(struct rzt2h_adc *adc) adc->num_channels = ret; adc->channels = chan_array; - for (i = 0; i < adc->num_channels; i++) - if (chan_array[i].channel + 1 > adc->max_channels) - adc->max_channels = chan_array[i].channel + 1; + for (unsigned int i = 0; i < adc->num_channels; i++) { + adc->sst[chan_array[i].channel] = RZT2H_ADC_SST_DEFAULT; + + chan_array[i].scan_index = i; + } return 0; } +static void rzt2h_adc_free_dma_buf(void *p) +{ + struct rzt2h_adc *adc = p; + + dma_free_noncoherent(adc->dev, RZT2H_ADC_DMA_BUFFER_SIZE, + adc->dma.buf, adc->dma.addr, DMA_FROM_DEVICE); +} + +static int rzt2h_adc_setup_dma(struct iio_dev *indio_dev) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + int ret; + + adc->dma.chan = devm_dma_request_chan(dev, "rx"); + if (IS_ERR(adc->dma.chan)) { + ret = PTR_ERR(adc->dma.chan); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, "DMA channel request failed\n"); + + adc->dma.chan = NULL; + return 0; + } + + adc->dma.buf = dma_alloc_noncoherent(dev, RZT2H_ADC_DMA_BUFFER_SIZE, + &adc->dma.addr, DMA_FROM_DEVICE, + GFP_KERNEL); + if (!adc->dma.buf) + return -ENOMEM; + + dma_sync_single_for_device(dev, adc->dma.addr, RZT2H_ADC_DMA_BUFFER_SIZE, + DMA_FROM_DEVICE); + + ret = devm_add_action_or_reset(dev, rzt2h_adc_free_dma_buf, adc); + if (ret) + return ret; + + return devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &rzt2h_adc_buffer_setup_ops, NULL); +} + static int rzt2h_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct rzt2h_adc *adc; - int ret, irq; + struct resource *res; + struct clk *adclk; + int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); if (!indio_dev) @@ -224,6 +672,7 @@ static int rzt2h_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); adc->dev = dev; init_completion(&adc->completion); + init_waitqueue_head(&adc->dma.wq); ret = devm_mutex_init(dev, &adc->lock); if (ret) @@ -235,21 +684,35 @@ static int rzt2h_adc_probe(struct platform_device *pdev) if (ret) return ret; - adc->base = devm_platform_ioremap_resource(pdev, 0); + adc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(adc->base)) return PTR_ERR(adc->base); + adc->phys_base = res->start; + + adclk = devm_clk_get(dev, "adclk"); + if (IS_ERR(adclk)) + return dev_err_probe(dev, PTR_ERR(adclk), "failed to get adclk\n"); + + adc->adclk_rate = clk_get_rate(adclk); + if (!adc->adclk_rate) + return dev_err_probe(dev, -EINVAL, "invalid adclk rate\n"); + + adc->samp_freq_avail[0] = rzt2h_adc_sst_to_freq(adc, RZT2H_ADC_SST_MAX); + adc->samp_freq_avail[1] = 1; + adc->samp_freq_avail[2] = rzt2h_adc_sst_to_freq(adc, RZT2H_ADC_SST_MIN); + pm_runtime_set_autosuspend_delay(dev, 300); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); if (ret) return ret; - irq = platform_get_irq_byname(pdev, "adi"); - if (irq < 0) - return irq; + adc->irq = platform_get_irq_byname(pdev, "adi"); + if (adc->irq < 0) + return adc->irq; - ret = devm_request_irq(dev, irq, rzt2h_adc_isr, 0, dev_name(dev), adc); + ret = devm_request_irq(dev, adc->irq, rzt2h_adc_isr, 0, dev_name(dev), adc); if (ret) return ret; @@ -259,6 +722,10 @@ static int rzt2h_adc_probe(struct platform_device *pdev) indio_dev->channels = adc->channels; indio_dev->num_channels = adc->num_channels; + ret = rzt2h_adc_setup_dma(indio_dev); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/adc/sophgo-cv1800b-adc.c b/drivers/iio/adc/sophgo-cv1800b-adc.c index bdc3e1326a9a..c6eed09acfcd 100644 --- a/drivers/iio/adc/sophgo-cv1800b-adc.c +++ b/drivers/iio/adc/sophgo-cv1800b-adc.c @@ -182,6 +182,8 @@ static int cv1800b_adc_probe(struct platform_device *pdev) return PTR_ERR(saradc->regs); saradc->irq = platform_get_irq_optional(pdev, 0); + if (saradc->irq < 0 && saradc->irq != -ENXIO) + return saradc->irq; if (saradc->irq > 0) { init_completion(&saradc->completion); ret = devm_request_irq(dev, saradc->irq, diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5c6c06b269be..3cde89c06ca3 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2456,6 +2456,7 @@ 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 device *dma_dev; int ret; adc->dma_chan = dma_request_chan(dev, "rx"); @@ -2470,7 +2471,8 @@ static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) return 0; } - adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + dma_dev = dmaengine_get_dma_device(adc->dma_chan); + adc->rx_buf = dma_alloc_coherent(dma_dev, STM32_DMA_BUFFER_SIZE, &adc->rx_dma_buf, GFP_KERNEL); if (!adc->rx_buf) { @@ -2490,7 +2492,7 @@ static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) return 0; err_free: - dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE, + dma_free_coherent(dma_dev, STM32_DMA_BUFFER_SIZE, adc->rx_buf, adc->rx_dma_buf); err_release: dma_release_channel(adc->dma_chan); @@ -2617,7 +2619,7 @@ static int stm32_adc_probe(struct platform_device *pdev) err_dma_disable: if (adc->dma_chan) { - dma_free_coherent(adc->dma_chan->device->dev, + dma_free_coherent(dmaengine_get_dma_device(adc->dma_chan), STM32_DMA_BUFFER_SIZE, adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); @@ -2640,7 +2642,7 @@ static void stm32_adc_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); iio_triggered_buffer_cleanup(indio_dev); if (adc->dma_chan) { - dma_free_coherent(adc->dma_chan->device->dev, + dma_free_coherent(dmaengine_get_dma_device(adc->dma_chan), STM32_DMA_BUFFER_SIZE, adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); @@ -2725,7 +2727,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .set_ovs = stm32h7_adc_set_ovs, }; -static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 0, 0, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp1_adc_cfg = { @@ -2747,7 +2749,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .set_ovs = stm32h7_adc_set_ovs, }; -static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp13[] = { 1000, 1000, 1000, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp13_adc_cfg = { diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 00f05e167afc..674767da3ac3 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1484,7 +1484,7 @@ static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev) struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); if (adc->dma_chan) { - dma_free_coherent(adc->dma_chan->device->dev, + dma_free_coherent(dmaengine_get_dma_device(adc->dma_chan), DFSDM_DMA_BUFFER_SIZE, adc->rx_buf, adc->dma_buf); dma_release_channel(adc->dma_chan); @@ -1504,7 +1504,7 @@ static int stm32_dfsdm_dma_request(struct device *dev, return ret; } - adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + adc->rx_buf = dma_alloc_coherent(dmaengine_get_dma_device(adc->dma_chan), DFSDM_DMA_BUFFER_SIZE, &adc->dma_buf, GFP_KERNEL); if (!adc->rx_buf) { diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 1899813c1aee..f9a995ff403e 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -195,12 +195,14 @@ static int adc128_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; - indio_dev->name = spi_get_device_id(spi)->name; + config = spi_get_device_match_data(spi); + if (!config) + return -ENODATA; + + indio_dev->name = spi->modalias; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; - config = spi_get_device_match_data(spi); - indio_dev->channels = config->channels; indio_dev->num_channels = config->num_channels; diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 0fbfa4e499aa..0f9244107c6d 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -233,6 +233,7 @@ static const struct iio_event_spec ads1015_events[] = { struct ads1015_channel_data { unsigned int pga; unsigned int data_rate; + const char *label; }; struct ads1015_thresh_data { @@ -586,6 +587,15 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, } } +static int ads1015_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + return sysfs_emit(label, "%s\n", + data->channel_data[chan->address].label); +} + static int ads1015_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -843,6 +853,7 @@ static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { static const struct iio_info ads1015_info = { .read_avail = ads1015_read_avail, .read_raw = ads1015_read_raw, + .read_label = ads1015_read_label, .write_raw = ads1015_write_raw, .read_event_value = ads1015_read_event, .write_event_value = ads1015_write_event, @@ -853,6 +864,7 @@ static const struct iio_info ads1015_info = { static const struct iio_info tla2024_info = { .read_avail = ads1015_read_avail, .read_raw = ads1015_read_raw, + .read_label = ads1015_read_label, .write_raw = ads1015_write_raw, }; @@ -861,7 +873,9 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ads1015_data *data = iio_priv(indio_dev); struct device *dev = &client->dev; + const char *label; int i = -1; + int ret; device_for_each_child_node_scoped(dev, node) { u32 pval; @@ -897,6 +911,15 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) } } + if (fwnode_property_present(node, "label")) { + ret = fwnode_property_read_string(node, "label", &label); + if (ret) { + dev_err(dev, "invalid label on %pfw\n", node); + return ret; + } + data->channel_data[channel].label = label; + } + data->channel_data[channel].pga = pga; data->channel_data[channel].data_rate = data_rate; @@ -985,6 +1008,13 @@ static int ads1015_probe(struct i2c_client *client) /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ ads1015_get_channels_config(client); + /* Channels without a label property fall back to the datasheet name */ + for (i = 0; i < ADS1015_CHANNELS; i++) { + if (!data->channel_data[i].label) + data->channel_data[i].label = + indio_dev->channels[i].datasheet_name; + } + data->regmap = devm_regmap_init_i2c(client, chip->has_comparator ? &ads1015_regmap_config : &tla2024_regmap_config); diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index d9ec4a8d7ce5..8f2d03eee77b 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -624,12 +624,16 @@ static int ads1018_trigger_setup(struct iio_dev *indio_dev) static int ads1018_spi_probe(struct spi_device *spi) { - const struct ads1018_chip_info *info = spi_get_device_match_data(spi); + const struct ads1018_chip_info *info; struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ads1018 *ads1018; int ret; + info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; + indio_dev = devm_iio_device_alloc(dev, sizeof(*ads1018)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c index 9fe8d54cce83..9957c4d813fb 100644 --- a/drivers/iio/adc/ti-ads1100.c +++ b/drivers/iio/adc/ti-ads1100.c @@ -5,7 +5,7 @@ * Copyright (c) 2023, Topic Embedded Products * * Datasheet: https://www.ti.com/lit/gpn/ads1100 - * IIO driver for ADS1100 and ADS1000 ADC 16-bit I2C + * IIO driver for ADS1100 and similar single channel ADC 16-bit I2C */ #include @@ -15,10 +15,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -39,17 +41,44 @@ #define ADS1100_SINGLESHOT ADS1100_CFG_SC #define ADS1100_SLEEP_DELAY_MS 2000 +#define ADS1110_INTERNAL_REF_mV 2048 static const int ads1100_data_rate[] = { 128, 32, 16, 8 }; +static const int ads1110_data_rate[] = { 240, 60, 30, 15 }; static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 }; +/* Timeout based on the minimum sample rate of 8 SPS (7500ms) */ +#define ADS1100_MAX_DRDY_TIMEOUT_US (7500 * USEC_PER_MSEC) + +struct ads1100_config { + const char *name; + const int *available_data_rate_Hz; + const int data_rate_count; + bool has_internal_vref_only; +}; + +static const struct ads1100_config ads1100_config = { + .name = "ads1100", + .available_data_rate_Hz = ads1100_data_rate, + .data_rate_count = ARRAY_SIZE(ads1100_data_rate), + .has_internal_vref_only = false, +}; + +static const struct ads1100_config ads1110_config = { + .name = "ads1110", + .available_data_rate_Hz = ads1110_data_rate, + .data_rate_count = ARRAY_SIZE(ads1110_data_rate), + .has_internal_vref_only = true, +}; + struct ads1100_data { struct i2c_client *client; struct regulator *reg_vdd; struct mutex lock; int scale_avail[2 * 4]; /* 4 gain settings */ + const struct ads1100_config *chip_info; u8 config; - bool supports_data_rate; /* Only the ADS1100 can select the rate */ + bool supports_data_rate; }; static const struct iio_chan_spec ads1100_channel = { @@ -85,6 +114,20 @@ static int ads1100_set_config_bits(struct ads1100_data *data, u8 mask, u8 value) return 0; }; +static int ads1100_get_vref_millivolts(struct ads1100_data *data) +{ + int voltage_uV; + + if (data->chip_info->has_internal_vref_only) + return ADS1110_INTERNAL_REF_mV; + + voltage_uV = regulator_get_voltage(data->reg_vdd); + if (voltage_uV < 0) + return voltage_uV; + + return voltage_uV / (MICRO / MILLI); +} + static int ads1100_data_bits(struct ads1100_data *data) { return ads1100_data_rate_bits[FIELD_GET(ADS1100_DR_MASK, data->config)]; @@ -123,10 +166,86 @@ static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val) return 0; } +static int ads1100_conversion_busy(struct ads1100_data *data) +{ + u8 buffer[3]; + int ret; + + ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer)); + if (ret < 0) { + dev_err(&data->client->dev, "I2C read fail: %d\n", ret); + return ret; + } + + return FIELD_GET(ADS1100_CFG_ST_BSY, buffer[2]); +} + +static int ads1100_wait_single_conversion(struct ads1100_data *data) +{ + int data_rate_index = FIELD_GET(ADS1100_DR_MASK, data->config); + int data_rate_Hz = data->chip_info->available_data_rate_Hz[data_rate_index]; + unsigned long poll_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, data_rate_Hz) / 4; + int busy; + int ret; + + ret = readx_poll_timeout(ads1100_conversion_busy, data, + busy, busy <= 0, + poll_us, ADS1100_MAX_DRDY_TIMEOUT_US); + if (busy < 0) + return busy; + + return ret; +} + +static int ads1100_start_single_conversion(struct ads1100_data *data) +{ + u8 config = data->config | ADS1100_CFG_SC; + int ret; + + ret = i2c_master_send(data->client, &config, sizeof(config)); + if (ret < 0) { + dev_err(&data->client->dev, "I2C write fail: %d\n", ret); + return ret; + } + /* Need to wait because of change from continuous to single mode */ + ret = ads1100_wait_single_conversion(data); + if (ret) + return ret; + + config |= ADS1100_CFG_ST_BSY; + + ret = i2c_master_send(data->client, &config, sizeof(config)); + if (ret < 0) { + dev_err(&data->client->dev, "I2C write fail: %d\n", ret); + return ret; + } + + /* No need to cache it, it's status bit */ + data->config = config & ~ADS1100_CFG_ST_BSY; + + return 0; +} + +static int ads1100_poll_data_ready(struct ads1100_data *data) +{ + int ret; + + ret = ads1100_start_single_conversion(data); + if (ret) + return ret; + + ret = ads1100_wait_single_conversion(data); + if (ret) + return ret; + + return ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_CONTINUOUS); +} + static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) { int microvolts; int gain; + int ret; /* With Vdd between 2.7 and 5V, the scale is always below 1 */ if (val) @@ -135,7 +254,12 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) if (!val2) return -EINVAL; - microvolts = regulator_get_voltage(data->reg_vdd); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + microvolts = ads1100_get_vref_millivolts(data) * (MICRO / MILLI); /* * val2 is in 'micro' units, n = val2 / 1000000 * result must be millivolts, d = microvolts / 1000 @@ -149,32 +273,40 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1); - return 0; + return ads1100_poll_data_ready(data); } static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate) { unsigned int i; unsigned int size; + int ret; - size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1; + size = data->supports_data_rate ? data->chip_info->data_rate_count : 1; for (i = 0; i < size; i++) { - if (ads1100_data_rate[i] == rate) - return ads1100_set_config_bits(data, ADS1100_DR_MASK, - FIELD_PREP(ADS1100_DR_MASK, i)); + if (data->chip_info->available_data_rate_Hz[i] == rate) + break; } - return -EINVAL; -} + if (i == size) + return -EINVAL; -static int ads1100_get_vdd_millivolts(struct ads1100_data *data) -{ - return regulator_get_voltage(data->reg_vdd) / (MICRO / MILLI); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = ads1100_set_config_bits(data, ADS1100_DR_MASK, + FIELD_PREP(ADS1100_DR_MASK, i)); + if (ret) + return ret; + + return ads1100_poll_data_ready(data); } static void ads1100_calc_scale_avail(struct ads1100_data *data) { - int millivolts = ads1100_get_vdd_millivolts(data); + int millivolts = ads1100_get_vref_millivolts(data); unsigned int i; for (i = 0; i < ARRAY_SIZE(data->scale_avail) / 2; i++) { @@ -196,9 +328,9 @@ static int ads1100_read_avail(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: *type = IIO_VAL_INT; - *vals = ads1100_data_rate; + *vals = data->chip_info->available_data_rate_Hz; if (data->supports_data_rate) - *length = ARRAY_SIZE(ads1100_data_rate); + *length = data->chip_info->data_rate_count; else *length = 1; return IIO_AVAIL_LIST; @@ -218,6 +350,7 @@ static int ads1100_read_raw(struct iio_dev *indio_dev, { int ret; struct ads1100_data *data = iio_priv(indio_dev); + int data_rate_index; guard(mutex)(&data->lock); switch (mask) { @@ -233,12 +366,12 @@ static int ads1100_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* full-scale is the supply voltage in millivolts */ - *val = ads1100_get_vdd_millivolts(data); + *val = ads1100_get_vref_millivolts(data); *val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config); return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: - *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK, - data->config)]; + data_rate_index = FIELD_GET(ADS1100_DR_MASK, data->config); + *val = data->chip_info->available_data_rate_Hz[data_rate_index]; return IIO_VAL_INT; default: return -EINVAL; @@ -318,7 +451,12 @@ static int ads1100_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); - indio_dev->name = "ads1100"; + data->chip_info = i2c_get_match_data(client); + if (!data->chip_info) + return dev_err_probe(dev, -ENODATA, + "Can't get device data from firmware\n"); + + indio_dev->name = data->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = &ads1100_channel; indio_dev->num_channels = 1; @@ -400,16 +538,18 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops, NULL); static const struct i2c_device_id ads1100_id[] = { - { .name = "ads1100" }, - { .name = "ads1000" }, + { .name = "ads1000", .driver_data = (kernel_ulong_t)&ads1100_config }, + { .name = "ads1100", .driver_data = (kernel_ulong_t)&ads1100_config }, + { .name = "ads1110", .driver_data = (kernel_ulong_t)&ads1110_config }, { } }; MODULE_DEVICE_TABLE(i2c, ads1100_id); static const struct of_device_id ads1100_of_match[] = { - {.compatible = "ti,ads1100" }, - {.compatible = "ti,ads1000" }, + { .compatible = "ti,ads1000", .data = &ads1100_config }, + { .compatible = "ti,ads1100", .data = &ads1100_config }, + { .compatible = "ti,ads1110", .data = &ads1110_config }, { } }; diff --git a/drivers/iio/adc/ti-ads112c04.c b/drivers/iio/adc/ti-ads112c04.c new file mode 100644 index 000000000000..acf7209db761 --- /dev/null +++ b/drivers/iio/adc/ti-ads112c04.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Texas Instruments ADS112C04 16-bit I2C ADC driver + * + * Copyright (c) 2026 Kyle Hsieh + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads112c04.pdf + * Based on TI Reference Code and standard Linux IIO framework. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADS112C04_CMD_RESET 0x06 +#define ADS112C04_CMD_START_SYNC 0x08 +#define ADS112C04_CMD_RDATA 0x10 +#define ADS112C04_CMD_RREG(reg) (0x20 | ((reg) << 2)) +#define ADS112C04_CMD_WREG(reg) (0x40 | ((reg) << 2)) + +#define ADS112C04_REG_CONFIG0 0x00 +#define ADS112C04_CONF0_MUX GENMASK(7, 4) +#define ADS112C04_CONF0_MUX_AIN0_AIN1 0 +#define ADS112C04_CONF0_MUX_AIN_SINGLE_BASE 8 +#define ADS112C04_CONF0_GAIN GENMASK(3, 1) +#define ADS112C04_CONF0_GAIN_X1 0 +#define ADS112C04_CONF0_PGA_BYPASS BIT(0) + +#define ADS112C04_REG_CONFIG1 0x01 +#define ADS112C04_CONF1_DR GENMASK(7, 5) +#define ADS112C04_CONF1_DR_20SPS 0 +#define ADS112C04_CONF1_MODE BIT(4) +#define ADS112C04_CONF1_MODE_NORMAL 0 +#define ADS112C04_CONF1_CM BIT(3) +#define ADS112C04_CONF1_CM_SINGLE_SHOT 0 +#define ADS112C04_CONF1_VREF GENMASK(2, 1) +#define ADS112C04_CONF1_VREF_INTERNAL 0 +#define ADS112C04_CONF1_VREF_EXTERNAL 1 +#define ADS112C04_CONF1_VREF_AVDD 2 +#define ADS112C04_CONF1_TS BIT(0) +#define ADS112C04_CONF1_TS_DISABLED 0 + +#define ADS112C04_REG_CONFIG2 0x02 +#define ADS112C04_CONF2_DRDY BIT(7) + +#define ADS112C04_INT_REF_mV 2048 + +#define ADS112C04_MAX_CHANNELS 12 + +#define ADS112C04_RESOLUTION_BITS 16 + +enum { + ADS112C04_VREF_SOURCE_INTERNAL, + ADS112C04_VREF_SOURCE_EXTERNAL, + ADS112C04_VREF_SOURCE_AVDD, +}; + +static const char * const ads112c04_vref_names[] = { + [ADS112C04_VREF_SOURCE_INTERNAL] = "internal", + [ADS112C04_VREF_SOURCE_EXTERNAL] = "external", + [ADS112C04_VREF_SOURCE_AVDD] = "avdd", +}; + +static const u8 ads112c04_vref_reg_val[] = { + [ADS112C04_VREF_SOURCE_INTERNAL] = ADS112C04_CONF1_VREF_INTERNAL, + [ADS112C04_VREF_SOURCE_EXTERNAL] = ADS112C04_CONF1_VREF_EXTERNAL, + [ADS112C04_VREF_SOURCE_AVDD] = ADS112C04_CONF1_VREF_AVDD, +}; + +/* Indexed by [AINP][AINN], -1 means the combination is not available. */ +static const s8 ads112c04_diff_mux[4][4] = { + { -1, 0, 1, 2 }, + { 3, -1, 4, 5 }, + { -1, -1, -1, 6 }, + { -1, -1, 7, -1 }, +}; + +struct ads112c04_state { + struct i2c_client *client; + /* Protects concurrent ADC reads and device configuration */ + struct mutex lock; + struct completion completion; + u32 avdd_mV; + u32 ext_ref_mV; + u8 *vref_source; /* one entry per channel, indexed by scan order */ + u8 config0; + u8 config1; +}; + +static int ads112c04_write_cmd(struct i2c_client *client, u8 cmd) +{ + return i2c_smbus_write_byte(client, cmd); +} + +static int ads112c04_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, ADS112C04_CMD_RREG(reg)); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int ads112c04_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, ADS112C04_CMD_WREG(reg), val); +} + +static int ads112c04_wait_for_data(struct ads112c04_state *st) +{ + int ret, err; + u8 val; + + if (st->client->irq > 0) { + /* Timeout is 100ms (slowest data rate is 20 SPS) */ + if (!wait_for_completion_timeout(&st->completion, msecs_to_jiffies(100))) + return -ETIMEDOUT; + + return 0; + } + + ret = read_poll_timeout(ads112c04_read_reg, err, + err < 0 || (val & ADS112C04_CONF2_DRDY), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC, false, + st->client, ADS112C04_REG_CONFIG2, &val); + if (err < 0) + return err; + + return ret; +} + +static int ads112c04_read_data(struct ads112c04_state *st, int *val) +{ + int ret; + + ret = i2c_smbus_read_word_swapped(st->client, ADS112C04_CMD_RDATA); + if (ret < 0) + return ret; + + *val = sign_extend32(ret, ADS112C04_RESOLUTION_BITS - 1); + + return 0; +} + +static int ads112c04_get_adc_result(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ads112c04_state *st = iio_priv(indio_dev); + unsigned int idx = chan - indio_dev->channels; + u8 new_config0, new_config1; + int ret; + + new_config0 = st->config0; + FIELD_MODIFY(ADS112C04_CONF0_MUX, &new_config0, chan->address); + + if (st->config0 != new_config0) { + ret = ads112c04_write_reg(st->client, ADS112C04_REG_CONFIG0, + new_config0); + if (ret < 0) + return ret; + + st->config0 = new_config0; + } + + new_config1 = st->config1; + FIELD_MODIFY(ADS112C04_CONF1_VREF, &new_config1, + ads112c04_vref_reg_val[st->vref_source[idx]]); + + if (st->config1 != new_config1) { + ret = ads112c04_write_reg(st->client, ADS112C04_REG_CONFIG1, + new_config1); + if (ret < 0) + return ret; + + st->config1 = new_config1; + } + + reinit_completion(&st->completion); + + ret = ads112c04_write_cmd(st->client, ADS112C04_CMD_START_SYNC); + if (ret < 0) + return ret; + + ret = ads112c04_wait_for_data(st); + if (ret < 0) + return ret; + + return ads112c04_read_data(st, val); +} + +static int ads112c04_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ads112c04_state *st = iio_priv(indio_dev); + unsigned int idx = chan - indio_dev->channels; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + ret = ads112c04_get_adc_result(indio_dev, chan, val); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (st->vref_source[idx]) { + case ADS112C04_VREF_SOURCE_EXTERNAL: + *val = st->ext_ref_mV; + break; + case ADS112C04_VREF_SOURCE_AVDD: + *val = st->avdd_mV; + break; + default: + *val = ADS112C04_INT_REF_mV; + break; + } + *val2 = ADS112C04_RESOLUTION_BITS - 1; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static irqreturn_t ads112c04_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ads112c04_state *st = iio_priv(indio_dev); + + complete(&st->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info ads112c04_info = { + .read_raw = ads112c04_read_raw, +}; + +static int ads112c04_parse_vref_source(struct fwnode_handle *child) +{ + if (!fwnode_property_present(child, "reference-sources")) + return ADS112C04_VREF_SOURCE_INTERNAL; + + return fwnode_property_match_property_string(child, "reference-sources", + ads112c04_vref_names, + ARRAY_SIZE(ads112c04_vref_names)); +} + +static int ads112c04_parse_channels(struct iio_dev *indio_dev, + bool *need_avdd_ref, bool *need_ext_ref) +{ + struct device *dev = indio_dev->dev.parent; + struct ads112c04_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels; + u32 num_channels, pair[2], channel; + unsigned int i; + int ret; + + num_channels = device_get_named_child_node_count(dev, "channel"); + if (!num_channels) + return dev_err_probe(dev, -EINVAL, "no channel subnodes found\n"); + + if (num_channels > ADS112C04_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "num of channel nodes exceeds %d\n", + ADS112C04_MAX_CHANNELS); + + channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + st->vref_source = devm_kcalloc(dev, num_channels, + sizeof(*st->vref_source), GFP_KERNEL); + if (!st->vref_source) + return -ENOMEM; + + i = 0; + device_for_each_named_child_node_scoped(dev, child, "channel") { + const char *s_chan = "single-channel", *d_chan = "diff-channels"; + struct iio_chan_spec *spec = &channels[i]; + + if (fwnode_property_present(child, "excitation-channels")) + return dev_err_probe(dev, -EOPNOTSUPP, + "excitation-channels is not supported yet\n"); + + ret = ads112c04_parse_vref_source(child); + if (ret < 0) + return dev_err_probe(dev, ret, + "invalid reference-sources value\n"); + + st->vref_source[i] = ret; + + *need_ext_ref |= st->vref_source[i] == ADS112C04_VREF_SOURCE_EXTERNAL; + *need_avdd_ref |= st->vref_source[i] == ADS112C04_VREF_SOURCE_AVDD; + + /* + * REVISIT: when ti,refp-refn-resistor-ohms is implemented, a + * channel using an external resistor reference is effectively + * a resistance measurement and should use IIO_RESISTANCE. + */ + spec->type = IIO_VOLTAGE; + spec->indexed = 1; + spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); + + if (fwnode_property_present(child, s_chan)) { + ret = fwnode_property_read_u32(child, s_chan, &channel); + if (ret) + return dev_err_probe(dev, ret, + "failed to read %s property\n", s_chan); + + if (channel > 3) + return dev_err_probe(dev, -EINVAL, "%s must be 0-3\n", s_chan); + + spec->channel = channel; + spec->address = ADS112C04_CONF0_MUX_AIN_SINGLE_BASE + channel; + } else if (fwnode_property_present(child, d_chan)) { + ret = fwnode_property_read_u32_array(child, d_chan, pair, ARRAY_SIZE(pair)); + if (ret) + return dev_err_probe(dev, ret, + "failed to read %s property\n", d_chan); + + if (pair[0] > 3 || pair[1] > 3) + return dev_err_probe(dev, -EINVAL, "%s must be 0-3\n", d_chan); + + spec->channel = pair[0]; + spec->channel2 = pair[1]; + spec->differential = 1; + + if (ads112c04_diff_mux[pair[0]][pair[1]] < 0) + return dev_err_probe(dev, -EINVAL, + "invalid %s combination\n", d_chan); + + spec->address = ads112c04_diff_mux[pair[0]][pair[1]]; + } else { + return dev_err_probe(dev, -EINVAL, + "channel node must have %s or %s\n", s_chan, d_chan); + } + + i++; + } + + indio_dev->channels = channels; + indio_dev->num_channels = i; + + return 0; +} + +static int ads112c04_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct ads112c04_state *st; + struct reset_control *reset; + bool need_avdd_ref = false, need_ext_ref = false; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->client = client; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + init_completion(&st->completion); + + indio_dev->name = "ads112c04"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ads112c04_info; + + /* Forward compatibility checks for unimplemented DT properties */ + if (device_property_present(dev, "refn-supply") || + device_property_present(dev, "ti,refp-refn-resistor-ohms")) + return dev_err_probe(dev, -EOPNOTSUPP, + "refn-supply and external resistors are not supported yet\n"); + + ret = ads112c04_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref); + if (ret) + return ret; + + if (need_avdd_ref) { + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get avdd voltage\n"); + + st->avdd_mV = ret / (MICRO / MILLI); + } else { + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + } + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); + + if (device_property_present(dev, "refp-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refp"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refp voltage\n"); + + st->ext_ref_mV = ret / (MICRO / MILLI); + } + + if (need_ext_ref && !st->ext_ref_mV) + return dev_err_probe(dev, -EINVAL, + "external reference measurements require refp-supply\n"); + + /* Datasheet: POR releases ~500us after supplies are stable */ + fsleep(500); + + reset = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), "failed to get reset\n"); + + if (reset) { + /* Datasheet: tw(RSL), the RESET low pulse, is 250ns minimum */ + fsleep(1); + + ret = reset_control_deassert(reset); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset\n"); + } else { + ret = ads112c04_write_cmd(client, ADS112C04_CMD_RESET); + if (ret < 0) + return ret; + } + + /* Datasheet: td(RSSTA) is 100ns minimum after the RESET rising edge */ + fsleep(1); + + /* + * Initialize CONFIG0 with all fields explicit: gain of 1 with the PGA + * bypassed, which allows full-scale single-ended measurements. The MUX + * field is updated per channel before each conversion. + */ + st->config0 = FIELD_PREP(ADS112C04_CONF0_MUX, + ADS112C04_CONF0_MUX_AIN0_AIN1) | + FIELD_PREP(ADS112C04_CONF0_GAIN, + ADS112C04_CONF0_GAIN_X1) | + ADS112C04_CONF0_PGA_BYPASS; + + ret = ads112c04_write_reg(client, ADS112C04_REG_CONFIG0, st->config0); + if (ret) + return ret; + + st->config1 = FIELD_PREP(ADS112C04_CONF1_DR, + ADS112C04_CONF1_DR_20SPS) | + FIELD_PREP(ADS112C04_CONF1_MODE, + ADS112C04_CONF1_MODE_NORMAL) | + FIELD_PREP(ADS112C04_CONF1_CM, + ADS112C04_CONF1_CM_SINGLE_SHOT) | + FIELD_PREP(ADS112C04_CONF1_VREF, + ADS112C04_CONF1_VREF_INTERNAL) | + FIELD_PREP(ADS112C04_CONF1_TS, + ADS112C04_CONF1_TS_DISABLED); + + ret = ads112c04_write_reg(client, ADS112C04_REG_CONFIG1, st->config1); + if (ret) + return ret; + + if (client->irq > 0) { + ret = devm_request_irq(dev, client->irq, ads112c04_irq_handler, 0, + indio_dev->name, indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id ads112c04_id[] = { + { .name = "ads112c04" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads112c04_id); + +static const struct of_device_id ads112c04_of_match[] = { + { .compatible = "ti,ads112c04" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads112c04_of_match); + +static struct i2c_driver ads112c04_driver = { + .driver = { + .name = "ads112c04", + .of_match_table = ads112c04_of_match, + }, + .probe = ads112c04_probe, + .id_table = ads112c04_id, +}; +module_i2c_driver(ads112c04_driver); + +MODULE_AUTHOR("Kyle Hsieh "); +MODULE_DESCRIPTION("Texas Instruments ADS112C04 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index 60eab4852ba2..3c877126b0be 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -9,7 +9,10 @@ */ #include +#include #include +#include +#include #include #include #include @@ -17,8 +20,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -72,7 +77,14 @@ #define ADS112C14_DEVICE_CFG_PWDN BIT(7) #define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6) #define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4) +#define ADS112C14_DEVICE_CFG_BOCS_DISABLED 0 +#define ADS112C14_DEVICE_CFG_BOCS_200_nA 1 +#define ADS112C14_DEVICE_CFG_BOCS_1_uA 2 +#define ADS112C14_DEVICE_CFG_BOCS_10_uA 3 + #define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3) +#define ADS112C14_DEVICE_CFG_CLK_SEL_INTERNAL 0 +#define ADS112C14_DEVICE_CFG_CLK_SEL_EXTERNAL 1 #define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2) #define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0 #define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1 @@ -82,6 +94,14 @@ #define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4) #define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3) #define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0) +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_16 0 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_32 1 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_128 2 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_256 3 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_512 4 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024 5 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS 6 +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS 7 #define ADS112C14_REG_MUX_CFG 0x07 #define ADS112C14_MUX_CFG_AINP GENMASK(7, 4) @@ -117,9 +137,15 @@ #define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4) #define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2) #define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0) +#define ADS112C14_GPIO_CFG_GPIO_CFG_DISABLED 0 +#define ADS112C14_GPIO_CFG_GPIO_CFG_INPUT 1 +#define ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL 2 +#define ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_OPEN_DRAIN 3 #define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C #define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DAT_OUT 0 +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DRDY 1 #define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6) #define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3) #define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2) @@ -163,6 +189,63 @@ static const u32 ads112c14_pga_gains_x10[] = { 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */ }; +#define ADS112C14_INTERNAL_CLK_Hz (4096 * HZ_PER_KHZ) + +/* Index corresponds to first 2 ADS112C14_DATA_RATE_CFG_FLTR_OSR values. */ +static const int ads112c14_sinc4_osr_available[] = { + 16, 32 +}; + +/* Index corresponds to next 4 ADS112C14_DATA_RATE_CFG_FLTR_OSR values. */ +static const int ads112c14_sinc4_sinc1_osr_available[] = { + 128, 256, 512, 1024 +}; + +/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */ +static const int ads112c14_sinc4_sinc1_pf1_20sps_osr_available[] = { + 1600, 12800, 25600, 51200 +}; + +/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */ +static const int ads112c14_sinc4_sinc1_pf1_25sps_osr_available[] = { + 1280, 10240, 20480, 40960 +}; + +/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */ +static const int ads112c14_fmod_div[] = { + 128, 16, 8, 4 +}; + +#define ADS112C14_DELAY_MAX FIELD_MAX(ADS112C14_DATA_RATE_CFG_DELAY) + +/* Table 7-6 latency in t_MOD for OSR [16, 32, 128, 256, 512, 1024]. */ +static const int ads112c14_sinc_latency_tmod[][ARRAY_SIZE(ads112c14_fmod_div)] = { + { 80, 88, 88, 104 }, + { 144, 152, 152, 168 }, + { 240, 248, 248, 264 }, + { 368, 376, 376, 392 }, + { 624, 632, 632, 648 }, + { 1136, 1144, 1144, 1160 }, +}; + +/* Table 7-7 latency in t_MOD for output data rates [25SPS, 20SPS]. */ +static const int ads112c14_fir_latency_tmod[][ARRAY_SIZE(ads112c14_fmod_div)] = { + { 1416, 10384, 20624, 41120 }, + { 1736, 12944, 25744, 51360 }, +}; + +enum ads112c14_filter_type { + ADS112C14_FILTER_TYPE_SINC4, + ADS112C14_FILTER_TYPE_SINC4_SINC1, + ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1, +}; + +static const char * const ads112c14_filter_type_names[] = { + [ADS112C14_FILTER_TYPE_SINC4] = "sinc4", + [ADS112C14_FILTER_TYPE_SINC4_SINC1] = "sinc4+sinc1", + [ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1] = "sinc4+sinc1+pf1", +}; + #define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07 DECLARE_CRC8_TABLE(ads112c14_crc8_table); @@ -184,6 +267,8 @@ enum { ADS112C14_SYS_MON_CHANNEL_SHORT, }; +static const struct iio_chan_spec_ext_info ads112c14_ext_info[]; + static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { { .type = IIO_TEMP, @@ -192,7 +277,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .address = 2, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) - | BIT(IIO_CHAN_INFO_OFFSET), + | BIT(IIO_CHAN_INFO_OFFSET) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ads112c14_ext_info, }, { .type = IIO_VOLTAGE, @@ -200,7 +290,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF, .address = 3, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_SCALE), + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ads112c14_ext_info, }, { .type = IIO_VOLTAGE, @@ -208,7 +303,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .channel = ADS112C14_SYS_MON_CHANNEL_AVDD, .address = 4, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_SCALE), + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ads112c14_ext_info, }, { .type = IIO_VOLTAGE, @@ -216,7 +316,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .channel = ADS112C14_SYS_MON_CHANNEL_DVDD, .address = 5, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_SCALE), + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ads112c14_ext_info, }, { .type = IIO_VOLTAGE, @@ -226,8 +331,13 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .differential = 1, .address = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_SCALE), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_SAMP_FREQ) + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ads112c14_ext_info, }, }; @@ -241,16 +351,28 @@ struct ads112c14_measurement { u8 idac2_mux; u8 iadc_count; u8 gain_val; + u8 burnout; bool global_chop; bool bipolar; int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; }; +struct ads112c14_channel_state { + u8 speed_mode; + u8 filter_osr; + u8 delay; +}; + struct ads112c14_data { const struct ads112c14_chip_info *chip_info; struct regmap *regmap; + struct iio_trigger *drdy_trig; /* Synchronizes access to register value fields. */ struct mutex lock; + long fclk_Hz; + int drdy_irq; + struct completion drdy_completion; + bool continuous_mode; bool i2c_crc_enabled; u32 avdd_uV; u32 ext_ref_uV; @@ -258,13 +380,36 @@ struct ads112c14_data { bool refn_is_gnd; u32 ext_ref_ohms; struct ads112c14_measurement *measurements; + struct ads112c14_channel_state *channel_states; u32 num_measurements; u8 sys_mon_chan_short_gain_val; int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; + int sinc4_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2]; + int sinc4_sinc1_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2]; + int sinc4_sinc1_pf1_sample_rate_available[2][2]; + int sinc_settling_time_range_available[ARRAY_SIZE(ads112c14_sinc_latency_tmod)][ARRAY_SIZE(ads112c14_fmod_div)][3][2]; + int fir_settling_time_range_available[ARRAY_SIZE(ads112c14_fir_latency_tmod)][ARRAY_SIZE(ads112c14_fmod_div)][3][2]; IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS + ARRAY_SIZE(ads112c14_sys_mon_channels)); }; +static irqreturn_t ads112c14_drdy_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ads112c14_data *data = iio_priv(indio_dev); + + if (READ_ONCE(data->continuous_mode)) + iio_trigger_poll(data->drdy_trig); + else + complete(&data->drdy_completion); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops ads112c14_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -429,13 +574,250 @@ static const struct regmap_config ads112c14_regmap_config = { .cache_type = REGCACHE_MAPLE, }; +static int ads112c14_get_osr(struct ads112c14_channel_state *channel_state) +{ + u8 i; + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + i = channel_state->filter_osr; + return ads112c14_sinc4_osr_available[i]; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + i = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128; + return ads112c14_sinc4_sinc1_osr_available[i]; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS: + i = channel_state->speed_mode; + return ads112c14_sinc4_sinc1_pf1_25sps_osr_available[i]; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + i = channel_state->speed_mode; + return ads112c14_sinc4_sinc1_pf1_20sps_osr_available[i]; + default: + return -EINVAL; + } +} + +static int ads112c14_get_fmod_Hz(struct ads112c14_data *data, + struct ads112c14_channel_state *channel_state) +{ + return data->fclk_Hz / ads112c14_fmod_div[channel_state->speed_mode]; +} + +static int ads112c14_delay_to_tmod(u8 delay) +{ + if (!delay) + return 0; + + return BIT(delay - 1); +} + +static int ads112c14_get_latency_tmod(struct ads112c14_channel_state *channel_state) +{ + u8 speed_mode = channel_state->speed_mode; + u8 filter_osr = channel_state->filter_osr; + + if (speed_mode >= ARRAY_SIZE(ads112c14_fmod_div)) + return -EINVAL; + + if (filter_osr <= ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024) + return ads112c14_sinc_latency_tmod[filter_osr][speed_mode]; + + if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS) + return ads112c14_fir_latency_tmod[0][speed_mode]; + + if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS) + return ads112c14_fir_latency_tmod[1][speed_mode]; + + return -EINVAL; +} + +static int ads112c14_get_settling_time_us(struct ads112c14_data *data, + struct ads112c14_channel_state *channel_state, + u8 delay, u32 *settling_time_us) +{ + int fmod_Hz, latency_tmod; + u64 total_tmod; + + fmod_Hz = ads112c14_get_fmod_Hz(data, channel_state); + if (fmod_Hz <= 0) + return -EINVAL; + + latency_tmod = ads112c14_get_latency_tmod(channel_state); + if (latency_tmod < 0) + return latency_tmod; + + total_tmod = latency_tmod + ads112c14_delay_to_tmod(delay); + *settling_time_us = div64_u64(total_tmod * USEC_PER_SEC, fmod_Hz); + + return 0; +} + +static int ads112c14_find_delay_for_settling_time_us(struct ads112c14_data *data, + struct ads112c14_channel_state *channel_state, + s64 settling_time_us, u8 *delay) +{ + u64 delay_us, delay_tmod_needed; + u32 fixed_latency_us; + int ret, fmod_Hz; + u8 i; + + ret = ads112c14_get_settling_time_us(data, channel_state, 0, &fixed_latency_us); + if (ret) + return ret; + + if (settling_time_us <= fixed_latency_us) { + *delay = 0; + return 0; + } + + fmod_Hz = ads112c14_get_fmod_Hz(data, channel_state); + if (fmod_Hz <= 0) + return -EINVAL; + + delay_us = settling_time_us - fixed_latency_us; + delay_tmod_needed = DIV_ROUND_UP_ULL(delay_us * fmod_Hz, USEC_PER_SEC); + + for (i = 1; i < ADS112C14_DELAY_MAX; i++) { + if (ads112c14_delay_to_tmod(i) >= delay_tmod_needed) + break; + } + + *delay = i; + + return 0; +} + +static ssize_t ads112c14_read_settling_time(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + u32 settling_time_us; + int vals[2]; + int ret; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + ret = ads112c14_get_settling_time_us(data, channel_state, + channel_state->delay, + &settling_time_us); + if (ret) + return ret; + + iio_val_s64_decompose(settling_time_us, &vals[0], &vals[1]); + + return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals); +} + +static ssize_t ads112c14_write_settling_time(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + s64 settling_time_us; + int integer; + int fract; + u8 delay; + int ret; + + ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract); + if (ret) + return ret; + + settling_time_us = integer * MICRO + fract; + if (settling_time_us < 0) + return -EINVAL; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + ret = ads112c14_find_delay_for_settling_time_us(data, channel_state, + settling_time_us, &delay); + if (ret) + return ret; + + channel_state->delay = delay; + + return len; +} + +static ssize_t ads112c14_read_settling_time_available(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + u8 filter_osr, speed_mode; + const int (*range)[2]; + size_t len = 0; + int i; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + filter_osr = channel_state->filter_osr; + speed_mode = channel_state->speed_mode; + + if (speed_mode >= ARRAY_SIZE(ads112c14_fmod_div)) + return -EINVAL; + + if (filter_osr <= ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024) + range = data->sinc_settling_time_range_available[filter_osr][speed_mode]; + else if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS) + range = data->fir_settling_time_range_available[0][speed_mode]; + else if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS) + range = data->fir_settling_time_range_available[1][speed_mode]; + else + return -EINVAL; + + len += sysfs_emit_at(buf, len, "["); + for (i = 0; i < 3; i++) { + s64 range_val; + s32 int_val, rem; + + range_val = iio_val_s64_compose(range[i][0], range[i][1]); + int_val = div_s64_rem(range_val, MICRO, &rem); + + if (i) + len += sysfs_emit_at(buf, len, " "); + + len += sysfs_emit_at(buf, len, "%d.%06d", int_val, rem); + } + len += sysfs_emit_at(buf, len, "]\n"); + + return len; +} + static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, - const struct iio_chan_spec *chan) + const struct iio_chan_spec *chan, + bool en_burnout) { struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index]; + struct ads112c14_channel_state *channel_state; u32 refp_buf_en, refn_buf_en, ref_val, ref_sel; int ret; + channel_state = &data->channel_states[chan->scan_index]; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_SPEED_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_SPEED_MODE, + channel_state->speed_mode)); + if (ret) + return ret; + ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG, ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN, FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) | @@ -482,9 +864,16 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, return ret; ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, - ADS112C14_DATA_RATE_CFG_GC_EN, + ADS112C14_DATA_RATE_CFG_DELAY | + ADS112C14_DATA_RATE_CFG_GC_EN | + ADS112C14_DATA_RATE_CFG_FLTR_OSR, + FIELD_PREP(ADS112C14_DATA_RATE_CFG_DELAY, + channel_state->delay) | FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, - measurement->global_chop)); + (measurement->global_chop && + !en_burnout) ? 1 : 0) | + FIELD_PREP(ADS112C14_DATA_RATE_CFG_FLTR_OSR, + channel_state->filter_osr)); if (ret) return ret; @@ -527,9 +916,12 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, const struct iio_chan_spec *chan) { + struct ads112c14_channel_state *channel_state; u32 gain_val; int ret; + channel_state = &data->channel_states[chan->scan_index]; + /* * NB: IDAC registers are left as-is in case they are generating current * needed for the external reference measurement. @@ -556,6 +948,25 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, if (ret) return ret; + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_SPEED_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_SPEED_MODE, + channel_state->speed_mode)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, + ADS112C14_DATA_RATE_CFG_DELAY | + ADS112C14_DATA_RATE_CFG_GC_EN | + ADS112C14_DATA_RATE_CFG_FLTR_OSR, + FIELD_PREP(ADS112C14_DATA_RATE_CFG_DELAY, + channel_state->delay) | + FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, 0) | + FIELD_PREP(ADS112C14_DATA_RATE_CFG_FLTR_OSR, + channel_state->filter_osr)); + if (ret) + return ret; + /* * REVISIT: if we implement regulator support for the REFOUT pin, we * might need to make this voltage match what is required by that. In @@ -581,35 +992,108 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, return 0; } -static int ads112c14_single_conversion(struct ads112c14_data *data, - const struct iio_chan_spec *chan, - u8 *buf, bool for_scan) +static int ads112c14_prepare_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan, + bool en_burnout) +{ + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) + return ads112c14_prepare_measurement_channel(data, chan, en_burnout); + + return ads112c14_prepare_sys_mon_channel(data, chan); +} + +static int ads112c14_scan_read(struct ads112c14_data *data, u8 *buf) { struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); - u32 reg_val; + int ret; + u8 len; + + len = BITS_TO_BYTES(data->chip_info->resolution_bits); + if (data->i2c_crc_enabled) + len += 1; + + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, len, buf); + if (ret < 0) + return ret; + + return 0; +} + +static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data, + u32 settle_time_us) +{ + unsigned long remaining; int ret; - guard(mutex)(&data->lock); - - if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { - ret = ads112c14_prepare_measurement_channel(data, chan); - if (ret) - return ret; - } else { - ret = ads112c14_prepare_sys_mon_channel(data, chan); - if (ret) - return ret; - } + reinit_completion(&data->drdy_completion); ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, ADS112C14_CONVERSION_CTRL_START); if (ret) return ret; - ret = regmap_read_poll_timeout(data->regmap, - ADS112C14_REG_STATUS_MSB, reg_val, - FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val), - 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + /* Give it 1ms more than calculated settling time. */ + remaining = wait_for_completion_timeout(&data->drdy_completion, + usecs_to_jiffies(settle_time_us + + 1 * USEC_PER_MSEC)); + + return remaining ? 0 : -ETIMEDOUT; +} + +static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data, + u32 settle_time_us) +{ + u32 reg_val; + int ret; + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) + return ret; + + /* Give it 1ms more than calculated settling time. */ + return regmap_read_poll_timeout(data->regmap, + ADS112C14_REG_STATUS_MSB, reg_val, + FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val), + 1 * USEC_PER_MSEC, + settle_time_us + 1 * USEC_PER_MSEC); +} + +static int ads112c14_single_conversion(struct ads112c14_data *data, + const struct iio_chan_spec *chan, + u8 *buf, bool en_burnout, bool for_scan) +{ + struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); + struct ads112c14_channel_state *channel_state; + u32 settle_time_us; + int ret; + + guard(mutex)(&data->lock); + + ret = ads112c14_prepare_channel(data, chan, en_burnout); + if (ret) + return ret; + + channel_state = &data->channel_states[chan->scan_index]; + ret = ads112c14_get_settling_time_us(data, channel_state, + channel_state->delay, + &settle_time_us); + if (ret) + return ret; + + ret = regmap_test_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, + ADS112C14_DATA_RATE_CFG_GC_EN); + if (ret < 0) + return ret; + + /* Input chopping doubles the settling time. */ + if (ret) + settle_time_us *= 2; + + if (data->drdy_irq) + ret = ads112c14_wait_for_conversion_irq(data, settle_time_us); + else + ret = ads112c14_wait_for_conversion_poll(data, settle_time_us); if (ret) return ret; @@ -619,23 +1103,21 @@ static int ads112c14_single_conversion(struct ads112c14_data *data, * with CRC errors, but rather leave it to userspace to decide what to * do. */ - if (for_scan) { - u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) + - (data->i2c_crc_enabled ? 1 : 0); - - ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, - len, buf); - if (ret < 0) - return ret; - - return 0; - } + if (for_scan) + return ads112c14_scan_read(data, buf); return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf, BITS_TO_BYTES(data->chip_info->resolution_bits), data->i2c_crc_enabled); } +static bool ads112c14_using_drdy_trigger(struct iio_dev *indio_dev) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + return data->drdy_trig && indio_dev->trig == data->drdy_trig; +} + static int ads112c14_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -665,7 +1147,7 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; - ret = ads112c14_single_conversion(data, chan, buf, false); + ret = ads112c14_single_conversion(data, chan, buf, false, false); if (ret) return ret; @@ -743,6 +1225,45 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, */ *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV); return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: { + struct ads112c14_channel_state *channel_state; + const int (*available)[2]; + u8 i, j; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + j = channel_state->filter_osr; + available = data->sinc4_sample_rate_available[j]; + i = channel_state->speed_mode; + break; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + j = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128; + available = data->sinc4_sinc1_sample_rate_available[j]; + i = channel_state->speed_mode; + break; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS: + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + available = data->sinc4_sinc1_pf1_sample_rate_available; + i = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS; + break; + default: + return -EINVAL; + } + + *val = available[i][0]; + *val2 = available[i][1]; + return IIO_VAL_INT_PLUS_MICRO; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->lock); + + *val = ads112c14_get_osr(&data->channel_states[chan->scan_index]); + return IIO_VAL_INT; + } default: return -EINVAL; } @@ -753,6 +1274,9 @@ static int ads112c14_read_avail(struct iio_dev *indio_dev, int *type, int *length, long mask) { struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + + channel_state = &data->channel_states[chan->scan_index]; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -778,6 +1302,58 @@ static int ads112c14_read_avail(struct iio_dev *indio_dev, } return -EINVAL; + + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->lock); + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + *vals = (const int *)data->sinc4_sample_rate_available[channel_state->filter_osr]; + *length = 2 * ARRAY_SIZE(data->sinc4_sample_rate_available[0]); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + *vals = (const int *)data->sinc4_sinc1_sample_rate_available[channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128]; + *length = 2 * ARRAY_SIZE(data->sinc4_sinc1_sample_rate_available[0]); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS...ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + *vals = (const int *)data->sinc4_sinc1_pf1_sample_rate_available; + *length = 2 * ARRAY_SIZE(data->sinc4_sinc1_pf1_sample_rate_available); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->lock); + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + *vals = ads112c14_sinc4_osr_available; + *length = ARRAY_SIZE(ads112c14_sinc4_osr_available); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + *vals = ads112c14_sinc4_sinc1_osr_available; + *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS: + *vals = ads112c14_sinc4_sinc1_pf1_25sps_osr_available; + *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_25sps_osr_available); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + *vals = ads112c14_sinc4_sinc1_pf1_20sps_osr_available; + *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_20sps_osr_available); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + } default: return -EINVAL; } @@ -822,6 +1398,99 @@ static int ads112c14_write_raw(struct iio_dev *indio_dev, return -EINVAL; } + case IIO_CHAN_INFO_SAMP_FREQ: { + struct ads112c14_channel_state *channel_state; + const int (*available)[2]; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + if (channel_state->filter_osr < ADS112C14_DATA_RATE_CFG_FLTR_OSR_128) { + u8 idx = channel_state->filter_osr; + + available = data->sinc4_sample_rate_available[idx]; + } else { + u8 idx = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128; + + available = data->sinc4_sinc1_sample_rate_available[idx]; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_fmod_div); i++) { + if (val == available[i][0] && val2 == available[i][1]) { + channel_state->speed_mode = i; + return 0; + } + } + + return -EINVAL; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS: + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: { + available = data->sinc4_sinc1_pf1_sample_rate_available; + + for (unsigned int i = 0; i < ARRAY_SIZE(data->sinc4_sinc1_pf1_sample_rate_available); i++) { + if (val == available[i][0] && val2 == available[i][1]) { + channel_state->filter_osr = i + ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS; + return 0; + } + } + + return -EINVAL; + } + default: + return -EINVAL; + } + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + struct ads112c14_channel_state *channel_state; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_osr_available); i++) { + if (val == ads112c14_sinc4_osr_available[i]) { + channel_state->filter_osr = i; + return 0; + } + } + + return -EINVAL; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available); i++) { + if (val == ads112c14_sinc4_sinc1_osr_available[i]) { + channel_state->filter_osr = i + ADS112C14_DATA_RATE_CFG_FLTR_OSR_128; + return 0; + } + } + + return -EINVAL; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS: + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_25sps_osr_available); i++) { + if (val == ads112c14_sinc4_sinc1_pf1_25sps_osr_available[i]) { + channel_state->speed_mode = i; + return 0; + } + } + + return -EINVAL; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_20sps_osr_available); i++) { + if (val == ads112c14_sinc4_sinc1_pf1_20sps_osr_available[i]) { + channel_state->speed_mode = i; + return 0; + } + } + + return -EINVAL; + default: + return -EINVAL; + } + } default: return -EINVAL; } @@ -839,6 +1508,19 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static int ads112c14_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + /* Only continuous mode is limited to a single channel. */ + if (!ads112c14_using_drdy_trigger(indio_dev)) + return 0; + + if (!iio_validate_scan_mask_onehot(indio_dev, scan_mask)) + return -EINVAL; + + return 0; +} + static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, @@ -893,6 +1575,19 @@ static int ads112c14_read_label(struct iio_dev *indio_dev, return sysfs_emit(label, "%s\n", label_source); } +static const struct iio_chan_spec * +ads112c14_first_active_channel(struct iio_dev *indio_dev) +{ + unsigned int scan_mask_len = iio_get_masklength(indio_dev); + unsigned int i; + + i = find_first_bit(indio_dev->active_scan_mask, scan_mask_len); + if (i == scan_mask_len) + return NULL; + + return &indio_dev->channels[i]; +} + static irqreturn_t ads112c14_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; @@ -902,12 +1597,32 @@ static irqreturn_t ads112c14_trigger_handler(int irq, void *private) u32 i; int ret; + if (ads112c14_using_drdy_trigger(indio_dev)) { + const struct iio_chan_spec *chan; + + chan = ads112c14_first_active_channel(indio_dev); + if (!chan) + goto out; + + ret = ads112c14_scan_read(data, (u8 *)&data->scan[0]); + if (ret) { + dev_err_once(indio_dev->dev.parent, + "failed to read channel %d: %pe; additional errors will be suppressed\n", + chan->channel, ERR_PTR(ret)); + goto out; + } + + iio_push_to_buffers_with_ts(indio_dev, data->scan, + sizeof(data->scan), pf->timestamp); + goto out; + } + iio_for_each_active_channel(indio_dev, i) { const struct iio_chan_spec *chan = &indio_dev->channels[i]; ret = ads112c14_single_conversion(data, chan, (u8 *)&data->scan[offset++], - true); + false, true); if (ret) { dev_err_once(indio_dev->dev.parent, "failed to read channel %d: %pe; additional errors will be suppressed\n", @@ -929,10 +1644,258 @@ static const struct iio_info ads112c14_info = { .read_avail = ads112c14_read_avail, .write_raw = ads112c14_write_raw, .write_raw_get_fmt = ads112c14_write_raw_get_fmt, + .update_scan_mode = ads112c14_update_scan_mode, .debugfs_reg_access = ads112c14_debugfs_reg_access, .read_label = ads112c14_read_label, }; +static int ads112c14_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + int ret; + + if (!ads112c14_using_drdy_trigger(indio_dev)) + return 0; + + chan = ads112c14_first_active_channel(indio_dev); + if (!chan) + return -EINVAL; + + guard(mutex)(&data->lock); + + ret = ads112c14_prepare_channel(data, chan, false); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS)); + if (ret) + return ret; + + WRITE_ONCE(data->continuous_mode, true); + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) { + WRITE_ONCE(data->continuous_mode, false); + regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT)); + return ret; + } + + return 0; +} + +static int ads112c14_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + int ret; + + if (!ads112c14_using_drdy_trigger(indio_dev)) + return 0; + + guard(mutex)(&data->lock); + + WRITE_ONCE(data->continuous_mode, false); + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_STOP); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT)); +} + +static const struct iio_buffer_setup_ops ads112c14_buffer_setup_ops = { + .postenable = ads112c14_buffer_postenable, + .predisable = ads112c14_buffer_predisable, +}; + +static ssize_t ads112c14_read_burnout_raw(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *chan, + char *buf) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_measurement *measurement; + int ret, ret2, val; + u8 raw_buf[3]; + + if (chan->channel >= ADS112C14_SYS_MON_CHANNEL_BASE) + return -EINVAL; + + measurement = &data->measurements[chan->scan_index]; + if (!measurement->burnout) + return -EINVAL; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_BOCS, + FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS, + measurement->burnout)); + if (ret) + return ret; + + ret = ads112c14_single_conversion(data, chan, raw_buf, true, false); + + /* + * Important to always turn off burnout current even if the conversion + * fails so that it does not affect subsequent measurements. + */ + ret2 = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_BOCS, + FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS, + ADS112C14_DEVICE_CFG_BOCS_DISABLED)); + if (ret < 0) + return ret; + if (ret2) + return ret2; + + switch (data->chip_info->resolution_bits) { + case 16: + val = get_unaligned_be16(raw_buf); + break; + case 24: + val = get_unaligned_be24(raw_buf); + break; + default: + return -EINVAL; + } + + if (measurement->bipolar) + val = sign_extend32(val, data->chip_info->resolution_bits - 1); + + return sysfs_emit(buf, "%d\n", val); +} + +static int ads112c14_get_filter_type_from_state(struct ads112c14_channel_state *channel_state) +{ + switch (channel_state->filter_osr) { + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32: + return ADS112C14_FILTER_TYPE_SINC4; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024: + return ADS112C14_FILTER_TYPE_SINC4_SINC1; + case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS...ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: + return ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1; + default: + return -EINVAL; + } +} + +static int ads112c14_set_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int val) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + int ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + ret = ads112c14_get_filter_type_from_state(channel_state); + if (ret < 0) + return ret; + + /* + * channel_state->filter_osr affects multiple attributes, so don't modify + * it if the filter type is already set to the requested value. + */ + if (ret == val) + return 0; + + /* Otherwise, pick an arbitrary default for each type. */ + switch (val) { + case ADS112C14_FILTER_TYPE_SINC4: + channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16; + break; + case ADS112C14_FILTER_TYPE_SINC4_SINC1: + channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_128; + break; + case ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1: + channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ads112c14_get_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_channel_state *channel_state; + + guard(mutex)(&data->lock); + + channel_state = &data->channel_states[chan->scan_index]; + + return ads112c14_get_filter_type_from_state(channel_state); +} + +static const struct iio_enum ads112c14_filter_type_enum = { + .items = ads112c14_filter_type_names, + .num_items = ARRAY_SIZE(ads112c14_filter_type_names), + .set = ads112c14_set_filter_type, + .get = ads112c14_get_filter_type, +}; + +static const struct iio_chan_spec_ext_info ads112c14_ext_info[] = { + { + .name = "settlingtime", + .read = ads112c14_read_settling_time, + .write = ads112c14_write_settling_time, + .shared = IIO_SEPARATE, + }, + { + .name = "settlingtime_available", + .read = ads112c14_read_settling_time_available, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum), + { } +}; + +static const struct iio_chan_spec_ext_info ads112c14_ext_info_burnout[] = { + { + .name = "burnoutraw", + .read = ads112c14_read_burnout_raw, + }, + { + .name = "settlingtime", + .read = ads112c14_read_settling_time, + .write = ads112c14_write_settling_time, + .shared = IIO_SEPARATE, + }, + { + .name = "settlingtime_available", + .read = ads112c14_read_settling_time_available, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum), + { } +}; + static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag) { u32 current_uA = current_nA / (NANO / MICRO); @@ -955,7 +1918,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, struct ads112c14_data *data = iio_priv(indio_dev); struct device *dev = indio_dev->dev.parent; struct iio_chan_spec *channels; - u32 num_child_nodes, i, pair[2]; + u32 num_child_nodes, num_data_chans, i, pair[2]; int ret; *need_avdd_ref = false; @@ -968,8 +1931,15 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, if (!data->measurements) return -ENOMEM; - channels = devm_kcalloc(dev, num_child_nodes + - ARRAY_SIZE(ads112c14_sys_mon_channels) + 1, + num_data_chans = num_child_nodes + ARRAY_SIZE(ads112c14_sys_mon_channels); + + data->channel_states = devm_kcalloc(dev, num_data_chans, + sizeof(*data->channel_states), + GFP_KERNEL); + if (!data->channel_states) + return -ENOMEM; + + channels = devm_kcalloc(dev, num_data_chans + 1, sizeof(*channels), GFP_KERNEL); if (!channels) return -ENOMEM; @@ -978,10 +1948,13 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, device_for_each_named_child_node_scoped(dev, child, "channel") { struct ads112c14_measurement *measurement = &data->measurements[i]; struct iio_chan_spec *spec = &channels[i]; + const char *propname; spec->indexed = 1; spec->scan_index = i; + spec->ext_info = ads112c14_ext_info; measurement->gain_val = 1; + data->channel_states[i].filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16; if (fwnode_property_present(child, "label")) { ret = fwnode_property_read_string(child, "label", &measurement->label); @@ -1101,6 +2074,35 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, measurement->global_chop = fwnode_property_read_bool(child, "input-chopping"); + propname = "burn-out-current-nanoamp"; + if (fwnode_property_present(child, propname)) { + u32 burnout_nA; + + ret = fwnode_property_read_u32(child, propname, &burnout_nA); + if (ret) + return dev_err_probe(dev, ret, + "failed to read %s property\n", + propname); + + switch (burnout_nA) { + case 200: + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_200_nA; + break; + case 1000: + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_1_uA; + break; + case 10000: + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_10_uA; + break; + default: + return dev_err_probe(dev, -EINVAL, + "invalid %s value\n", propname); + } + + if (measurement->burnout != ADS112C14_DEVICE_CFG_BOCS_DISABLED) + spec->ext_info = ads112c14_ext_info_burnout; + } + if (fwnode_property_present(child, "reference-sources")) { ret = fwnode_property_match_property_string(child, "reference-sources", ads112c14_vref_source_names, @@ -1117,8 +2119,13 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL) *need_ext_ref = true; - spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); - spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE); + spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); /* * If reference source is resistor rather than voltage supply, @@ -1152,6 +2159,10 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) { struct iio_chan_spec *spec = &channels[i]; + struct ads112c14_channel_state *channel_state; + + channel_state = &data->channel_states[i]; + channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16; /* Update the template that was already copied with dynamic values. */ spec->scan_index = i; @@ -1190,6 +2201,94 @@ static void ads112c14_populate_scale_available(int (*scale_avail)[2], } } +static void ads112c14_populate_odr_tables(struct ads112c14_data *data) +{ + int *available; + u32 osr, fmod_Hz; + u64 odr_uHz; + u32 rem; + + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_osr_available); i++) { + osr = ads112c14_sinc4_osr_available[i]; + + for (unsigned int j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) { + fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j]; + odr_uHz = div_u64((u64)fmod_Hz * MICRO, osr); + available = data->sinc4_sample_rate_available[i][j]; + available[0] = div_u64_rem(odr_uHz, MICRO, &rem); + available[1] = rem; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available); i++) { + osr = ads112c14_sinc4_sinc1_osr_available[i]; + + for (unsigned int j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) { + fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j]; + odr_uHz = div_u64((u64)fmod_Hz * MICRO, osr); + available = data->sinc4_sinc1_sample_rate_available[i][j]; + available[0] = div_u64_rem(odr_uHz, MICRO, &rem); + available[1] = rem; + } + } + + odr_uHz = div_u64((u64)25 * data->fclk_Hz * MICRO, ADS112C14_INTERNAL_CLK_Hz); + available = data->sinc4_sinc1_pf1_sample_rate_available[0]; + available[0] = div_u64_rem(odr_uHz, MICRO, &rem); + available[1] = rem; + + odr_uHz = div_u64((u64)20 * data->fclk_Hz * MICRO, ADS112C14_INTERNAL_CLK_Hz); + available = data->sinc4_sinc1_pf1_sample_rate_available[1]; + available[0] = div_u64_rem(odr_uHz, MICRO, &rem); + available[1] = rem; +} + +static void ads112c14_populate_settling_range_tables(struct ads112c14_data *data) +{ + s32 (*avail)[2]; + u32 i, j; + + for (i = 0; i < ARRAY_SIZE(ads112c14_sinc_latency_tmod); i++) { + for (j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) { + u64 fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j]; + u64 start_tmod = ads112c14_sinc_latency_tmod[i][j]; + u64 step_tmod = ads112c14_delay_to_tmod(1); + u64 stop_tmod = start_tmod + ads112c14_delay_to_tmod(ADS112C14_DELAY_MAX); + s64 start_us, step_us, stop_us; + + start_us = DIV_ROUND_CLOSEST_ULL(start_tmod * USEC_PER_SEC, fmod_Hz); + step_us = DIV_ROUND_CLOSEST_ULL(step_tmod * USEC_PER_SEC, fmod_Hz); + stop_us = DIV_ROUND_CLOSEST_ULL(stop_tmod * USEC_PER_SEC, fmod_Hz); + + avail = data->sinc_settling_time_range_available[i][j]; + + iio_val_s64_decompose(start_us, &avail[0][0], &avail[0][1]); + iio_val_s64_decompose(step_us, &avail[1][0], &avail[1][1]); + iio_val_s64_decompose(stop_us, &avail[2][0], &avail[2][1]); + } + } + + for (i = 0; i < ARRAY_SIZE(ads112c14_fir_latency_tmod); i++) { + for (j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) { + u64 fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j]; + u64 start_tmod = ads112c14_fir_latency_tmod[i][j]; + u64 step_tmod = ads112c14_delay_to_tmod(1); + u64 stop_tmod = start_tmod + ads112c14_delay_to_tmod(ADS112C14_DELAY_MAX); + s64 start_us, step_us, stop_us; + + start_us = DIV_ROUND_CLOSEST_ULL(start_tmod * USEC_PER_SEC, fmod_Hz); + step_us = DIV_ROUND_CLOSEST_ULL(step_tmod * USEC_PER_SEC, fmod_Hz); + stop_us = DIV_ROUND_CLOSEST_ULL(stop_tmod * USEC_PER_SEC, fmod_Hz); + + avail = data->fir_settling_time_range_available[i][j]; + + iio_val_s64_decompose(start_us, &avail[0][0], &avail[0][1]); + iio_val_s64_decompose(step_us, &avail[1][0], &avail[1][1]); + iio_val_s64_decompose(stop_us, &avail[2][0], &avail[2][1]); + } + } +} + static void ads112c14_populate_tables(struct ads112c14_data *data) { u32 full_scale, fsr_bits; @@ -1227,6 +2326,8 @@ static void ads112c14_populate_tables(struct ads112c14_data *data) ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available, full_scale, fsr_bits); + ads112c14_populate_odr_tables(data); + ads112c14_populate_settling_range_tables(data); } static int ads112c14_probe(struct i2c_client *client) @@ -1235,6 +2336,7 @@ static int ads112c14_probe(struct i2c_client *client) const struct ads112c14_chip_info *info; struct iio_dev *indio_dev; struct ads112c14_data *data; + struct clk *clk; bool need_avdd_ref, need_ext_ref; u32 refp_uV = 0; u32 refn_uV = 0; @@ -1327,6 +2429,18 @@ static int ads112c14_probe(struct i2c_client *client) return dev_err_probe(dev, -EINVAL, "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n"); + clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clk\n"); + + if (clk) { + data->fclk_Hz = clk_get_rate(clk); + if (!data->fclk_Hz) + return dev_err_probe(dev, -EINVAL, "clk rate is 0\n"); + } else { + data->fclk_Hz = ADS112C14_INTERNAL_CLK_Hz; + } + /* It takes some time for the internal reference to stabilize. */ fsleep(10 * USEC_PER_MSEC); @@ -1391,6 +2505,71 @@ static int ads112c14_probe(struct i2c_client *client) if (ret) return ret; + if (fwnode_property_match_string(dev_fwnode(dev), "interrupt-names", "drdy") >= 0) { + data->drdy_irq = fwnode_irq_get_byname(dev_fwnode(dev), "drdy"); + if (data->drdy_irq < 0) + return dev_err_probe(dev, data->drdy_irq, + "failed to get drdy interrupt\n"); + + if (clk) + return dev_err_probe(dev, -EINVAL, + "cannot use both DRDY and CLK - they share the same pin\n"); + + /* + * REVISIT: would probably need to implement a pin controller in + * order to support open drain option here. + */ + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG, + ADS112C14_GPIO_CFG_GPIO3_CFG, + FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG, + ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_DATA_OUTPUT, + ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC, + FIELD_PREP(ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC, + ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DRDY)); + if (ret) + return ret; + + init_completion(&data->drdy_completion); + + data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-drdy", + info->name, + iio_device_id(indio_dev)); + if (!data->drdy_trig) + return -ENOMEM; + + data->drdy_trig->ops = &ads112c14_trigger_ops; + iio_trigger_set_drvdata(data->drdy_trig, indio_dev); + + ret = devm_iio_trigger_register(dev, data->drdy_trig); + if (ret) + return ret; + + ret = devm_request_irq(dev, data->drdy_irq, ads112c14_drdy_irq_handler, + 0, dev_name(dev), indio_dev); + if (ret) + return ret; + } + + if (clk) { + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG, + ADS112C14_GPIO_CFG_GPIO3_CFG, + FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG, + ADS112C14_GPIO_CFG_GPIO_CFG_INPUT)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CLK_SEL, + FIELD_PREP(ADS112C14_DEVICE_CFG_CLK_SEL, + ADS112C14_DEVICE_CFG_CLK_SEL_EXTERNAL)); + if (ret) + return ret; + } + ads112c14_populate_tables(data); indio_dev->name = info->name; @@ -1399,7 +2578,8 @@ static int ads112c14_probe(struct i2c_client *client) ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, - ads112c14_trigger_handler, NULL); + ads112c14_trigger_handler, + &ads112c14_buffer_setup_ops); if (ret) return ret; diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c index 2f8f75c8216b..7ecfb27fd7ef 100644 --- a/drivers/iio/adc/ti-ads131m02.c +++ b/drivers/iio/adc/ti-ads131m02.c @@ -897,6 +897,8 @@ static int ads131m_probe(struct spi_device *spi) indio_dev->info = &ads131m_info; config = spi_get_device_match_data(spi); + if (!config) + return -ENODATA; priv->config = config; indio_dev->name = config->name; diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index d27c1f2885fd..0d5708e0b831 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -527,8 +527,10 @@ static int ti_ads7950_probe(struct spi_device *spi) st->spi = spi; info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = spi->modalias; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = info->channels; indio_dev->num_channels = info->num_channels; diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 1516dd332f90..e53c839e87ef 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -548,7 +548,7 @@ static int tiadc_request_dma(struct platform_device *pdev, } /* RX buffer */ - dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + dma->buf = dma_alloc_coherent(dmaengine_get_dma_device(dma->chan), DMA_BUFFER_SIZE, &dma->addr, GFP_KERNEL); if (!dma->buf) goto err; @@ -688,7 +688,7 @@ static void tiadc_remove(struct platform_device *pdev) u32 step_en; if (dma->chan) { - dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + dma_free_coherent(dmaengine_get_dma_device(dma->chan), DMA_BUFFER_SIZE, dma->buf, dma->addr); dma_release_channel(dma->chan); } diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index e12f8604f59f..9adff120de1a 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -326,6 +326,8 @@ static int ad8366_probe(struct spi_device *spi) st->spi = spi; st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODATA; enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(enable_gpio)) diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 1bd82d3db5ea..a38922d13d25 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -88,9 +88,6 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, unsigned int i; int nents; - max_size = min(block->size, dmaengine_buffer->max_size); - max_size = round_down(max_size, dmaengine_buffer->align); - if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) dma_dir = DMA_DEV_TO_MEM; else @@ -251,6 +248,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { */ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan) { + struct device *dma_dev = dmaengine_get_dma_device(chan); struct dmaengine_buffer *dmaengine_buffer; unsigned int width, src_width, dest_width; struct dma_slave_caps caps; @@ -278,9 +276,9 @@ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan) INIT_LIST_HEAD(&dmaengine_buffer->active); dmaengine_buffer->chan = chan; dmaengine_buffer->align = width; - dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev); + dmaengine_buffer->max_size = dma_get_max_seg_size(dma_dev); - iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev, + iio_dma_buffer_init(&dmaengine_buffer->queue, dma_dev, &iio_dmaengine_default_ops); dmaengine_buffer->queue.buffer.attrs = iio_dmaengine_buffer_attrs; diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index 7c1ffda38b99..5e2b15463c99 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -283,12 +283,7 @@ static int ens160_set_trigger_state(struct iio_trigger *trig, bool state) ENS160_REG_CONFIG_INTDAT | ENS160_REG_CONFIG_INT_CFG; - if (state) - return regmap_set_bits(data->regmap, ENS160_REG_CONFIG, - int_bits); - else - return regmap_clear_bits(data->regmap, ENS160_REG_CONFIG, - int_bits); + return regmap_assign_bits(data->regmap, ENS160_REG_CONFIG, int_bits, state); } static const struct iio_trigger_ops ens160_trigger_ops = { diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index 8e15baa31423..b47d08ab4d0f 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "sps30.h" @@ -193,7 +194,10 @@ static ssize_t start_cleaning_store(struct device *dev, struct sps30_state *state = iio_priv(indio_dev); int val, ret; - if (kstrtoint(buf, 0, &val) || val != 1) + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + if (val != 1) return -EINVAL; guard(mutex)(&state->lock); @@ -230,8 +234,9 @@ static ssize_t cleaning_period_store(struct device *dev, struct device_attribute struct sps30_state *state = iio_priv(indio_dev); int val, ret; - if (kstrtoint(buf, 0, &val)) - return -EINVAL; + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) || (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index b971f8b646be..551b86236abc 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -311,7 +311,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids); static struct platform_driver cros_ec_sensors_platform_driver = { .driver = { .name = "cros-ec-sensors", - .pm = &cros_ec_sensors_pm_ops, + .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops), }, .probe = cros_ec_sensors_probe, .id_table = cros_ec_sensors_ids, diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index 5133755c2ea6..d03e40c91582 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -862,7 +862,7 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, } EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write); -static int __maybe_unused cros_ec_sensors_resume(struct device *dev) +static int cros_ec_sensors_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); @@ -879,8 +879,7 @@ static int __maybe_unused cros_ec_sensors_resume(struct device *dev) return ret; } -SIMPLE_DEV_PM_OPS(cros_ec_sensors_pm_ops, NULL, cros_ec_sensors_resume); -EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops); +EXPORT_GPL_SIMPLE_DEV_PM_OPS(cros_ec_sensors_pm_ops, NULL, cros_ec_sensors_resume); MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3b3c77d8b3e1..e373885e7c7a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -16,6 +16,8 @@ config AD3530R - AD3530/AD3530R (8-channel) - AD3531/AD3531R (4-channel) - AD3532/AD3532R (16-channel) + - AD3536R (16-channel, 12-bit) + - AD5710R/AD5711R (8-channel configurable IDAC/VDAC) To compile this driver as a module, choose M here: the module will be called ad3530r. @@ -137,6 +139,22 @@ config AD5449 To compile this driver as a module, choose M here: the module will be called ad5449. +config AD5529R + tristate "Analog Devices AD5529R High Voltage DAC driver" + depends on SPI_MASTER + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD5529R 16-Channel, + 12-Bit/16-Bit, 40V High Voltage Precision Digital to Analog Converter. + + The device features multiple output voltage ranges from -20V to +20V, + built-in 4.096V voltage reference, and digital functions including + toggle, dither, and ramp modes. Supports both 12-bit and 16-bit + resolution variants. + + To compile this driver as a module, choose M here: the + module will be called ad5529r. + config AD5592R_BASE tristate @@ -593,9 +611,13 @@ config MCP47A1 will be called mcp47a1. config MCP47FEB02 - tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" + tristate + +config MCP47FEB02_I2C + tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 I2C DAC driver" depends on I2C select REGMAP_I2C + select MCP47FEB02 help Say yes here if you want to build the driver for the Microchip: - 8-bit DAC: @@ -611,7 +633,31 @@ config MCP47FEB02 (DAC) with I2C interface. To compile this driver as a module, choose M here: the module - will be called mcp47feb02. + will be called mcp47feb02_i2c and you will also get + mcp47feb02_core for the core module. + +config MCP47FEB02_SPI + tristate "MCP48F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 SPI DAC driver" + depends on SPI + select REGMAP_SPI + select MCP47FEB02 + help + Say yes here if you want to build the driver for the Microchip: + - 8-bit DAC: + MCP48FEB01, MCP48FEB02, MCP48FEB04, MCP48FEB08, + MCP48FVB01, MCP48FVB02, MCP48FVB04, MCP48FVB08 + - 10-bit DAC: + MCP48FEB11, MCP48FEB12, MCP48FEB14, MCP48FEB18, + MCP48FVB11, MCP48FVB12, MCP48FVB14, MCP48FVB18 + - 12-bit DAC: + MCP48FEB21, MCP48FEB22, MCP48FEB24, MCP48FEB28, + MCP48FVB21, MCP48FVB22, MCP48FVB24, MCP48FVB28 + having 1 to 8 channels, 8/10/12-bit digital-to-analog converter + (DAC) with SPI interface. + + To compile this driver as a module, choose M here: the module + will be called mcp47feb02_spi and you will also get + mcp47feb02_core for the core module. config MCP4821 tristate "MCP4801/02/11/12/21/22 DAC driver" diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 992f8930f95c..2cd0fe23db9e 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_AD5446) += ad5446.o obj-$(CONFIG_AD5446_SPI) += ad5446-spi.o obj-$(CONFIG_AD5446_I2C) += ad5446-i2c.o obj-$(CONFIG_AD5449) += ad5449.o +obj-$(CONFIG_AD5529R) += ad5529r.o obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o obj-$(CONFIG_AD5592R) += ad5592r.o obj-$(CONFIG_AD5593R) += ad5593r.o @@ -56,7 +57,9 @@ obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o obj-$(CONFIG_MCP47A1) += mcp47a1.o -obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o +obj-$(CONFIG_MCP47FEB02) += mcp47feb02-core.o +obj-$(CONFIG_MCP47FEB02_I2C) += mcp47feb02-i2c.o +obj-$(CONFIG_MCP47FEB02_SPI) += mcp47feb02-spi.o obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 06b7f280f61b..9e043639801d 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -3,8 +3,11 @@ * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver + * AD3536R 16-channel, 12-bit Voltage Output DAC Driver + * AD5710R 8-channel, 16-bit Configurable IDAC/VDAC Driver + * AD5711R 8-channel, 12-bit Configurable IDAC/VDAC Driver * - * Copyright 2025 Analog Devices Inc. + * Copyright 2025-2026 Analog Devices Inc. */ #include @@ -27,6 +30,8 @@ #include #include +#include + #define AD3530R_INTERFACE_CONFIG_A 0x00 #define AD3530R_OUTPUT_OPERATING_MODE_0 0x20 #define AD3530R_OUTPUT_OPERATING_MODE_1 0x21 @@ -39,6 +44,8 @@ #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +#define AD5710R_CHN_VMODE_EN 0xFF + /* AD3532R/AD3532 bank 0 registers (channels 0-7) */ #define AD3532R_INTERFACE_CONFIG_A_0 0x1000 #define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 @@ -61,14 +68,14 @@ #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) -#define AD3530R_REG_VAL_MASK GENMASK(15, 0) #define AD3530R_OP_MODE_CHAN_MSK(chan) (GENMASK(1, 0) << 2 * (chan)) +#define AD5710R_CHN_VMODE_EN_BIT(chan) BIT(chan) #define AD3530R_SW_RESET (BIT(7) | BIT(0)) #define AD3530R_INTERNAL_VREF_mV 2500 +#define AD5710R_INTERNAL_IREF_mA 50 #define AD3530R_LDAC_PULSE_US 100 -#define AD3530R_DAC_MAX_VAL GENMASK(15, 0) #define AD3530R_CH_PER_REG 4 #define AD3530R_CH_PER_BANK 8 #define AD3531R_MAX_CHANNELS 4 @@ -99,7 +106,9 @@ struct ad3530r_chip_info { unsigned int num_channels; unsigned int num_banks; unsigned int num_op_mode_regs; + unsigned int resolution; bool internal_ref_support; + bool configurable_ch_func; }; struct ad3530r_state { @@ -195,6 +204,23 @@ static const struct iio_enum ad3532r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static ssize_t ad5710r_read_powerdown_mode(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + int ret; + + /* Voltage output pulls to 15kohm_to_gnd, current goes three_state. */ + ret = regmap_test_bits(st->regmap, AD5710R_CHN_VMODE_EN, + AD5710R_CHN_VMODE_EN_BIT(chan->channel)); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%s\n", ret ? "15kohm_to_gnd" : "three_state"); +} + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -278,6 +304,61 @@ static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static void ad5710r_get_op_mode_reg(unsigned int channel, unsigned int *reg, + unsigned int *mask) +{ + unsigned int local_ch, reg_in_bank, ch_in_reg; + + local_ch = channel % AD3530R_CH_PER_BANK; + reg_in_bank = local_ch / AD3530R_CH_PER_REG; + ch_in_reg = local_ch % AD3530R_CH_PER_REG; + + *reg = AD3530R_OUTPUT_OPERATING_MODE_0 + reg_in_bank; + *mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg); +} + +static ssize_t ad5710r_get_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int reg, mask; + int ret; + + ad5710r_get_op_mode_reg(chan->channel, ®, &mask); + + ret = regmap_test_bits(st->regmap, reg, field_prep(mask, 1)); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", ret); +} + +static ssize_t ad5710r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int reg, mask; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + ad5710r_get_op_mode_reg(chan->channel, ®, &mask); + + ret = regmap_update_bits(st->regmap, reg, mask, + field_prep(mask, powerdown)); + if (ret) + return ret; + + return len; +} + static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) { return AD3530R_SW_LDAC_TRIG_A; @@ -310,7 +391,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, int ret; guard(mutex)(&st->lock); - st->buf = cpu_to_be16(val); + st->buf = cpu_to_be16(val << (16 - st->chip_info->resolution)); ret = regmap_bulk_write(st->regmap, st->chip_info->input_ch_reg(chan), &st->buf, sizeof(st->buf)); @@ -340,12 +421,15 @@ static int ad3530r_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - *val = FIELD_GET(AD3530R_REG_VAL_MASK, be16_to_cpu(st->buf)); + *val = be16_to_cpu(st->buf) >> (16 - st->chip_info->resolution); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = st->vref_mV; - *val2 = 16; + if (chan->type == IIO_CURRENT) + *val = AD5710R_INTERNAL_IREF_mA; + else + *val = st->vref_mV; + *val2 = st->chip_info->resolution; return IIO_VAL_FRACTIONAL_LOG2; default: @@ -361,7 +445,7 @@ static int ad3530r_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - if (val < 0 || val > AD3530R_DAC_MAX_VAL) + if (val < 0 || val > GENMASK(st->chip_info->resolution - 1, 0)) return -EINVAL; return ad3530r_dac_write(st, chan->channel, val); @@ -420,6 +504,21 @@ static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = { { } }; +static const struct iio_chan_spec_ext_info ad5710r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad5710r_get_dac_powerdown, + .write = ad5710r_set_dac_powerdown, + }, + { + .name = "powerdown_mode", + .shared = IIO_SEPARATE, + .read = ad5710r_read_powerdown_mode, + }, + { } +}; + #define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ @@ -468,6 +567,17 @@ static const struct iio_chan_spec ad3532r_channels[] = { AD3530R_CHAN(15, ad3532r_ext_info), }; +static const struct iio_chan_spec ad5710r_channels[] = { + AD3530R_CHAN(0, ad5710r_ext_info), + AD3530R_CHAN(1, ad5710r_ext_info), + AD3530R_CHAN(2, ad5710r_ext_info), + AD3530R_CHAN(3, ad5710r_ext_info), + AD3530R_CHAN(4, ad5710r_ext_info), + AD3530R_CHAN(5, ad5710r_ext_info), + AD3530R_CHAN(6, ad5710r_ext_info), + AD3530R_CHAN(7, ad5710r_ext_info), +}; + static const unsigned int ad3530r_if_config[] = { AD3530R_INTERFACE_CONFIG_A, }; @@ -523,8 +633,15 @@ static const struct regmap_config ad3532r_regmap_config = { .max_register = AD3532R_MAX_REG_ADDR, }; +static const struct regmap_config ad5710r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD5710R_CHN_VMODE_EN, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", + .resolution = 16, .channels = ad3530r_channels, .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), @@ -537,10 +654,12 @@ static const struct ad3530r_chip_info ad3530_chip = { .num_banks = ARRAY_SIZE(ad3530r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = false, + .configurable_ch_func = false, }; static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", + .resolution = 16, .channels = ad3530r_channels, .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), @@ -553,10 +672,12 @@ static const struct ad3530r_chip_info ad3530r_chip = { .num_banks = ARRAY_SIZE(ad3530r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = true, + .configurable_ch_func = false, }; static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", + .resolution = 16, .channels = ad3531r_channels, .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), @@ -569,10 +690,12 @@ static const struct ad3530r_chip_info ad3531_chip = { .num_banks = ARRAY_SIZE(ad3530r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = false, + .configurable_ch_func = false, }; static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", + .resolution = 16, .channels = ad3531r_channels, .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), @@ -585,10 +708,12 @@ static const struct ad3530r_chip_info ad3531r_chip = { .num_banks = ARRAY_SIZE(ad3530r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = true, + .configurable_ch_func = false, }; static const struct ad3530r_chip_info ad3532_chip = { .name = "ad3532", + .resolution = 16, .channels = ad3532r_channels, .regmap_config = &ad3532r_regmap_config, .num_channels = ARRAY_SIZE(ad3532r_channels), @@ -601,10 +726,12 @@ static const struct ad3530r_chip_info ad3532_chip = { .num_banks = ARRAY_SIZE(ad3532r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), .internal_ref_support = false, + .configurable_ch_func = false, }; static const struct ad3530r_chip_info ad3532r_chip = { .name = "ad3532r", + .resolution = 16, .channels = ad3532r_channels, .regmap_config = &ad3532r_regmap_config, .num_channels = ARRAY_SIZE(ad3532r_channels), @@ -617,8 +744,141 @@ static const struct ad3530r_chip_info ad3532r_chip = { .num_banks = ARRAY_SIZE(ad3532r_if_config), .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), .internal_ref_support = true, + .configurable_ch_func = false, }; +static const struct ad3530r_chip_info ad3536r_chip = { + .name = "ad3536r", + .resolution = 12, + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = true, + .configurable_ch_func = false, +}; + +static const struct ad3530r_chip_info ad5710r_chip = { + .name = "ad5710r", + .resolution = 16, + .regmap_config = &ad5710r_regmap_config, + .num_channels = ARRAY_SIZE(ad5710r_channels), + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, + .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), + .internal_ref_support = true, + .configurable_ch_func = true, +}; + +static const struct ad3530r_chip_info ad5711r_chip = { + .name = "ad5711r", + .resolution = 12, + .regmap_config = &ad5710r_regmap_config, + .num_channels = ARRAY_SIZE(ad5710r_channels), + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, + .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), + .internal_ref_support = true, + .configurable_ch_func = true, +}; + +static int ad3530r_parse_channel_cfg(struct iio_dev *indio_dev) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + struct iio_chan_spec *channels; + unsigned int num_chan, i; + int ret; + + num_chan = device_get_child_node_count(dev); + if (!num_chan) + return dev_err_probe(dev, -ENOENT, "No channels configured\n"); + + channels = devm_kcalloc(dev, num_chan, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + i = 0; + device_for_each_child_node_scoped(dev, child) { + unsigned int mode_reg, mode_mask, ch_func; + enum iio_chan_type chan_type; + u32 reg; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property of %pfwP\n", + child); + + if (reg >= st->chip_info->num_channels) + return dev_err_probe(dev, -ECHRNG, + "reg out of range in %pfwP\n", + child); + + ret = fwnode_property_read_u32(child, "adi,ch-func", &ch_func); + if (ret) + return dev_err_probe(dev, ret, + "Missing adi,ch-func property for %pfwP\n", + child); + + switch (ch_func) { + case CH_FUNC_VOLTAGE_OUTPUT: + ret = regmap_set_bits(st->regmap, AD5710R_CHN_VMODE_EN, + AD5710R_CHN_VMODE_EN_BIT(reg)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set voltage output for %pfwP\n", + child); + + chan_type = IIO_VOLTAGE; + break; + case CH_FUNC_CURRENT_OUTPUT: + chan_type = IIO_CURRENT; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Invalid adi,ch-func %u for %pfwP\n", + ch_func, child); + } + + channels[i] = ad5710r_channels[reg]; + channels[i].type = chan_type; + i++; + + ad5710r_get_op_mode_reg(reg, &mode_reg, &mode_mask); + + /* Enable the channel in normal operation mode */ + ret = regmap_update_bits(st->regmap, mode_reg, mode_mask, + field_prep(mode_mask, AD3530R_NORMAL_OP)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set normal operating mode for %pfwP\n", + child); + } + + indio_dev->channels = channels; + indio_dev->num_channels = num_chan; + + return 0; +} + static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, const unsigned int *regs, unsigned int num_regs, @@ -651,8 +911,9 @@ static int ad3530r_write_reg_banks(const struct ad3530r_state *st, return 0; } -static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) +static int ad3530r_setup(struct iio_dev *indio_dev, int external_vref_uV) { + struct ad3530r_state *st = iio_priv(indio_dev); const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; @@ -702,19 +963,29 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) st->vref_mV = range_multiplier * AD3530R_INTERNAL_VREF_mV; } - /* Set normal operating mode for all channels */ - val = FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(0), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(1), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); + if (chip_info->configurable_ch_func) { + /* Channels and their operating mode are configured from DT */ + ret = ad3530r_parse_channel_cfg(indio_dev); + if (ret) + return ret; + } else { + /* Set normal operating mode for all channels */ + val = FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(0), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(1), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode, - st->chip_info->num_op_mode_regs, val); - if (ret) - return ret; + ret = ad3530r_write_reg_banks(st, chip_info->op_mode, + chip_info->num_op_mode_regs, val); + if (ret) + return ret; - for (unsigned int i = 0; i < st->chip_info->num_channels; i++) - st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; + for (unsigned int i = 0; i < chip_info->num_channels; i++) + st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; + + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + } st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); if (IS_ERR(st->ldac_gpio)) @@ -772,15 +1043,13 @@ static int ad3530r_probe(struct spi_device *spi) if (!st->chip_info->internal_ref_support && external_vref_uV == 0) return -ENODEV; - ret = ad3530r_setup(st, external_vref_uV); + ret = ad3530r_setup(indio_dev, external_vref_uV); if (ret) return ret; indio_dev->name = st->chip_info->name; indio_dev->info = &ad3530r_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; return devm_iio_device_register(&spi->dev, indio_dev); } @@ -792,6 +1061,9 @@ static const struct spi_device_id ad3530r_id[] = { { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip }, { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip }, + { .name = "ad3536r", .driver_data = (kernel_ulong_t)&ad3536r_chip }, + { .name = "ad5710r", .driver_data = (kernel_ulong_t)&ad5710r_chip }, + { .name = "ad5711r", .driver_data = (kernel_ulong_t)&ad5711r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -803,6 +1075,9 @@ static const struct of_device_id ad3530r_of_match[] = { { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, { .compatible = "adi,ad3532", .data = &ad3532_chip }, { .compatible = "adi,ad3532r", .data = &ad3532r_chip }, + { .compatible = "adi,ad3536r", .data = &ad3536r_chip }, + { .compatible = "adi,ad5710r", .data = &ad5710r_chip }, + { .compatible = "adi,ad5711r", .data = &ad5711r_chip }, { } }; MODULE_DEVICE_TABLE(of, ad3530r_of_match); diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index 16db94fef9d4..039af514adde 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -696,6 +696,6 @@ static struct spi_driver ad3552r_driver = { module_spi_driver(ad3552r_driver); MODULE_AUTHOR("Mihail Chindris "); -MODULE_DESCRIPTION("Analog Device AD3552R DAC"); +MODULE_DESCRIPTION("Analog Devices AD3552R DAC"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_AD3552R"); diff --git a/drivers/iio/dac/ad5529r.c b/drivers/iio/dac/ad5529r.c new file mode 100644 index 000000000000..8154b87ff831 --- /dev/null +++ b/drivers/iio/dac/ad5529r.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD5529R Digital-to-Analog Converter Driver + * 16-Channel, 12/16-Bit, 40V High Voltage Precision DAC + * + * Copyright 2026 Analog Devices Inc. + * Author: Janani Sunil + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD5529R_REG_INTERFACE_CONFIG_A 0x00 +#define AD5529R_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0)) +#define AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION BIT(5) +#define AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4) +#define AD5529R_REG_DEVICE_CONFIG 0x02 +#define AD5529R_REG_CHIP_GRADE 0x06 +#define AD5529R_REG_SCRATCH_PAD 0x0A +#define AD5529R_REG_SPI_REVISION 0x0B +#define AD5529R_REG_VENDOR_H 0x0D +#define AD5529R_REG_STREAM_MODE 0x0E +#define AD5529R_REG_INTERFACE_STATUS_A 0x11 +#define AD5529R_REG_MULTI_DAC_CH_SEL 0x14 +#define AD5529R_REG_OUT_RANGE_BASE 0x3C +#define AD5529R_REG_OUT_RANGE(ch) (AD5529R_REG_OUT_RANGE_BASE + (ch) * 2) +#define AD5529R_REG_DAC_INPUT_A_BASE 0x148 +#define AD5529R_REG_DAC_INPUT_A(ch) (AD5529R_REG_DAC_INPUT_A_BASE + (ch) * 2) +#define AD5529R_REG_DAC_DATA_READBACK_BASE 0x16A +#define AD5529R_REG_TSENS_ALERT_FLAG 0x18C +#define AD5529R_REG_TSENS_SHTD_FLAG 0x18E +#define AD5529R_REG_FUNC_BUSY 0x1A0 +#define AD5529R_REG_REF_SEL 0x1A2 +#define AD5529R_REF_SEL_INTERNAL_REF BIT(0) +#define AD5529R_REG_INIT_CRC_ERR_STAT 0x1A4 +#define AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC 0x1A8 + +#define AD5529R_MAX_REGISTER 0x232 +#define AD5529R_8BIT_REG_MAX 0x13 +#define AD5529R_MAX_CHANNELS 16 +#define AD5529R_SPI_READ_FLAG 0x80 +#define AD5529R_ADDR_SHIFT 12 + +struct ad5529r_model_data { + const char *model_name; + unsigned int resolution; +}; + +#define AD5529R_DAC_CHANNEL(chan) ((struct iio_chan_spec) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +}) + +static const char * const ad5529r_supply_names[] = { + "vdd-io", + "vdd-5v", + "vdd-hv0", + "vdd-hv1", + "vdd-hv2", + "vdd-hv3", +}; + +static const char * const ad5529r_vss_supply_names[] = { + "vss-hv0", + "vss-hv1", + "vss-hv2", + "vss-hv3", +}; + +static const struct ad5529r_model_data ad5529r_16bit_model_data = { + .model_name = "ad5529r-16", + .resolution = 16, +}; + +static const struct ad5529r_model_data ad5529r_12bit_model_data = { + .model_name = "ad5529r-12", + .resolution = 12, +}; + +enum ad5529r_output_range { + AD5529R_RANGE_0V_5V, + AD5529R_RANGE_0V_10V, + AD5529R_RANGE_0V_20V, + AD5529R_RANGE_0V_40V, + AD5529R_RANGE_M5V_5V, + AD5529R_RANGE_M10V_10V, + AD5529R_RANGE_M15V_15V, + AD5529R_RANGE_M20V_20V, +}; + +static const s32 ad5529r_output_ranges_mV[8][2] = { + [AD5529R_RANGE_0V_5V] = { 0, 5000 }, + [AD5529R_RANGE_0V_10V] = { 0, 10000 }, + [AD5529R_RANGE_0V_20V] = { 0, 20000 }, + [AD5529R_RANGE_0V_40V] = { 0, 40000 }, + [AD5529R_RANGE_M5V_5V] = { -5000, 5000 }, + [AD5529R_RANGE_M10V_10V] = { -10000, 10000 }, + [AD5529R_RANGE_M15V_15V] = { -15000, 15000 }, + [AD5529R_RANGE_M20V_20V] = { -20000, 20000 }, +}; + +struct ad5529r_state { + const struct ad5529r_model_data *model_data; + struct regmap *regmap_8bit; + struct regmap *regmap_16bit; + struct iio_chan_spec channels[AD5529R_MAX_CHANNELS]; + unsigned int num_channels; + enum ad5529r_output_range output_range_idx[AD5529R_MAX_CHANNELS]; +}; + +static const struct regmap_range ad5529r_8bit_readable_ranges[] = { + regmap_reg_range(AD5529R_REG_INTERFACE_CONFIG_A, AD5529R_REG_CHIP_GRADE), + regmap_reg_range(AD5529R_REG_SCRATCH_PAD, AD5529R_REG_VENDOR_H), + regmap_reg_range(AD5529R_REG_STREAM_MODE, AD5529R_REG_INTERFACE_STATUS_A), +}; + +static const struct regmap_range ad5529r_16bit_readable_ranges[] = { + regmap_reg_range(AD5529R_REG_MULTI_DAC_CH_SEL, AD5529R_REG_INIT_CRC_ERR_STAT), + regmap_reg_range(AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC, AD5529R_MAX_REGISTER), +}; + +static const struct regmap_access_table ad5529r_8bit_readable_table = { + .yes_ranges = ad5529r_8bit_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad5529r_8bit_readable_ranges), +}; + +static const struct regmap_access_table ad5529r_16bit_readable_table = { + .yes_ranges = ad5529r_16bit_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad5529r_16bit_readable_ranges), +}; + +static const struct regmap_range ad5529r_8bit_read_only_ranges[] = { + regmap_reg_range(AD5529R_REG_DEVICE_CONFIG, AD5529R_REG_CHIP_GRADE), + regmap_reg_range(AD5529R_REG_SPI_REVISION, AD5529R_REG_VENDOR_H), +}; + +static const struct regmap_range ad5529r_16bit_read_only_ranges[] = { + regmap_reg_range(AD5529R_REG_DAC_DATA_READBACK_BASE, + AD5529R_REG_DAC_DATA_READBACK_BASE + + (AD5529R_MAX_CHANNELS - 1) * 2), + regmap_reg_range(AD5529R_REG_TSENS_ALERT_FLAG, AD5529R_REG_TSENS_SHTD_FLAG), + regmap_reg_range(AD5529R_REG_FUNC_BUSY, AD5529R_REG_FUNC_BUSY), + regmap_reg_range(AD5529R_REG_INIT_CRC_ERR_STAT, AD5529R_REG_INIT_CRC_ERR_STAT), +}; + +static const struct regmap_access_table ad5529r_8bit_writeable_table = { + .no_ranges = ad5529r_8bit_read_only_ranges, + .n_no_ranges = ARRAY_SIZE(ad5529r_8bit_read_only_ranges), +}; + +static const struct regmap_access_table ad5529r_16bit_writeable_table = { + .no_ranges = ad5529r_16bit_read_only_ranges, + .n_no_ranges = ARRAY_SIZE(ad5529r_16bit_read_only_ranges), +}; + +static struct regmap *ad5529r_get_regmap(struct ad5529r_state *st, + unsigned int reg) +{ + if (reg <= AD5529R_8BIT_REG_MAX) + return st->regmap_8bit; + + return st->regmap_16bit; +} + +static int ad5529r_reset(struct ad5529r_state *st) +{ + struct regmap *map = st->regmap_8bit; + struct device *dev = regmap_get_device(map); + struct reset_control *rst; + int ret; + + rst = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + if (rst) { + ret = reset_control_assert(rst); + if (ret) + return ret; + + /* Minimum reset low width (t_reset) is 20 ns per datasheet. */ + ndelay(20); + + ret = reset_control_deassert(rst); + if (ret) + return ret; + } else { + ret = regmap_write(map, AD5529R_REG_INTERFACE_CONFIG_A, + AD5529R_INTERFACE_CONFIG_A_SW_RESET); + if (ret) + return ret; + } + + /* + * Wait 10 ms for digital initialization to complete. + * Per datasheet, Interface Status A register NOT_READY_ERR bit is + * set if SPI transactions are attempted before digital initialization + * completes. + */ + fsleep(10 * USEC_PER_MSEC); + + return regmap_write(map, AD5529R_REG_INTERFACE_CONFIG_A, + AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE | + AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION); +} + +static int ad5529r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + enum ad5529r_output_range range_idx; + unsigned int reg_addr, reg_val_h; + int ret, span_mV; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * Read from DAC_INPUT_A register rather than DAC_DATA_READBACK. + * The DAC operates in transparent mode and directly reflects + * whatever value is written to the INPUT_A register. + */ + reg_addr = AD5529R_REG_DAC_INPUT_A(chan->channel); + ret = regmap_read(st->regmap_16bit, reg_addr, ®_val_h); + if (ret) + return ret; + + *val = reg_val_h; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + range_idx = st->output_range_idx[chan->channel]; + + /* + * The datasheet specifies a 4.096 V external reference, + * matching the nominal output voltage of the internal + * reference. + */ + span_mV = ad5529r_output_ranges_mV[range_idx][1] - + ad5529r_output_ranges_mV[range_idx][0]; + *val = span_mV; + *val2 = st->model_data->resolution; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + range_idx = st->output_range_idx[chan->channel]; + + if (ad5529r_output_ranges_mV[range_idx][0] < 0) + *val = -BIT(st->model_data->resolution - 1); + else + *val = 0; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad5529r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + unsigned int reg_addr; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!in_range(val, 0, BIT(st->model_data->resolution))) + return -EINVAL; + + reg_addr = AD5529R_REG_DAC_INPUT_A(chan->channel); + + return regmap_write(st->regmap_16bit, reg_addr, val); + default: + return -EINVAL; + } +} + +static int ad5529r_find_output_range(const s32 *vals) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ad5529r_output_ranges_mV); i++) { + const s32 *range = ad5529r_output_ranges_mV[i]; + + if (vals[0] == range[0] * (MICRO / MILLI) && + vals[1] == range[1] * (MICRO / MILLI)) + return i; + } + + return -EINVAL; +} + +static int ad5529r_parse_channel_ranges(struct device *dev, + struct ad5529r_state *st) +{ + unsigned long channel_mask = 0; + + device_for_each_child_node_scoped(dev, child) { + int ret, range_idx; + u32 ch; + + if (st->num_channels == ARRAY_SIZE(st->channels)) + return dev_err_probe(dev, -ENOSPC, "Too many channels\n"); + + ret = fwnode_property_read_u32(child, "reg", &ch); + if (ret) + return dev_err_probe(dev, ret, + "Missing reg property in channel node\n"); + + if (ch >= AD5529R_MAX_CHANNELS) + return dev_err_probe(dev, -ECHRNG, + "Channel %u exceeds maximum %u\n", + ch, AD5529R_MAX_CHANNELS - 1); + + if (__test_and_set_bit(ch, &channel_mask)) + return dev_err_probe(dev, -EEXIST, + "Duplicate channel %u\n", ch); + + if (fwnode_property_present(child, "output-range-microvolt")) { + s32 vals[2]; + + /* + * DT stores cells as raw 32-bit values; signed endpoints are + * encoded by dtc in two's-complement and then interpreted + * here as s32. + */ + ret = fwnode_property_read_u32_array(child, + "output-range-microvolt", + (u32 *)vals, ARRAY_SIZE(vals)); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to read range for ch %u\n", + ch); + + range_idx = ad5529r_find_output_range(vals); + if (range_idx < 0) + return dev_err_probe(dev, range_idx, + "Invalid range [%d %d] for ch %u\n", + vals[0], vals[1], ch); + } else { + range_idx = AD5529R_RANGE_0V_5V; + } + + st->output_range_idx[ch] = range_idx; + ret = regmap_write(st->regmap_16bit, + AD5529R_REG_OUT_RANGE(ch), range_idx); + if (ret) + return dev_err_probe(dev, ret, + "Failed to configure range for ch %u\n", + ch); + + st->channels[st->num_channels++] = AD5529R_DAC_CHANNEL(ch); + } + + return 0; +} + +static int ad5529r_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(ad5529r_get_regmap(st, reg), reg, readval); + + return regmap_write(ad5529r_get_regmap(st, reg), reg, writeval); +} + +static const struct iio_info ad5529r_info = { + .read_raw = ad5529r_read_raw, + .write_raw = ad5529r_write_raw, + .debugfs_reg_access = ad5529r_reg_access, +}; + +static int ad5529r_probe(struct spi_device *spi) +{ + struct regmap_config regmap_16bit_cfg; + struct regmap_config regmap_8bit_cfg; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad5529r_state *st; + bool external_vref; + u32 dev_addr; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->model_data = spi_get_device_match_data(spi); + if (!st->model_data) + return dev_err_probe(dev, -ENODATA, + "Failed to identify device variant\n"); + + if (device_property_present(dev, "spi-device-addr")) { + ret = device_property_read_u32(dev, "spi-device-addr", &dev_addr); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read spi-device-addr\n"); + + if (dev_addr > 3) + return dev_err_probe(dev, -EADDRNOTAVAIL, + "spi-device-addr %u out of range [0, 3]\n", + dev_addr); + } else { + dev_addr = 0; + } + + regmap_8bit_cfg = (struct regmap_config) { + .name = "ad5529r-8bit", + .reg_bits = 16, + .val_bits = 8, + .max_register = AD5529R_8BIT_REG_MAX, + .read_flag_mask = AD5529R_SPI_READ_FLAG, + .rd_table = &ad5529r_8bit_readable_table, + .wr_table = &ad5529r_8bit_writeable_table, + .reg_base = dev_addr << AD5529R_ADDR_SHIFT, + }; + regmap_16bit_cfg = (struct regmap_config) { + .name = "ad5529r-16bit", + .reg_bits = 16, + .val_bits = 16, + .max_register = AD5529R_MAX_REGISTER, + .read_flag_mask = AD5529R_SPI_READ_FLAG, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &ad5529r_16bit_readable_table, + .wr_table = &ad5529r_16bit_writeable_table, + .reg_stride = 2, + .reg_base = dev_addr << AD5529R_ADDR_SHIFT, + }; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad5529r_supply_names), + ad5529r_supply_names); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable regulators\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(ad5529r_vss_supply_names); i++) { + ret = devm_regulator_get_enable_optional(dev, + ad5529r_vss_supply_names[i]); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to get and enable %s regulator\n", + ad5529r_vss_supply_names[i]); + } + + ret = devm_regulator_get_enable_optional(dev, "vref"); + if (ret == -ENODEV) + external_vref = false; + else if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable vref regulator\n"); + else + external_vref = true; + + /* Wait 10 ms after power-up before the first SPI transaction. */ + fsleep(10 * USEC_PER_MSEC); + + st->regmap_8bit = devm_regmap_init_spi(spi, ®map_8bit_cfg); + if (IS_ERR(st->regmap_8bit)) + return dev_err_probe(dev, PTR_ERR(st->regmap_8bit), + "Failed to initialize 8-bit regmap\n"); + + st->regmap_16bit = devm_regmap_init_spi(spi, ®map_16bit_cfg); + if (IS_ERR(st->regmap_16bit)) + return dev_err_probe(dev, PTR_ERR(st->regmap_16bit), + "Failed to initialize 16-bit regmap\n"); + + ret = ad5529r_reset(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset device\n"); + + ret = regmap_assign_bits(st->regmap_16bit, AD5529R_REG_REF_SEL, + AD5529R_REF_SEL_INTERNAL_REF, + !external_vref); + if (ret) + return dev_err_probe(dev, ret, "Failed to configure reference\n"); + + ret = ad5529r_parse_channel_ranges(dev, st); + if (ret) + return ret; + + indio_dev->name = st->model_data->model_name; + indio_dev->info = &ad5529r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->channels; + indio_dev->num_channels = st->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad5529r_of_match[] = { + { .compatible = "adi,ad5529r-16", .data = &ad5529r_16bit_model_data }, + { .compatible = "adi,ad5529r-12", .data = &ad5529r_12bit_model_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5529r_of_match); + +static const struct spi_device_id ad5529r_id[] = { + { + .name = "ad5529r-16", + .driver_data = (kernel_ulong_t)&ad5529r_16bit_model_data, + }, + { + .name = "ad5529r-12", + .driver_data = (kernel_ulong_t)&ad5529r_12bit_model_data, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5529r_id); + +static struct spi_driver ad5529r_driver = { + .driver = { + .name = "ad5529r", + .of_match_table = ad5529r_of_match, + }, + .probe = ad5529r_probe, + .id_table = ad5529r_id, +}; +module_spi_driver(ad5529r_driver); + +MODULE_AUTHOR("Janani Sunil "); +MODULE_DESCRIPTION("Analog Devices AD5529R 12/16-bit DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 0828b9909099..730289dd8ef6 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -819,6 +819,9 @@ static int ad5755_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODATA; + st->spi = spi; st->pwr_down = 0xf; diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 44efa373cd80..1e21ad2a050d 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -117,7 +118,7 @@ struct ad5758_state { unsigned int dc_dc_ilim; unsigned int slew_time; bool pwr_down; - __be32 d32[3]; + __be32 d32[3] __aligned(IIO_DMA_MINALIGN); }; /* @@ -540,7 +541,7 @@ static int ad5758_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OFFSET: min = st->out_range.min; max = st->out_range.max; - *val = ((min * (1 << 16)) / (max - min)) / 1000; + *val = div_s64((s64)min * (1 << 16), max - min); return IIO_VAL_INT; default: return -EINVAL; @@ -556,6 +557,9 @@ static int ad5758_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: + if (val < 0 || val > U16_MAX) + return -EINVAL; + mutex_lock(&st->lock); ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val); mutex_unlock(&st->lock); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index a2292810e4cf..469294fd1783 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -304,7 +304,7 @@ static ssize_t ad8460_write_powerdown(struct iio_dev *indio_dev, uintptr_t priva * If powerdown is set, HVDAC is enabled and the HV driver is * enabled via HV_RESET in case it is in shutdown mode, * If powerdown is cleared, HVDAC is set to shutdown state - * as well as the HV driver. Quiescent current decreases and ouput is + * as well as the HV driver. Quiescent current decreases and output is * floating (high impedance). */ diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02-core.c similarity index 69% rename from drivers/iio/dac/mcp47feb02.c rename to drivers/iio/dac/mcp47feb02-core.c index 8640b0ef4433..88bffbab0854 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02-core.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface + * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C and SPI interface * - * Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries + * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries * * Author: Ariana Lazar * @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -29,6 +28,8 @@ #include #include +#include "mcp47feb02.h" + /* Register addresses must be left shifted with 3 positions in order to append command mask */ #define MCP47FEB02_DAC0_REG_ADDR 0x00 #define MCP47FEB02_VREF_REG_ADDR 0x40 @@ -36,7 +37,6 @@ #define MCP47FEB02_DAC_CTRL_MASK GENMASK(1, 0) #define MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR 0x50 -#define MCP47FEB02_GAIN_BIT_MASK BIT(0) #define MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK BIT(6) #define MCP47FEB02_GAIN_BITS_MASK GENMASK(15, 8) @@ -49,12 +49,10 @@ #define MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK GENMASK(7, 0) /* Voltage reference, Power-Down control register and DAC Wiperlock status register fields */ -#define DAC_CTRL_MASK(ch) (GENMASK(1, 0) << (2 * (ch))) -#define DAC_CTRL_VAL(ch, val) ((val) << (2 * (ch))) +#define MCP47FEB02_VREF_PD_MASK(ch) (GENMASK(1, 0) << (2 * (ch))) -/* Gain Control and I2C Slave Address Reguster fields */ +/* Gain Control and I2C Slave Address Register fields */ #define DAC_GAIN_MASK(ch) (BIT(0) << (8 + (ch))) -#define DAC_GAIN_VAL(ch, val) ((val) << (8 + (ch))) #define REG_ADDR(reg) ((reg) << 3) #define NV_REG_ADDR(reg) ((NV_DAC_ADDR_OFFSET + (reg)) << 3) @@ -91,214 +89,6 @@ static const char * const mcp47feb02_powerdown_modes[] = { "open_circuit", }; -/** - * struct mcp47feb02_features - chip specific data - * @name: device name - * @phys_channels: number of hardware channels - * @resolution: DAC resolution - * @have_ext_vref1: does the hardware have an the second external voltage reference? - * @have_eeprom: does the hardware have an internal eeprom? - */ -struct mcp47feb02_features { - const char *name; - unsigned int phys_channels; - unsigned int resolution; - bool have_ext_vref1; - bool have_eeprom; -}; - -static const struct mcp47feb02_features mcp47feb01_chip_features = { - .name = "mcp47feb01", - .phys_channels = 1, - .resolution = 8, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb02_chip_features = { - .name = "mcp47feb02", - .phys_channels = 2, - .resolution = 8, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb04_chip_features = { - .name = "mcp47feb04", - .phys_channels = 4, - .resolution = 8, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb08_chip_features = { - .name = "mcp47feb08", - .phys_channels = 8, - .resolution = 8, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb11_chip_features = { - .name = "mcp47feb11", - .phys_channels = 1, - .resolution = 10, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb12_chip_features = { - .name = "mcp47feb12", - .phys_channels = 2, - .resolution = 10, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb14_chip_features = { - .name = "mcp47feb14", - .phys_channels = 4, - .resolution = 10, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb18_chip_features = { - .name = "mcp47feb18", - .phys_channels = 8, - .resolution = 10, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb21_chip_features = { - .name = "mcp47feb21", - .phys_channels = 1, - .resolution = 12, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb22_chip_features = { - .name = "mcp47feb22", - .phys_channels = 2, - .resolution = 12, - .have_ext_vref1 = false, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb24_chip_features = { - .name = "mcp47feb24", - .phys_channels = 4, - .resolution = 12, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47feb28_chip_features = { - .name = "mcp47feb28", - .phys_channels = 8, - .resolution = 12, - .have_ext_vref1 = true, - .have_eeprom = true, -}; - -static const struct mcp47feb02_features mcp47fvb01_chip_features = { - .name = "mcp47fvb01", - .phys_channels = 1, - .resolution = 8, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb02_chip_features = { - .name = "mcp47fvb02", - .phys_channels = 2, - .resolution = 8, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb04_chip_features = { - .name = "mcp47fvb04", - .phys_channels = 4, - .resolution = 8, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb08_chip_features = { - .name = "mcp47fvb08", - .phys_channels = 8, - .resolution = 8, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb11_chip_features = { - .name = "mcp47fvb11", - .phys_channels = 1, - .resolution = 10, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb12_chip_features = { - .name = "mcp47fvb12", - .phys_channels = 2, - .resolution = 10, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb14_chip_features = { - .name = "mcp47fvb14", - .phys_channels = 4, - .resolution = 10, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb18_chip_features = { - .name = "mcp47fvb18", - .phys_channels = 8, - .resolution = 10, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb21_chip_features = { - .name = "mcp47fvb21", - .phys_channels = 1, - .resolution = 12, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb22_chip_features = { - .name = "mcp47fvb22", - .phys_channels = 2, - .resolution = 12, - .have_ext_vref1 = false, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb24_chip_features = { - .name = "mcp47fvb24", - .phys_channels = 4, - .resolution = 12, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - -static const struct mcp47feb02_features mcp47fvb28_chip_features = { - .name = "mcp47fvb28", - .phys_channels = 8, - .resolution = 12, - .have_ext_vref1 = true, - .have_eeprom = false, -}; - /** * struct mcp47feb02_channel_data - channel configuration * @ref_mode: chosen voltage for reference @@ -379,7 +169,7 @@ static const struct regmap_access_table mcp47feb02_volatile_table = { .n_yes_ranges = ARRAY_SIZE(mcp47feb02_volatile_ranges), }; -static const struct regmap_config mcp47feb02_regmap_config = { +const struct regmap_config mcp47feb02_regmap_config = { .name = "mcp47feb02_regmap", .reg_bits = 8, .val_bits = 16, @@ -391,6 +181,7 @@ static const struct regmap_config mcp47feb02_regmap_config = { .cache_type = REGCACHE_MAPLE, .val_format_endian = REGMAP_ENDIAN_BIG, }; +EXPORT_SYMBOL_NS_GPL(mcp47feb02_regmap_config, "IIO_MCP47FEB02"); /* For devices that doesn't have nonvolatile memory */ static const struct regmap_range mcp47fvb02_readable_ranges[] = { @@ -421,7 +212,7 @@ static const struct regmap_access_table mcp47fvb02_volatile_table = { .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_volatile_ranges), }; -static const struct regmap_config mcp47fvb02_regmap_config = { +const struct regmap_config mcp47fvb02_regmap_config = { .name = "mcp47fvb02_regmap", .reg_bits = 8, .val_bits = 16, @@ -433,6 +224,7 @@ static const struct regmap_config mcp47fvb02_regmap_config = { .cache_type = REGCACHE_MAPLE, .val_format_endian = REGMAP_ENDIAN_BIG, }; +EXPORT_SYMBOL_NS_GPL(mcp47fvb02_regmap_config, "IIO_MCP47FEB02"); static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int reg, unsigned int val) @@ -443,12 +235,10 @@ static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int * Wait until the currently occurring EEPROM Write Cycle is completed. * Only serial commands to the volatile memory are allowed. */ - guard(mutex)(&data->lock); - ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, eewa_val, !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK), - USEC_PER_MSEC, USEC_PER_MSEC * 5); + 1 * USEC_PER_MSEC, 150 * USEC_PER_MSEC); if (ret) return ret; @@ -468,7 +258,9 @@ static ssize_t store_eeprom_store(struct device *dev, struct device_attribute *a return ret; if (!state) - return 0; + return len; + + guard(mutex)(&data->lock); /* * Verify DAC Wiper and DAC Configuration are unlocked. If both are disabled, @@ -508,7 +300,7 @@ static ssize_t store_eeprom_store(struct device *dev, struct device_attribute *a ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, eewa_val, !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK), - USEC_PER_MSEC, USEC_PER_MSEC * 5); + 1 * USEC_PER_MSEC, 150 * USEC_PER_MSEC); if (ret) return ret; @@ -551,11 +343,13 @@ static int mcp47feb02_suspend(struct device *dev) for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) { u8 pd_mode; + u32 mask; data->chdata[ch].powerdown = true; pd_mode = data->chdata[ch].powerdown_mode + 1; + mask = MCP47FEB02_VREF_PD_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, - DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode)); + mask, field_prep(mask, pd_mode)); if (ret) return ret; @@ -576,30 +370,30 @@ static int mcp47feb02_resume(struct device *dev) guard(mutex)(&data->lock); for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) { - u8 pd_mode; + u32 mask; int ret; data->chdata[ch].powerdown = false; - pd_mode = data->chdata[ch].powerdown_mode + 1; ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data); if (ret) return ret; + mask = MCP47FEB02_VREF_PD_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR, - DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode)); + mask, field_prep(mask, data->chdata[ch].ref_mode)); if (ret) return ret; + mask = DAC_GAIN_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, - DAC_GAIN_MASK(ch), - DAC_GAIN_VAL(ch, data->chdata[ch].use_2x_gain)); + mask, field_prep(mask, data->chdata[ch].use_2x_gain)); if (ret) return ret; + mask = MCP47FEB02_VREF_PD_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, - DAC_CTRL_MASK(ch), - DAC_CTRL_VAL(ch, MCP47FEB02_NORMAL_OPERATION)); + mask, field_prep(mask, MCP47FEB02_NORMAL_OPERATION)); if (ret) return ret; } @@ -642,6 +436,7 @@ static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t p u32 reg = ch->address; u8 tmp_pd_mode; bool state; + u32 mask; int ret; guard(mutex)(&data->lock); @@ -655,8 +450,9 @@ static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t p * requires writing normal operation mode (0) to the channel-specific register bits. */ tmp_pd_mode = state ? (data->chdata[reg].powerdown_mode + 1) : MCP47FEB02_NORMAL_OPERATION; + mask = MCP47FEB02_VREF_PD_MASK(reg); ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, - DAC_CTRL_MASK(reg), DAC_CTRL_VAL(reg, tmp_pd_mode)); + mask, field_prep(mask, tmp_pd_mode)); if (ret) return ret; @@ -665,7 +461,8 @@ static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t p return len; } -static DEFINE_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, mcp47feb02_resume); +EXPORT_NS_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, + mcp47feb02_resume, IIO_MCP47FEB02); static const struct iio_enum mcp47febxx_powerdown_mode_enum = { .items = mcp47feb02_powerdown_modes, @@ -800,6 +597,7 @@ static int mcp47feb02_check_scale(struct mcp47feb02_data *data, int val, int val static int mcp47feb02_ch_scale(struct mcp47feb02_data *data, int ch, int scale) { int tmp_val, ret; + u32 mask; if (scale == MCP47FEB02_SCALE_VDD) { tmp_val = MCP47FEB02_VREF_VDD; @@ -821,8 +619,9 @@ static int mcp47feb02_ch_scale(struct mcp47feb02_data *data, int ch, int scale) tmp_val = MCP47FEB02_INTERNAL_BAND_GAP; } + mask = MCP47FEB02_VREF_PD_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR, - DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, tmp_val)); + mask, field_prep(mask, tmp_val)); if (ret) return ret; @@ -845,6 +644,7 @@ static int mcp47feb02_ch_scale(struct mcp47feb02_data *data, int ch, int scale) static int mcp47feb02_set_scale(struct mcp47feb02_data *data, int ch, int scale) { int tmp_val, ret; + u32 mask; ret = mcp47feb02_ch_scale(data, ch, scale); if (ret) @@ -855,8 +655,9 @@ static int mcp47feb02_set_scale(struct mcp47feb02_data *data, int ch, int scale) else tmp_val = MCP47FEB02_GAIN_BIT_X1; + mask = DAC_GAIN_MASK(ch); ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, - DAC_GAIN_MASK(ch), DAC_GAIN_VAL(ch, tmp_val)); + mask, field_prep(mask, tmp_val)); if (ret) return ret; @@ -922,7 +723,10 @@ static int mcp47feb02_read_label(struct iio_dev *indio_dev, struct iio_chan_spec { struct mcp47feb02_data *data = iio_priv(indio_dev); - return sysfs_emit(label, "%s\n", data->labels[ch->address]); + if (data->labels[ch->address]) + return sysfs_emit(label, "%s\n", data->labels[ch->address]); + + return -ENOENT; } static const struct iio_info mcp47feb02_info = { @@ -975,10 +779,11 @@ static int mcp47feb02_parse_fw(struct iio_dev *indio_dev, set_bit(reg, &data->active_channels_mask); - ret = fwnode_property_read_string(child, "label", &data->labels[reg]); - if (ret) - return dev_err_probe(dev, ret, "%pfw: invalid label\n", - fwnode_get_name(child)); + if (fwnode_property_present(child, "label")) { + ret = fwnode_property_read_string(child, "label", &data->labels[reg]); + if (ret) + dev_warn_probe(dev, ret, "%pfw: invalid label\n", child); + } chanspec.address = reg; chanspec.channel = reg; @@ -1016,13 +821,17 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data) if (ret) return ret; - gain_ch = gain_ch & MCP47FEB02_GAIN_BITS_MASK; for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) { struct device *dev = regmap_get_device(data->regmap); - unsigned int pd_tmp; + unsigned int pd_tmp, dac_val; - data->chdata[i].ref_mode = (vref_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK; - data->chdata[i].use_2x_gain = (gain_ch >> i) & MCP47FEB02_GAIN_BIT_MASK; + ret = regmap_read(data->regmap, REG_ADDR(i), &dac_val); + if (ret) + return ret; + data->chdata[i].dac_data = dac_val; + + data->chdata[i].ref_mode = field_get(MCP47FEB02_VREF_PD_MASK(i), vref_ch); + data->chdata[i].use_2x_gain = field_get(DAC_GAIN_MASK(i), gain_ch); /* * Inform the user that the current voltage reference read from the volatile @@ -1065,7 +874,7 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data) break; } - pd_tmp = (pd_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK; + pd_tmp = field_get(MCP47FEB02_VREF_PD_MASK(i), pd_ch); data->chdata[i].powerdown_mode = pd_tmp ? (pd_tmp - 1) : pd_tmp; data->chdata[i].powerdown = !!(data->chdata[i].powerdown_mode); } @@ -1090,10 +899,10 @@ static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_uV, return 0; } -static int mcp47feb02_probe(struct i2c_client *client) +int mcp47feb02_common_probe(const struct mcp47feb02_features *chip_features, + struct regmap *regmap) { - const struct mcp47feb02_features *chip_features; - struct device *dev = &client->dev; + struct device *dev = regmap_get_device(regmap); struct mcp47feb02_data *data; struct iio_dev *indio_dev; int vref1_uV, vref_uV, vdd_uV, ret; @@ -1102,22 +911,16 @@ static int mcp47feb02_probe(struct i2c_client *client) if (!indio_dev) return -ENOMEM; + dev_set_drvdata(dev, indio_dev); + data = iio_priv(indio_dev); - chip_features = i2c_get_match_data(client); - if (!chip_features) - return -EINVAL; - data->chip_features = chip_features; + data->regmap = regmap; - if (chip_features->have_eeprom) { - data->regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config); + if (chip_features->have_eeprom) indio_dev->info = &mcp47feb02_info; - } else { - data->regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config); + else indio_dev->info = &mcp47fvb02_info; - } - if (IS_ERR(data->regmap)) - return dev_err_probe(dev, PTR_ERR(data->regmap), "Error initializing i2c regmap\n"); indio_dev->name = chip_features->name; @@ -1174,76 +977,8 @@ static int mcp47feb02_probe(struct i2c_client *client) return devm_iio_device_register(dev, indio_dev); } - -static const struct i2c_device_id mcp47feb02_id[] = { - { .name = "mcp47feb01", .driver_data = (kernel_ulong_t)&mcp47feb01_chip_features }, - { .name = "mcp47feb02", .driver_data = (kernel_ulong_t)&mcp47feb02_chip_features }, - { .name = "mcp47feb04", .driver_data = (kernel_ulong_t)&mcp47feb04_chip_features }, - { .name = "mcp47feb08", .driver_data = (kernel_ulong_t)&mcp47feb08_chip_features }, - { .name = "mcp47feb11", .driver_data = (kernel_ulong_t)&mcp47feb11_chip_features }, - { .name = "mcp47feb12", .driver_data = (kernel_ulong_t)&mcp47feb12_chip_features }, - { .name = "mcp47feb14", .driver_data = (kernel_ulong_t)&mcp47feb14_chip_features }, - { .name = "mcp47feb18", .driver_data = (kernel_ulong_t)&mcp47feb18_chip_features }, - { .name = "mcp47feb21", .driver_data = (kernel_ulong_t)&mcp47feb21_chip_features }, - { .name = "mcp47feb22", .driver_data = (kernel_ulong_t)&mcp47feb22_chip_features }, - { .name = "mcp47feb24", .driver_data = (kernel_ulong_t)&mcp47feb24_chip_features }, - { .name = "mcp47feb28", .driver_data = (kernel_ulong_t)&mcp47feb28_chip_features }, - { .name = "mcp47fvb01", .driver_data = (kernel_ulong_t)&mcp47fvb01_chip_features }, - { .name = "mcp47fvb02", .driver_data = (kernel_ulong_t)&mcp47fvb02_chip_features }, - { .name = "mcp47fvb04", .driver_data = (kernel_ulong_t)&mcp47fvb04_chip_features }, - { .name = "mcp47fvb08", .driver_data = (kernel_ulong_t)&mcp47fvb08_chip_features }, - { .name = "mcp47fvb11", .driver_data = (kernel_ulong_t)&mcp47fvb11_chip_features }, - { .name = "mcp47fvb12", .driver_data = (kernel_ulong_t)&mcp47fvb12_chip_features }, - { .name = "mcp47fvb14", .driver_data = (kernel_ulong_t)&mcp47fvb14_chip_features }, - { .name = "mcp47fvb18", .driver_data = (kernel_ulong_t)&mcp47fvb18_chip_features }, - { .name = "mcp47fvb21", .driver_data = (kernel_ulong_t)&mcp47fvb21_chip_features }, - { .name = "mcp47fvb22", .driver_data = (kernel_ulong_t)&mcp47fvb22_chip_features }, - { .name = "mcp47fvb24", .driver_data = (kernel_ulong_t)&mcp47fvb24_chip_features }, - { .name = "mcp47fvb28", .driver_data = (kernel_ulong_t)&mcp47fvb28_chip_features }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mcp47feb02_id); - -static const struct of_device_id mcp47feb02_of_match[] = { - { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features }, - { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features }, - { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features }, - { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features }, - { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features }, - { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features }, - { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features }, - { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features }, - { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features }, - { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features }, - { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features }, - { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features }, - { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features }, - { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features }, - { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features }, - { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features }, - { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features }, - { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features }, - { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features }, - { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features }, - { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features }, - { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features }, - { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features }, - { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features }, - { } -}; -MODULE_DEVICE_TABLE(of, mcp47feb02_of_match); - -static struct i2c_driver mcp47feb02_driver = { - .driver = { - .name = "mcp47feb02", - .of_match_table = mcp47feb02_of_match, - .pm = pm_sleep_ptr(&mcp47feb02_pm_ops), - }, - .probe = mcp47feb02_probe, - .id_table = mcp47feb02_id, -}; -module_i2c_driver(mcp47feb02_driver); +EXPORT_SYMBOL_NS(mcp47feb02_common_probe, "IIO_MCP47FEB02"); MODULE_AUTHOR("Ariana Lazar "); -MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface"); +MODULE_DESCRIPTION("IIO driver for MCP47FEB02/MCP48FEB02 Multi-Channel DAC"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/mcp47feb02-i2c.c b/drivers/iio/dac/mcp47feb02-i2c.c new file mode 100644 index 000000000000..7ec0cccad79b --- /dev/null +++ b/drivers/iio/dac/mcp47feb02-i2c.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface + * + * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Ariana Lazar + * + * Datasheet links for devices with I2C interface: + * [MCP47FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf + * [MCP47FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf + * [MCP47FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf + */ +#include +#include +#include +#include +#include +#include +#include + +#include "mcp47feb02.h" + +/* Parts with EEPROM memory */ +MCP47FEB02_CHIP_INFO(mcp47feb01, 1, 8, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb02, 2, 8, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb04, 4, 8, true, true); +MCP47FEB02_CHIP_INFO(mcp47feb08, 8, 8, true, true); +MCP47FEB02_CHIP_INFO(mcp47feb11, 1, 10, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb12, 2, 10, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb14, 4, 10, true, true); +MCP47FEB02_CHIP_INFO(mcp47feb18, 8, 10, true, true); +MCP47FEB02_CHIP_INFO(mcp47feb21, 1, 12, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb22, 2, 12, false, true); +MCP47FEB02_CHIP_INFO(mcp47feb24, 4, 12, true, true); +MCP47FEB02_CHIP_INFO(mcp47feb28, 8, 12, true, true); + +/* Parts without EEPROM memory */ +MCP47FEB02_CHIP_INFO(mcp47fvb01, 1, 8, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb02, 2, 8, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb04, 4, 8, true, false); +MCP47FEB02_CHIP_INFO(mcp47fvb08, 8, 8, true, false); +MCP47FEB02_CHIP_INFO(mcp47fvb11, 1, 10, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb12, 2, 10, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb14, 4, 10, true, false); +MCP47FEB02_CHIP_INFO(mcp47fvb18, 8, 10, true, false); +MCP47FEB02_CHIP_INFO(mcp47fvb21, 1, 12, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb22, 2, 12, false, false); +MCP47FEB02_CHIP_INFO(mcp47fvb24, 4, 12, true, false); +MCP47FEB02_CHIP_INFO(mcp47fvb28, 8, 12, true, false); + +static int mcp47feb02_i2c_probe(struct i2c_client *client) +{ + const struct mcp47feb02_features *chip_features; + struct device *dev = &client->dev; + struct regmap *regmap; + + chip_features = i2c_get_match_data(client); + if (!chip_features) + return dev_err_probe(dev, -ENODATA, "No match data found\n"); + + if (chip_features->have_eeprom) + regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config); + else + regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config); + + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing I2C regmap\n"); + + return mcp47feb02_common_probe(chip_features, regmap); +} + +static const struct i2c_device_id mcp47feb02_i2c_id[] = { + { .name = "mcp47feb01", .driver_data = (kernel_ulong_t)&mcp47feb01_chip_features }, + { .name = "mcp47feb02", .driver_data = (kernel_ulong_t)&mcp47feb02_chip_features }, + { .name = "mcp47feb04", .driver_data = (kernel_ulong_t)&mcp47feb04_chip_features }, + { .name = "mcp47feb08", .driver_data = (kernel_ulong_t)&mcp47feb08_chip_features }, + { .name = "mcp47feb11", .driver_data = (kernel_ulong_t)&mcp47feb11_chip_features }, + { .name = "mcp47feb12", .driver_data = (kernel_ulong_t)&mcp47feb12_chip_features }, + { .name = "mcp47feb14", .driver_data = (kernel_ulong_t)&mcp47feb14_chip_features }, + { .name = "mcp47feb18", .driver_data = (kernel_ulong_t)&mcp47feb18_chip_features }, + { .name = "mcp47feb21", .driver_data = (kernel_ulong_t)&mcp47feb21_chip_features }, + { .name = "mcp47feb22", .driver_data = (kernel_ulong_t)&mcp47feb22_chip_features }, + { .name = "mcp47feb24", .driver_data = (kernel_ulong_t)&mcp47feb24_chip_features }, + { .name = "mcp47feb28", .driver_data = (kernel_ulong_t)&mcp47feb28_chip_features }, + { .name = "mcp47fvb01", .driver_data = (kernel_ulong_t)&mcp47fvb01_chip_features }, + { .name = "mcp47fvb02", .driver_data = (kernel_ulong_t)&mcp47fvb02_chip_features }, + { .name = "mcp47fvb04", .driver_data = (kernel_ulong_t)&mcp47fvb04_chip_features }, + { .name = "mcp47fvb08", .driver_data = (kernel_ulong_t)&mcp47fvb08_chip_features }, + { .name = "mcp47fvb11", .driver_data = (kernel_ulong_t)&mcp47fvb11_chip_features }, + { .name = "mcp47fvb12", .driver_data = (kernel_ulong_t)&mcp47fvb12_chip_features }, + { .name = "mcp47fvb14", .driver_data = (kernel_ulong_t)&mcp47fvb14_chip_features }, + { .name = "mcp47fvb18", .driver_data = (kernel_ulong_t)&mcp47fvb18_chip_features }, + { .name = "mcp47fvb21", .driver_data = (kernel_ulong_t)&mcp47fvb21_chip_features }, + { .name = "mcp47fvb22", .driver_data = (kernel_ulong_t)&mcp47fvb22_chip_features }, + { .name = "mcp47fvb24", .driver_data = (kernel_ulong_t)&mcp47fvb24_chip_features }, + { .name = "mcp47fvb28", .driver_data = (kernel_ulong_t)&mcp47fvb28_chip_features }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp47feb02_i2c_id); + +static const struct of_device_id mcp47feb02_of_i2c_match[] = { + { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features }, + { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features }, + { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features }, + { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features }, + { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features }, + { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features }, + { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features }, + { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features }, + { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features }, + { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features }, + { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features }, + { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features }, + { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features }, + { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features }, + { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features }, + { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features }, + { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features }, + { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features }, + { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features }, + { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features }, + { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features }, + { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features }, + { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features }, + { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp47feb02_of_i2c_match); + +static struct i2c_driver mcp47feb02_i2c_driver = { + .driver = { + .name = "mcp47feb02", + .of_match_table = mcp47feb02_of_i2c_match, + .pm = pm_sleep_ptr(&mcp47feb02_pm_ops), + }, + .probe = mcp47feb02_i2c_probe, + .id_table = mcp47feb02_i2c_id, +}; +module_i2c_driver(mcp47feb02_i2c_driver); + +MODULE_AUTHOR("Ariana Lazar "); +MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_MCP47FEB02"); diff --git a/drivers/iio/dac/mcp47feb02-spi.c b/drivers/iio/dac/mcp47feb02-spi.c new file mode 100644 index 000000000000..974f8fe1b86e --- /dev/null +++ b/drivers/iio/dac/mcp47feb02-spi.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for MCP48FEB02 Multi-Channel DAC with SPI interface + * + * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Ariana Lazar + * + * Datasheet links for devices with SPI interface: + * [MCP48FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005429B.pdf + * [MCP48FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005466A.pdf + * [MCP48FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP48FXBX4-8-Family-Data-Sheet-DS20006362A.pdf + */ +#include +#include +#include +#include +#include +#include +#include + +#include "mcp47feb02.h" + +/* Parts with EEPROM memory */ +MCP47FEB02_CHIP_INFO(mcp48feb01, 1, 8, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb02, 2, 8, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb04, 4, 8, true, true); +MCP47FEB02_CHIP_INFO(mcp48feb08, 8, 8, true, true); +MCP47FEB02_CHIP_INFO(mcp48feb11, 1, 10, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb12, 2, 10, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb14, 4, 10, true, true); +MCP47FEB02_CHIP_INFO(mcp48feb18, 8, 10, true, true); +MCP47FEB02_CHIP_INFO(mcp48feb21, 1, 12, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb22, 2, 12, false, true); +MCP47FEB02_CHIP_INFO(mcp48feb24, 4, 12, true, true); +MCP47FEB02_CHIP_INFO(mcp48feb28, 8, 12, true, true); + +/* Parts without EEPROM memory */ +MCP47FEB02_CHIP_INFO(mcp48fvb01, 1, 8, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb02, 2, 8, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb04, 4, 8, true, false); +MCP47FEB02_CHIP_INFO(mcp48fvb08, 8, 8, true, false); +MCP47FEB02_CHIP_INFO(mcp48fvb11, 1, 10, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb12, 2, 10, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb14, 4, 10, true, false); +MCP47FEB02_CHIP_INFO(mcp48fvb18, 8, 10, true, false); +MCP47FEB02_CHIP_INFO(mcp48fvb21, 1, 12, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb22, 2, 12, false, false); +MCP47FEB02_CHIP_INFO(mcp48fvb24, 4, 12, true, false); +MCP47FEB02_CHIP_INFO(mcp48fvb28, 8, 12, true, false); + +static int mcp47feb02_spi_probe(struct spi_device *spi) +{ + const struct mcp47feb02_features *chip_features; + struct device *dev = &spi->dev; + struct regmap *regmap; + + chip_features = spi_get_device_match_data(spi); + if (!chip_features) + return dev_err_probe(dev, -ENODATA, "No match data found\n"); + + if (chip_features->have_eeprom) + regmap = devm_regmap_init_spi(spi, &mcp47feb02_regmap_config); + else + regmap = devm_regmap_init_spi(spi, &mcp47fvb02_regmap_config); + + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing SPI regmap\n"); + + return mcp47feb02_common_probe(chip_features, regmap); +} + +static const struct spi_device_id mcp47feb02_spi_id[] = { + { .name = "mcp48feb01", .driver_data = (kernel_ulong_t)&mcp48feb01_chip_features }, + { .name = "mcp48feb02", .driver_data = (kernel_ulong_t)&mcp48feb02_chip_features }, + { .name = "mcp48feb04", .driver_data = (kernel_ulong_t)&mcp48feb04_chip_features }, + { .name = "mcp48feb08", .driver_data = (kernel_ulong_t)&mcp48feb08_chip_features }, + { .name = "mcp48feb11", .driver_data = (kernel_ulong_t)&mcp48feb11_chip_features }, + { .name = "mcp48feb12", .driver_data = (kernel_ulong_t)&mcp48feb12_chip_features }, + { .name = "mcp48feb14", .driver_data = (kernel_ulong_t)&mcp48feb14_chip_features }, + { .name = "mcp48feb18", .driver_data = (kernel_ulong_t)&mcp48feb18_chip_features }, + { .name = "mcp48feb21", .driver_data = (kernel_ulong_t)&mcp48feb21_chip_features }, + { .name = "mcp48feb22", .driver_data = (kernel_ulong_t)&mcp48feb22_chip_features }, + { .name = "mcp48feb24", .driver_data = (kernel_ulong_t)&mcp48feb24_chip_features }, + { .name = "mcp48feb28", .driver_data = (kernel_ulong_t)&mcp48feb28_chip_features }, + { .name = "mcp48fvb01", .driver_data = (kernel_ulong_t)&mcp48fvb01_chip_features }, + { .name = "mcp48fvb02", .driver_data = (kernel_ulong_t)&mcp48fvb02_chip_features }, + { .name = "mcp48fvb04", .driver_data = (kernel_ulong_t)&mcp48fvb04_chip_features }, + { .name = "mcp48fvb08", .driver_data = (kernel_ulong_t)&mcp48fvb08_chip_features }, + { .name = "mcp48fvb11", .driver_data = (kernel_ulong_t)&mcp48fvb11_chip_features }, + { .name = "mcp48fvb12", .driver_data = (kernel_ulong_t)&mcp48fvb12_chip_features }, + { .name = "mcp48fvb14", .driver_data = (kernel_ulong_t)&mcp48fvb14_chip_features }, + { .name = "mcp48fvb18", .driver_data = (kernel_ulong_t)&mcp48fvb18_chip_features }, + { .name = "mcp48fvb21", .driver_data = (kernel_ulong_t)&mcp48fvb21_chip_features }, + { .name = "mcp48fvb22", .driver_data = (kernel_ulong_t)&mcp48fvb22_chip_features }, + { .name = "mcp48fvb24", .driver_data = (kernel_ulong_t)&mcp48fvb24_chip_features }, + { .name = "mcp48fvb28", .driver_data = (kernel_ulong_t)&mcp48fvb28_chip_features }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp47feb02_spi_id); + +static const struct of_device_id mcp47feb02_of_spi_match[] = { + { .compatible = "microchip,mcp48feb01", .data = &mcp48feb01_chip_features }, + { .compatible = "microchip,mcp48feb02", .data = &mcp48feb02_chip_features }, + { .compatible = "microchip,mcp48feb04", .data = &mcp48feb04_chip_features }, + { .compatible = "microchip,mcp48feb08", .data = &mcp48feb08_chip_features }, + { .compatible = "microchip,mcp48feb11", .data = &mcp48feb11_chip_features }, + { .compatible = "microchip,mcp48feb12", .data = &mcp48feb12_chip_features }, + { .compatible = "microchip,mcp48feb14", .data = &mcp48feb14_chip_features }, + { .compatible = "microchip,mcp48feb18", .data = &mcp48feb18_chip_features }, + { .compatible = "microchip,mcp48feb21", .data = &mcp48feb21_chip_features }, + { .compatible = "microchip,mcp48feb22", .data = &mcp48feb22_chip_features }, + { .compatible = "microchip,mcp48feb24", .data = &mcp48feb24_chip_features }, + { .compatible = "microchip,mcp48feb28", .data = &mcp48feb28_chip_features }, + { .compatible = "microchip,mcp48fvb01", .data = &mcp48fvb01_chip_features }, + { .compatible = "microchip,mcp48fvb02", .data = &mcp48fvb02_chip_features }, + { .compatible = "microchip,mcp48fvb04", .data = &mcp48fvb04_chip_features }, + { .compatible = "microchip,mcp48fvb08", .data = &mcp48fvb08_chip_features }, + { .compatible = "microchip,mcp48fvb11", .data = &mcp48fvb11_chip_features }, + { .compatible = "microchip,mcp48fvb12", .data = &mcp48fvb12_chip_features }, + { .compatible = "microchip,mcp48fvb14", .data = &mcp48fvb14_chip_features }, + { .compatible = "microchip,mcp48fvb18", .data = &mcp48fvb18_chip_features }, + { .compatible = "microchip,mcp48fvb21", .data = &mcp48fvb21_chip_features }, + { .compatible = "microchip,mcp48fvb22", .data = &mcp48fvb22_chip_features }, + { .compatible = "microchip,mcp48fvb24", .data = &mcp48fvb24_chip_features }, + { .compatible = "microchip,mcp48fvb28", .data = &mcp48fvb28_chip_features }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp47feb02_of_spi_match); + +static struct spi_driver mcp47feb02_spi_driver = { + .driver = { + .name = "mcp47feb02", + .of_match_table = mcp47feb02_of_spi_match, + .pm = pm_sleep_ptr(&mcp47feb02_pm_ops), + }, + .probe = mcp47feb02_spi_probe, + .id_table = mcp47feb02_spi_id, +}; +module_spi_driver(mcp47feb02_spi_driver); + +MODULE_AUTHOR("Ariana Lazar "); +MODULE_DESCRIPTION("IIO driver for MCP48FEB02 Multi-Channel DAC with SPI interface"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_MCP47FEB02"); diff --git a/drivers/iio/dac/mcp47feb02.h b/drivers/iio/dac/mcp47feb02.h new file mode 100644 index 000000000000..37fa4497c1c1 --- /dev/null +++ b/drivers/iio/dac/mcp47feb02.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __DRIVERS_IIO_DAC_MCP47FEB02_H__ +#define __DRIVERS_IIO_DAC_MCP47FEB02_H__ + +#include +#include + +#define MCP47FEB02_CHIP_INFO(_name, _channels, _res, _vref1, _eeprom) \ +static const struct mcp47feb02_features _name##_chip_features = { \ + .name = #_name, \ + .phys_channels = _channels, \ + .resolution = _res, \ + .have_ext_vref1 = _vref1, \ + .have_eeprom = _eeprom, \ +} + +/** + * struct mcp47feb02_features - chip specific data + * @name: device name + * @phys_channels: number of hardware channels + * @resolution: DAC resolution + * @have_ext_vref1: does the hardware have an the second external voltage reference? + * @have_eeprom: does the hardware have an internal eeprom? + */ +struct mcp47feb02_features { + const char *name; + unsigned int phys_channels; + unsigned int resolution; + bool have_ext_vref1; + bool have_eeprom; +}; + +extern const struct regmap_config mcp47feb02_regmap_config; +extern const struct regmap_config mcp47fvb02_regmap_config; + +int mcp47feb02_common_probe(const struct mcp47feb02_features *chip_features, + struct regmap *regmap); + +extern const struct dev_pm_ops mcp47feb02_pm_ops; + +#endif /* __DRIVERS_IIO_DAC_MCP47FEB02_H__ */ diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 8d8c00cd60e9..59defb1b5e62 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -255,9 +255,13 @@ static const struct iio_info mcp4821_info = { static int mcp4821_probe(struct spi_device *spi) { + const struct mcp4821_chip_info *info; struct iio_dev *indio_dev; struct mcp4821_state *state; - const struct mcp4821_chip_info *info; + + info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); if (indio_dev == NULL) @@ -268,7 +272,6 @@ static int mcp4821_probe(struct spi_device *spi) /* default gain is 2x */ state->gain = 2; - info = spi_get_device_match_data(spi); indio_dev->name = info->name; indio_dev->info = &mcp4821_info; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c index 19fcdbbc11c6..28ae1e7b593d 100644 --- a/drivers/iio/dummy/iio_simple_dummy.c +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -116,7 +116,7 @@ static const struct iio_chan_spec iio_dummy_channels[] = { BIT(IIO_CHAN_INFO_OFFSET) | /* * in_voltage0_scale - * Multipler for userspace to apply post offset + * Multiplier for userspace to apply post offset * when converting to standard units (microvolts) */ BIT(IIO_CHAN_INFO_SCALE), diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 90c6304c4bcd..994d199080eb 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -23,6 +23,27 @@ config AD9523 endmenu +menu "Direct Digital Synthesis" + +config AD9910 + tristate "Analog Devices AD9910 Direct Digital Synthesizer" + depends on SPI + depends on GPIOLIB + select CRC32 + select FW_LOADER + select FW_UPLOAD + help + Say yes here to build support for Analog Devices AD9910 + 1 GSPS, 14-Bit DDS with integrated DAC. + + Supports single tone mode with 8 configurable profiles + and digital ramp generation. + + To compile this driver as a module, choose M here: the + module will be called ad9910. + +endmenu + # # Phase-Locked Loop (PLL) frequency synthesizers # diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 53b4d01414d8..e3b1d36ed620 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_AD9910) += ad9910.o obj-$(CONFIG_ADF41513) += adf41513.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c new file mode 100644 index 000000000000..04a4e184b873 --- /dev/null +++ b/drivers/iio/frequency/ad9910.c @@ -0,0 +1,2347 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD9910 SPI DDS (Direct Digital Synthesizer) driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Register addresses */ +#define AD9910_REG_CFR1 0x00 +#define AD9910_REG_CFR2 0x01 +#define AD9910_REG_CFR3 0x02 +#define AD9910_REG_AUX_DAC 0x03 +#define AD9910_REG_IO_UPDATE_RATE 0x04 +#define AD9910_REG_FTW 0x07 +#define AD9910_REG_POW 0x08 +#define AD9910_REG_ASF 0x09 +#define AD9910_REG_MULTICHIP_SYNC 0x0A +#define AD9910_REG_DRG_LIMIT 0x0B +#define AD9910_REG_DRG_STEP 0x0C +#define AD9910_REG_DRG_RATE 0x0D +#define AD9910_REG_PROFILE0 0x0E +#define AD9910_REG_PROFILE1 0x0F +#define AD9910_REG_PROFILE2 0x10 +#define AD9910_REG_PROFILE3 0x11 +#define AD9910_REG_PROFILE4 0x12 +#define AD9910_REG_PROFILE5 0x13 +#define AD9910_REG_PROFILE6 0x14 +#define AD9910_REG_PROFILE7 0x15 +#define AD9910_REG_RAM 0x16 + +#define AD9910_REG_NUM_CACHED 0x16 +#define AD9910_REG_PROFILE(x) (AD9910_REG_PROFILE0 + (x)) + +/* CFR1 bit definitions */ +#define AD9910_CFR1_RAM_ENABLE_MSK BIT(31) +#define AD9910_CFR1_RAM_PLAYBACK_DEST_MSK GENMASK(30, 29) +#define AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK BIT(23) +#define AD9910_CFR1_INV_SINC_EN_MSK BIT(22) +#define AD9910_CFR1_INT_PROFILE_CTL_MSK GENMASK(20, 17) +#define AD9910_CFR1_SELECT_SINE_MSK BIT(16) +#define AD9910_CFR1_LOAD_LRR_IO_UPDATE_MSK BIT(15) +#define AD9910_CFR1_AUTOCLR_DIG_RAMP_ACCUM_MSK BIT(14) +#define AD9910_CFR1_AUTOCLR_PHASE_ACCUM_MSK BIT(13) +#define AD9910_CFR1_CLEAR_DIG_RAMP_ACCUM_MSK BIT(12) +#define AD9910_CFR1_CLEAR_PHASE_ACCUM_MSK BIT(11) +#define AD9910_CFR1_LOAD_ARR_IO_UPDATE_MSK BIT(10) +#define AD9910_CFR1_OSK_ENABLE_MSK BIT(9) +#define AD9910_CFR1_SELECT_AUTO_OSK_MSK BIT(8) +#define AD9910_CFR1_DIGITAL_POWER_DOWN_MSK BIT(7) +#define AD9910_CFR1_DAC_POWER_DOWN_MSK BIT(6) +#define AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK BIT(5) +#define AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK BIT(4) +#define AD9910_CFR1_EXT_POWER_DOWN_CTL_MSK BIT(3) +#define AD9910_CFR1_SDIO_INPUT_ONLY_MSK BIT(1) +#define AD9910_CFR1_LSB_FIRST_MSK BIT(0) + +/* DIGITAL and REFCLK_INPUT powerdown bits are effective without I/O update */ +#define AD9910_CFR1_SW0_POWER_DOWN_MSK (AD9910_CFR1_DIGITAL_POWER_DOWN_MSK | \ + AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK) +/* DAC and AUX_DAC powerdown require I/O update */ +#define AD9910_CFR1_SW1_POWER_DOWN_MSK (AD9910_CFR1_DAC_POWER_DOWN_MSK | \ + AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK) + +#define AD9910_CFR1_SW_POWER_DOWN_MSK (AD9910_CFR1_SW0_POWER_DOWN_MSK | \ + AD9910_CFR1_SW1_POWER_DOWN_MSK) + +/* CFR2 bit definitions */ +#define AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK BIT(24) +#define AD9910_CFR2_INTERNAL_IO_UPDATE_MSK BIT(23) +#define AD9910_CFR2_SYNC_CLK_EN_MSK BIT(22) +#define AD9910_CFR2_DRG_DEST_MSK GENMASK(21, 20) +#define AD9910_CFR2_DRG_ENABLE_MSK BIT(19) +#define AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK BIT(18) +#define AD9910_CFR2_DRG_NO_DWELL_LOW_MSK BIT(17) +#define AD9910_CFR2_DRG_NO_DWELL_MSK GENMASK(18, 17) +#define AD9910_CFR2_READ_EFFECTIVE_FTW_MSK BIT(16) +#define AD9910_CFR2_IO_UPDATE_RATE_CTL_MSK GENMASK(15, 14) +#define AD9910_CFR2_PDCLK_ENABLE_MSK BIT(11) +#define AD9910_CFR2_PDCLK_INVERT_MSK BIT(10) +#define AD9910_CFR2_TXENABLE_INVERT_MSK BIT(9) +#define AD9910_CFR2_MATCHED_LATENCY_EN_MSK BIT(7) +#define AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK BIT(6) +#define AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK BIT(5) +#define AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK BIT(4) +#define AD9910_CFR2_FM_GAIN_MSK GENMASK(3, 0) + +/* CFR3 bit definitions */ +#define AD9910_CFR3_OPEN_MSK (BIT(27) | GENMASK(18, 16)) +#define AD9910_CFR3_DRV0_MSK GENMASK(29, 28) +#define AD9910_CFR3_VCO_SEL_MSK GENMASK(26, 24) +#define AD9910_CFR3_ICP_MSK GENMASK(21, 19) +#define AD9910_CFR3_REFCLK_DIV_BYPASS_MSK BIT(15) +#define AD9910_CFR3_REFCLK_DIV_RESETB_MSK BIT(14) +#define AD9910_CFR3_PFD_RESET_MSK BIT(10) +#define AD9910_CFR3_PLL_EN_MSK BIT(8) +#define AD9910_CFR3_N_MSK GENMASK(7, 1) + +/* Auxiliary DAC Control Register Bits */ +#define AD9910_AUX_DAC_FSC_MSK GENMASK(7, 0) + +/* POW Register Bits */ +#define AD9910_POW_PP_LSB_MSK GENMASK(7, 0) + +/* ASF Register Bits */ +#define AD9910_ASF_RAMP_RATE_MSK GENMASK(31, 16) +#define AD9910_ASF_SCALE_FACTOR_MSK GENMASK(15, 2) +#define AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK GENMASK(7, 2) +#define AD9910_ASF_STEP_SIZE_MSK GENMASK(1, 0) + +/* Multichip Sync Register Bits */ +#define AD9910_MC_SYNC_VALIDATION_DELAY_MSK GENMASK(31, 28) +#define AD9910_MC_SYNC_RECEIVER_ENABLE_MSK BIT(27) +#define AD9910_MC_SYNC_GENERATOR_ENABLE_MSK BIT(26) +#define AD9910_MC_SYNC_GENERATOR_POLARITY_MSK BIT(25) +#define AD9910_MC_SYNC_STATE_PRESET_MSK GENMASK(23, 18) +#define AD9910_MC_SYNC_OUTPUT_DELAY_MSK GENMASK(15, 11) +#define AD9910_MC_SYNC_INPUT_DELAY_MSK GENMASK(7, 3) + +/* Digital Ramp Limit Register */ +#define AD9910_DRG_LIMIT_UPPER_MSK GENMASK_ULL(63, 32) +#define AD9910_DRG_LIMIT_LOWER_MSK GENMASK_ULL(31, 0) + +/* Digital Ramp Step Register */ +#define AD9910_DRG_STEP_DEC_MSK GENMASK_ULL(63, 32) +#define AD9910_DRG_STEP_INC_MSK GENMASK_ULL(31, 0) + +/* Digital Ramp Rate Register */ +#define AD9910_DRG_RATE_DEC_MSK GENMASK(31, 16) +#define AD9910_DRG_RATE_INC_MSK GENMASK(15, 0) + +/* Profile Register Format (Single Tone Mode) */ +#define AD9910_PROFILE_ST_ASF_MSK GENMASK_ULL(61, 48) +#define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32) +#define AD9910_PROFILE_ST_FTW_MSK GENMASK_ULL(31, 0) + +/* Profile Register Format (RAM Mode) */ +#define AD9910_PROFILE_RAM_OPEN_MSK GENMASK_ULL(61, 56) +#define AD9910_PROFILE_RAM_STEP_RATE_MSK GENMASK_ULL(55, 40) +#define AD9910_PROFILE_RAM_END_ADDR_MSK GENMASK_ULL(39, 30) +#define AD9910_PROFILE_RAM_START_ADDR_MSK GENMASK_ULL(23, 14) +#define AD9910_PROFILE_RAM_NO_DWELL_HIGH_MSK BIT_ULL(5) +#define AD9910_PROFILE_RAM_ZERO_CROSSING_MSK BIT_ULL(3) +#define AD9910_PROFILE_RAM_MODE_CONTROL_MSK GENMASK_ULL(2, 0) + +/* Device constants */ +#define AD9910_PI_NANORAD 3141592653UL +#define AD9910_PI_PICORAD 3141592653590ULL + +#define AD9910_MAX_SYSCLK_HZ (1000 * HZ_PER_MHZ) +#define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500) + +#define AD9910_ASF_MAX FIELD_MAX(AD9910_PROFILE_ST_ASF_MSK) +#define AD9910_ASF_PP_LSB_MAX FIELD_MAX(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK) +#define AD9910_POW_MAX FIELD_MAX(AD9910_PROFILE_ST_POW_MSK) +#define AD9910_POW_PP_LSB_MAX FIELD_MAX(AD9910_POW_PP_LSB_MSK) +#define AD9910_STEP_RATE_MAX FIELD_MAX(AD9910_DRG_RATE_DEC_MSK) +#define AD9910_NUM_PROFILES 8 + +#define AD9910_RAM_FW_MAGIC 0x00AD9910 +#define AD9910_RAM_FW_V1 0x0001 +#define AD9910_RAM_SIZE_MAX_WORDS 1024 +#define AD9910_RAM_WORD_SIZE sizeof(u32) +#define AD9910_RAM_SIZE_MAX_BYTES (AD9910_RAM_SIZE_MAX_WORDS * AD9910_RAM_WORD_SIZE) +#define AD9910_RAM_ADDR_MAX (AD9910_RAM_SIZE_MAX_WORDS - 1) + +#define AD9910_RAM_ENABLED(st) \ + FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, (st)->reg[AD9910_REG_CFR1].val32) + +#define AD9910_DEST_FREQUENCY 0 +#define AD9910_DEST_PHASE 1 +#define AD9910_DEST_AMPLITUDE 2 +#define AD9910_DEST_POLAR 3 + +/* PLL constants */ +#define AD9910_PLL_MIN_N 12 +#define AD9910_PLL_MAX_N 127 + +#define AD9910_PLL_IN_MIN_FREQ_HZ (3307 * HZ_PER_KHZ) +#define AD9910_PLL_IN_MAX_FREQ_HZ (60 * HZ_PER_MHZ) + +#define AD9910_PLL_OUT_MIN_FREQ_HZ (420 * HZ_PER_MHZ) +#define AD9910_PLL_OUT_MAX_FREQ_HZ AD9910_MAX_SYSCLK_HZ + +#define AD9910_VCO0_RANGE_AUTO_MAX_HZ (457 * HZ_PER_MHZ) +#define AD9910_VCO1_RANGE_AUTO_MAX_HZ (530 * HZ_PER_MHZ) +#define AD9910_VCO2_RANGE_AUTO_MAX_HZ (632 * HZ_PER_MHZ) +#define AD9910_VCO3_RANGE_AUTO_MAX_HZ (775 * HZ_PER_MHZ) +#define AD9910_VCO4_RANGE_AUTO_MAX_HZ (897 * HZ_PER_MHZ) +#define AD9910_VCO_RANGE_NUM 6 + +#define AD9910_ICP_MIN_uA 212 +#define AD9910_ICP_MAX_uA 387 +#define AD9910_ICP_STEP_uA 25 + +#define AD9910_DAC_IOUT_MAX_uA 31590 +#define AD9910_DAC_IOUT_DEFAULT_uA 20070 +#define AD9910_DAC_IOUT_MIN_uA 8640 + +/* altcurrent ABI is in mA */ +#define AD9910_NANO_MILLIAMP_PER_MICROAMP 1000000UL +#define AD9910_PICO_MILLIAMP_PER_MICROAMP 1000000000UL + +#define AD9910_REFDIV2_MIN_FREQ_HZ (25 * HZ_PER_MHZ) +#define AD9910_REFDIV2_MAX_FREQ_HZ (1900 * HZ_PER_MHZ) + +#define AD9910_WAKEUP_DELAY_us 1000 /* 1ms: datasheet Table 1 */ +#define AD9910_RESET_DELAY_us 1 /* 5 sysclk cycles: < 1us */ + +#define AD9910_SPI_DATA_IDX 1 +#define AD9910_SPI_DATA_LEN_MAX AD9910_RAM_SIZE_MAX_BYTES +#define AD9910_SPI_MESSAGE_LEN_MAX (AD9910_SPI_DATA_IDX + AD9910_SPI_DATA_LEN_MAX) +#define AD9910_SPI_READ_MSK BIT(7) +#define AD9910_SPI_ADDR_MSK GENMASK(4, 0) + +/** + * enum ad9910_channel - AD9910 channel identifiers in priority order + * + * @AD9910_CHANNEL_PHY: Physical output channel + * @AD9910_CHANNEL_PROFILE_0: Profile 0 output channel + * @AD9910_CHANNEL_PROFILE_1: Profile 1 output channel + * @AD9910_CHANNEL_PROFILE_2: Profile 2 output channel + * @AD9910_CHANNEL_PROFILE_3: Profile 3 output channel + * @AD9910_CHANNEL_PROFILE_4: Profile 4 output channel + * @AD9910_CHANNEL_PROFILE_5: Profile 5 output channel + * @AD9910_CHANNEL_PROFILE_6: Profile 6 output channel + * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel + * @AD9910_CHANNEL_PARALLEL: Parallel Data output channel + * @AD9910_CHANNEL_PARALLEL_POLAR: Parallel Polar Data output channel + * @AD9910_CHANNEL_DRG: Digital Ramp Generator output channel + * @AD9910_CHANNEL_DRG_RAMP_UP: DRG ramp up channel + * @AD9910_CHANNEL_DRG_RAMP_DOWN: DRG ramp down channel + * @AD9910_CHANNEL_RAM: RAM control output channel + * @AD9910_CHANNEL_OSK: Output Shift Keying output channel + */ +enum ad9910_channel { + AD9910_CHANNEL_PHY = 100, + AD9910_CHANNEL_PROFILE_0 = 110, + AD9910_CHANNEL_PROFILE_1 = 111, + AD9910_CHANNEL_PROFILE_2 = 112, + AD9910_CHANNEL_PROFILE_3 = 113, + AD9910_CHANNEL_PROFILE_4 = 114, + AD9910_CHANNEL_PROFILE_5 = 115, + AD9910_CHANNEL_PROFILE_6 = 116, + AD9910_CHANNEL_PROFILE_7 = 117, + AD9910_CHANNEL_PARALLEL = 120, + AD9910_CHANNEL_PARALLEL_POLAR = 121, + AD9910_CHANNEL_DRG = 130, + AD9910_CHANNEL_DRG_RAMP_UP = 131, + AD9910_CHANNEL_DRG_RAMP_DOWN = 132, + AD9910_CHANNEL_RAM = 140, + AD9910_CHANNEL_OSK = 150, +}; + +/** + * struct ad9910_ram_fw - AD9910 RAM firmware format + * @magic: Magic number for RAM firmware validation + * @version: Firmware version number + * @wcount: Number of RAM words to be written + * @crc: CRC32 checksum of the RAM data for integrity verification + * @cfr1: Value of CFR1 register to be configured (not all fields are + * used, but this is included here for convenience) + * @profiles: Array of RAM profile configurations + * @words: Array of RAM words to be written. Data pattern should be set in + * reverse order and wcount specifies the number of words in this + * array + */ +struct ad9910_ram_fw { + __be32 magic; + __be16 version; + __be16 wcount; + __be32 crc; + __be32 cfr1; + __be64 profiles[AD9910_NUM_PROFILES]; + __be32 words[] __counted_by_be(wcount); +} __packed; + +enum { + AD9910_CHAN_IDX_PHY, + AD9910_CHAN_IDX_PROFILE_0, + AD9910_CHAN_IDX_PROFILE_1, + AD9910_CHAN_IDX_PROFILE_2, + AD9910_CHAN_IDX_PROFILE_3, + AD9910_CHAN_IDX_PROFILE_4, + AD9910_CHAN_IDX_PROFILE_5, + AD9910_CHAN_IDX_PROFILE_6, + AD9910_CHAN_IDX_PROFILE_7, + AD9910_CHAN_IDX_PARALLEL_AMP, + AD9910_CHAN_IDX_PARALLEL_PHASE, + AD9910_CHAN_IDX_PARALLEL_FREQ, + AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, + AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, + AD9910_CHAN_IDX_DRG_FREQ, + AD9910_CHAN_IDX_DRG_PHASE, + AD9910_CHAN_IDX_DRG_AMP, + AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, + AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, + AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, + AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, + AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, + AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, + AD9910_CHAN_IDX_RAM, + AD9910_CHAN_IDX_OSK, +}; + +enum { + AD9910_POWERDOWN, + AD9910_DWELL_EN, + AD9910_ROC, + AD9910_ROC_AVAIL, +}; + +struct ad9910_data { + u32 sysclk_freq_hz; + u32 output_current_uA; + + u16 pll_charge_pump_current; + u8 refclk_out_drv; + bool pll_enabled; +}; + +union ad9910_reg { + u64 val64; + u32 val32; + u16 val16; +}; + +struct ad9910_state { + struct spi_device *spi; + struct clk *refclk; + struct fw_upload *ram_fwu; + + struct gpio_desc *gpio_pwdown; + struct gpio_desc *gpio_update; + struct gpio_descs *gpio_profile; + + /* cached registers */ + union ad9910_reg reg[AD9910_REG_NUM_CACHED]; + + /* + * alternate profile registers used to store RAM profile settings when + * RAM mode is disabled and Single Tone profile settings when RAM mode + * is enabled. + */ + u64 reg_profile[AD9910_NUM_PROFILES]; + + /* Lock for accessing device registers and state variables */ + struct mutex lock; + + struct ad9910_data data; + u8 profile; + + bool ram_fwu_cancel; + char ram_fwu_name[20]; + + /* + * RAM loading requires a reasonable amount of bytes, at the same time + * DMA capable SPI drivers requires the transfer buffers to live in + * their own cache lines. + */ + u8 tx_buf[AD9910_SPI_MESSAGE_LEN_MAX] __aligned(IIO_DMA_MINALIGN); +}; + +/** + * ad9910_rational_scale() - Perform scaling of input given a reference. + * @input: The input value to be scaled. + * @scale: The numerator of the scaling factor. + * @reference: The denominator of the scaling factor. + * + * Closest rounding with mul_u64_add_u64_div_u64 + * + * Return: The scaled value. + */ +static u64 ad9910_rational_scale(u64 input, u64 scale, u64 reference) +{ + return mul_u64_add_u64_div_u64(input, scale, reference >> 1, reference); +} + +static inline u64 ad9910_ram_profile_val(struct ad9910_state *st) +{ + if (AD9910_RAM_ENABLED(st)) + return st->reg[AD9910_REG_PROFILE(st->profile)].val64; + else + return st->reg_profile[st->profile]; +} + +static inline u64 ad9910_st_profile_val(struct ad9910_state *st, u8 profile) +{ + if (AD9910_RAM_ENABLED(st)) + return st->reg_profile[profile]; + else + return st->reg[AD9910_REG_PROFILE(profile)].val64; +} + +static int ad9910_io_update(struct ad9910_state *st) +{ + if (st->gpio_update) { + gpiod_set_value_cansleep(st->gpio_update, 1); + fsleep(1); + gpiod_set_value_cansleep(st->gpio_update, 0); + } + + return 0; +} + +static int ad9910_spi_read(struct ad9910_state *st, u8 reg, void *data, + size_t len) +{ + u8 inst = AD9910_SPI_READ_MSK | FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); + + return spi_write_then_read(st->spi, &inst, sizeof(inst), data, len); +} + +static int ad9910_spi_write(struct ad9910_state *st, u8 reg, size_t len, + bool update) +{ + int ret; + + st->tx_buf[0] = FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); + ret = spi_write(st->spi, st->tx_buf, AD9910_SPI_DATA_IDX + len); + if (ret) + return ret; + + if (update) + return ad9910_io_update(st); + + return 0; +} + +#define AD9910_REG_READ_FN(nb) \ +static int ad9910_reg##nb##_read(struct ad9910_state *st, u8 reg, \ + u##nb * data) \ +{ \ + __be##nb be_data; \ + int ret; \ + \ + ret = ad9910_spi_read(st, reg, &be_data, sizeof(be_data)); \ + if (ret) \ + return ret; \ + \ + *data = be##nb##_to_cpu(be_data); \ + return ret; \ +} + +AD9910_REG_READ_FN(16) +AD9910_REG_READ_FN(32) +AD9910_REG_READ_FN(64) + +#define AD9910_REG_WRITE_FN(nb) \ +static int ad9910_reg##nb##_write(struct ad9910_state *st, u8 reg, \ + u##nb data, bool update) \ +{ \ + int ret; \ + \ + put_unaligned_be##nb(data, &st->tx_buf[AD9910_SPI_DATA_IDX]); \ + ret = ad9910_spi_write(st, reg, sizeof(data), update); \ + if (ret) \ + return ret; \ + \ + st->reg[reg].val##nb = data; \ + return ret; \ +} + +AD9910_REG_WRITE_FN(16) +AD9910_REG_WRITE_FN(32) +AD9910_REG_WRITE_FN(64) + +#define AD9910_REG_UPDATE_FN(nb) \ +static int ad9910_reg##nb##_update(struct ad9910_state *st, \ + u8 reg, u##nb mask, \ + u##nb data, bool update) \ +{ \ + u##nb reg_val = (st->reg[reg].val##nb & ~mask) | (data & mask); \ + \ + if (reg_val == st->reg[reg].val##nb && !update) \ + return 0; \ + \ + return ad9910_reg##nb##_write(st, reg, reg_val, update); \ +} + +AD9910_REG_UPDATE_FN(16) +AD9910_REG_UPDATE_FN(32) +AD9910_REG_UPDATE_FN(64) + +static int ad9910_set_dac_current(struct ad9910_state *st, u32 val_uA, + bool update) +{ + u32 code; + + /* FSC = (86.4 / Rset) * (1 + CODE/96) where Rset = 10k ohms */ + val_uA = clamp(val_uA, AD9910_DAC_IOUT_MIN_uA, AD9910_DAC_IOUT_MAX_uA); + code = DIV_ROUND_CLOSEST(val_uA - AD9910_DAC_IOUT_MIN_uA, 90); + st->data.output_current_uA = AD9910_DAC_IOUT_MIN_uA + code * 90; + + return ad9910_reg32_write(st, AD9910_REG_AUX_DAC, code, update); +} + +static int ad9910_set_sysclk_freq(struct ad9910_state *st, u32 freq_hz, + bool update) +{ + struct device *dev = &st->spi->dev; + unsigned long refclk_freq_hz; + u32 sysclk_freq_hz; + u32 tmp32, vco_sel; + int ret; + + if (!freq_hz || freq_hz > AD9910_MAX_SYSCLK_HZ) + return -EINVAL; + + refclk_freq_hz = clk_get_rate(st->refclk); + if (st->data.pll_enabled) { + if (refclk_freq_hz < AD9910_PLL_IN_MIN_FREQ_HZ || + refclk_freq_hz > AD9910_PLL_IN_MAX_FREQ_HZ) { + dev_err(dev, + "REF_CLK freq %lu Hz is out of PLL input range\n", + refclk_freq_hz); + return -ERANGE; + } + + tmp32 = DIV_ROUND_CLOSEST(freq_hz, refclk_freq_hz); + tmp32 = clamp(tmp32, DIV_ROUND_UP(AD9910_PLL_OUT_MIN_FREQ_HZ, refclk_freq_hz), + AD9910_PLL_OUT_MAX_FREQ_HZ / refclk_freq_hz); + tmp32 = clamp(tmp32, AD9910_PLL_MIN_N, AD9910_PLL_MAX_N); + sysclk_freq_hz = refclk_freq_hz * tmp32; + + if (sysclk_freq_hz <= AD9910_VCO0_RANGE_AUTO_MAX_HZ) + vco_sel = 0; + else if (sysclk_freq_hz <= AD9910_VCO1_RANGE_AUTO_MAX_HZ) + vco_sel = 1; + else if (sysclk_freq_hz <= AD9910_VCO2_RANGE_AUTO_MAX_HZ) + vco_sel = 2; + else if (sysclk_freq_hz <= AD9910_VCO3_RANGE_AUTO_MAX_HZ) + vco_sel = 3; + else if (sysclk_freq_hz <= AD9910_VCO4_RANGE_AUTO_MAX_HZ) + vco_sel = 4; + else + vco_sel = 5; + + ret = ad9910_reg32_update(st, AD9910_REG_CFR3, + AD9910_CFR3_N_MSK | AD9910_CFR3_VCO_SEL_MSK, + FIELD_PREP(AD9910_CFR3_N_MSK, tmp32) | + FIELD_PREP(AD9910_CFR3_VCO_SEL_MSK, vco_sel), + update); + if (ret) + return ret; + } else { + if (refclk_freq_hz < AD9910_REFDIV2_MIN_FREQ_HZ || + refclk_freq_hz > AD9910_REFDIV2_MAX_FREQ_HZ) { + dev_err(dev, + "REF_CLK freq %lu Hz is out of divider range\n", + refclk_freq_hz); + return -ERANGE; + } + + tmp32 = DIV_ROUND_CLOSEST(refclk_freq_hz, freq_hz); + tmp32 = clamp(tmp32, 1U, 2U); + sysclk_freq_hz = refclk_freq_hz / tmp32; + tmp32 = AD9910_CFR3_VCO_SEL_MSK | + FIELD_PREP(AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, tmp32 % 2); + ret = ad9910_reg32_update(st, AD9910_REG_CFR3, + AD9910_CFR3_VCO_SEL_MSK | + AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, + tmp32, update); + if (ret) + return ret; + } + + st->data.sysclk_freq_hz = sysclk_freq_hz; + + return 0; +} + +static int ad9910_profile_set(struct ad9910_state *st, u8 profile) +{ + DECLARE_BITMAP(values, BITS_PER_TYPE(profile)); + + st->profile = profile; + values[0] = profile; + gpiod_multi_set_value_cansleep(st->gpio_profile, values); + + return 0; +} + +static bool ad9910_sw_powerdown_get(struct ad9910_state *st) +{ + return FIELD_GET(AD9910_CFR1_SW_POWER_DOWN_MSK, + st->reg[AD9910_REG_CFR1].val32) ? true : false; +} + +static int ad9910_sw_powerdown_set(struct ad9910_state *st, bool enable) +{ + int ret; + + if (ad9910_sw_powerdown_get(st) == enable) + return 0; + + /* + * When powering down, the DAC and AUX_DAC (SW1) must be powered down + * first, as they require an I/O update to take effect. The opposite is + * true when powering up, the DAC and AUX_DAC must be powered up last, + * i.e., after the DIGITAL and REFCLK_INPUT (SW0) power up. + */ + if (enable) { + ret = ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW1_POWER_DOWN_MSK, + AD9910_CFR1_SW1_POWER_DOWN_MSK, + true); + if (ret) + return ret; + + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW0_POWER_DOWN_MSK, + AD9910_CFR1_SW0_POWER_DOWN_MSK, + false); + } + + ret = ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW0_POWER_DOWN_MSK, 0, false); + if (ret) + return ret; + + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW1_POWER_DOWN_MSK, 0, true); +} + +static ssize_t ad9910_ext_info_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st = iio_priv(indio_dev); + int val; + + guard(mutex)(&st->lock); + + switch (private) { + case AD9910_POWERDOWN: + val = ad9910_sw_powerdown_get(st); + break; + case AD9910_DWELL_EN: + if (chan->channel == AD9910_CHANNEL_DRG_RAMP_UP) + val = FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, + st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; + else + val = FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, + st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; + break; + default: + return -EINVAL; + } + + return iio_format_value(buf, IIO_VAL_INT, 1, &val); +} + +static ssize_t ad9910_ext_info_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u32 val32; + int ret; + + ret = kstrtou32(buf, 10, &val32); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + switch (private) { + case AD9910_POWERDOWN: + ret = ad9910_sw_powerdown_set(st, val32 ? true : false); + if (ret) + return ret; + break; + case AD9910_DWELL_EN: + if (chan->channel == AD9910_CHANNEL_DRG_RAMP_UP) { + val32 = val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK; + ret = ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, + val32, true); + if (ret) + return ret; + } else { + val32 = val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_LOW_MSK; + ret = ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, + val32, true); + if (ret) + return ret; + } + break; + default: + return -EINVAL; + } + + return len; +} + +static ssize_t ad9910_drg_roc_read(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u64 roc64; + u32 rate; + + guard(mutex)(&st->lock); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + roc64 = FIELD_GET(AD9910_DRG_STEP_INC_MSK, + st->reg[AD9910_REG_DRG_STEP].val64); + rate = FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + roc64 = FIELD_GET(AD9910_DRG_STEP_DEC_MSK, + st->reg[AD9910_REG_DRG_STEP].val64); + rate = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + default: + return -EINVAL; + } + + if (!rate) + return -ERANGE; + + roc64 *= st->data.sysclk_freq_hz; + return sysfs_emit(buf, "%llu\n", div_u64(roc64, 4 * rate)); +} + +static ssize_t ad9910_drg_roc_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u64 tmp64; + u32 rate; + int ret; + + ret = kstrtou64(buf, 10, &tmp64); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + rate = FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + if (!rate) + return -ERANGE; + + tmp64 = ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz); + tmp64 = min_t(u64, tmp64, U32_MAX); + + ret = ad9910_reg64_update(st, AD9910_REG_DRG_STEP, + AD9910_DRG_STEP_INC_MSK, + FIELD_PREP(AD9910_DRG_STEP_INC_MSK, tmp64), + true); + if (ret) + return ret; + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + rate = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + if (!rate) + return -ERANGE; + + tmp64 = ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz); + tmp64 = min_t(u64, tmp64, U32_MAX); + + ret = ad9910_reg64_update(st, AD9910_REG_DRG_STEP, + AD9910_DRG_STEP_DEC_MSK, + FIELD_PREP(AD9910_DRG_STEP_DEC_MSK, tmp64), + true); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return len; +} + +static const u32 ad9910_osk_raw_step[] = { + 0, /* no step: manual mode (NOT pin controlled) */ + 1, /* step size 1: automatic mode (pin controlled) */ + 2, /* step size 2: automatic mode (pin controlled) */ + 4, /* step size 4: automatic mode (pin controlled) */ + 8, /* step size 8: automatic mode (pin controlled) */ + GENMASK(13, 0), /* max step: manual mode (pin controlled) */ +}; + +static ssize_t ad9910_osk_attrs_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st = iio_priv(indio_dev); + bool auto_en, pinctrl_en; + u32 rate, step; + u64 roc64; + + guard(mutex)(&st->lock); + + rate = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val32); + if (!rate) + return -ERANGE; + + switch (private) { + case AD9910_ROC: + auto_en = FIELD_GET(AD9910_CFR1_SELECT_AUTO_OSK_MSK, + st->reg[AD9910_REG_CFR1].val32); + pinctrl_en = FIELD_GET(AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (auto_en) { + step = FIELD_GET(AD9910_ASF_STEP_SIZE_MSK, + st->reg[AD9910_REG_ASF].val32); + step = ad9910_osk_raw_step[step + 1]; + } else if (pinctrl_en) { + step = ad9910_osk_raw_step[ARRAY_SIZE(ad9910_osk_raw_step) - 1]; + } else { + step = ad9910_osk_raw_step[0]; + } + + roc64 = div_u64((u64)step * st->data.sysclk_freq_hz, 4 * rate); + return sysfs_emit(buf, "%llu\n", roc64); + case AD9910_ROC_AVAIL: { + ssize_t len = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(ad9910_osk_raw_step); i++) { + roc64 = div_u64((u64)ad9910_osk_raw_step[i] * st->data.sysclk_freq_hz, + 4 * rate); + len += sysfs_emit_at(buf, len, "%llu ", roc64); + } + + buf[len - 1] = '\n'; /* replace last space with a newline */ + return len; + } + default: + return -EINVAL; + } +} + +static ssize_t ad9910_osk_attrs_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u32 idx, reg_val, rate; + u64 step; + int ret; + + ret = kstrtou64(buf, 10, &step); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + rate = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val32); + if (!rate) + return -ERANGE; + + switch (private) { + case AD9910_ROC: + step = ad9910_rational_scale(step, 4 * rate, + st->data.sysclk_freq_hz); + step = min(step, AD9910_ASF_MAX); + idx = find_closest(step, ad9910_osk_raw_step, + ARRAY_SIZE(ad9910_osk_raw_step)); + if (idx == ARRAY_SIZE(ad9910_osk_raw_step) - 1) { + /* manual mode: pin-controlled */ + reg_val = AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK; + } else if (idx == 0) { + /* manual mode, NOT pin-controlled */ + reg_val = 0; + } else { + /* auto mode: pin-controlled */ + reg_val = FIELD_PREP(AD9910_ASF_STEP_SIZE_MSK, idx - 1); + ret = ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_STEP_SIZE_MSK, + reg_val, false); + if (ret) + return ret; + + reg_val = AD9910_CFR1_SELECT_AUTO_OSK_MSK; + } + + ret = ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SELECT_AUTO_OSK_MSK | + AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, + reg_val, true); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return len; +} + +static const struct iio_chan_spec_ext_info ad9910_phy_ext_info[] = { + { + .name = "powerdown", + .read = ad9910_ext_info_read, + .write = ad9910_ext_info_write, + .private = AD9910_POWERDOWN, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_chan_spec_ext_info ad9910_drg_ramp_ext_info[] = { + { + .name = "dwell_en", + .read = ad9910_ext_info_read, + .write = ad9910_ext_info_write, + .private = AD9910_DWELL_EN, + .shared = IIO_SEPARATE, + }, + { + .name = "raw_roc", + .read = ad9910_drg_roc_read, + .write = ad9910_drg_roc_write, + .private = AD9910_ROC, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_chan_spec_ext_info ad9910_osk_ext_info[] = { + { + .name = "raw_roc", + .read = ad9910_osk_attrs_read, + .write = ad9910_osk_attrs_write, + .private = AD9910_ROC, + .shared = IIO_SEPARATE, + }, + { + .name = "raw_roc_available", + .read = ad9910_osk_attrs_read, + .private = AD9910_ROC_AVAIL, + .shared = IIO_SEPARATE, + }, + { } +}; + +#define AD9910_PROFILE_CHAN(idx) { \ + .type = IIO_ALTCURRENT, \ + .indexed = 1, \ + .output = 1, \ + .channel = AD9910_CHANNEL_PROFILE_ ## idx, \ + .address = AD9910_CHAN_IDX_PROFILE_ ## idx, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | \ + BIT(IIO_CHAN_INFO_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_PHASE) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], \ +} + +static const struct iio_chan_spec ad9910_channels[] = { + [AD9910_CHAN_IDX_PHY] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PHY, + .address = AD9910_CHAN_IDX_PHY, + .info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .ext_info = ad9910_phy_ext_info, + }, + [AD9910_CHAN_IDX_PROFILE_0] = AD9910_PROFILE_CHAN(0), + [AD9910_CHAN_IDX_PROFILE_1] = AD9910_PROFILE_CHAN(1), + [AD9910_CHAN_IDX_PROFILE_2] = AD9910_PROFILE_CHAN(2), + [AD9910_CHAN_IDX_PROFILE_3] = AD9910_PROFILE_CHAN(3), + [AD9910_CHAN_IDX_PROFILE_4] = AD9910_PROFILE_CHAN(4), + [AD9910_CHAN_IDX_PROFILE_5] = AD9910_PROFILE_CHAN(5), + [AD9910_CHAN_IDX_PROFILE_6] = AD9910_PROFILE_CHAN(6), + [AD9910_CHAN_IDX_PROFILE_7] = AD9910_PROFILE_CHAN(7), + [AD9910_CHAN_IDX_PARALLEL_AMP] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PARALLEL, + .address = AD9910_CHAN_IDX_PARALLEL_AMP, + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_PHASE] = { + .type = IIO_PHASE, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PARALLEL, + .address = AD9910_CHAN_IDX_PARALLEL_PHASE, + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_FREQ] = { + .type = IIO_FREQUENCY, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PARALLEL, + .address = AD9910_CHAN_IDX_PARALLEL_FREQ, + .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PARALLEL_POLAR, + .address = AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] = { + .type = IIO_PHASE, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_PARALLEL_POLAR, + .address = AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, + .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_FREQ] = { + .type = IIO_FREQUENCY, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG, + .address = AD9910_CHAN_IDX_DRG_FREQ, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_PHASE] = { + .type = IIO_PHASE, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG, + .address = AD9910_CHAN_IDX_DRG_PHASE, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_AMP] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG, + .address = AD9910_CHAN_IDX_DRG_AMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] = { + .type = IIO_FREQUENCY, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_UP, + .address = AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], + }, + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] = { + .type = IIO_FREQUENCY, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, + .address = AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], + }, + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] = { + .type = IIO_PHASE, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_UP, + .address = AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], + }, + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] = { + .type = IIO_PHASE, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, + .address = AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], + }, + [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_UP, + .address = AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], + }, + [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, + .address = AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_drg_ramp_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], + }, + [AD9910_CHAN_IDX_RAM] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_RAM, + .address = AD9910_CHAN_IDX_RAM, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_FREQUENCY) | + BIT(IIO_CHAN_INFO_PHASE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_OSK] = { + .type = IIO_ALTCURRENT, + .indexed = 1, + .output = 1, + .channel = AD9910_CHANNEL_OSK, + .address = AD9910_CHAN_IDX_OSK, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad9910_osk_ext_info, + .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, +}; + +static int ad9910_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u64 tmp64; + u32 tmp32; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_ENABLE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + if (ad9910_sw_powerdown_get(st)) { + *val = 0; + } else { + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + *val = (tmp32 == st->profile); + } + break; + case AD9910_CHANNEL_DRG: + tmp32 = FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (tmp32 == (chan->address - AD9910_CHAN_IDX_DRG_FREQ)) + *val = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + else + *val = 0; + break; + case AD9910_CHANNEL_RAM: + *val = FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + break; + case AD9910_CHANNEL_OSK: + *val = FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + break; + default: + return -EINVAL; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 = FIELD_GET(AD9910_PROFILE_ST_FTW_MSK, + ad9910_st_profile_val(st, tmp32)); + break; + case AD9910_CHANNEL_RAM: + tmp64 = st->reg[AD9910_REG_FTW].val32; + break; + default: + return -EINVAL; + } + tmp64 *= st->data.sysclk_freq_hz; + *val = tmp64 >> 32; + *val2 = ((tmp64 & GENMASK_ULL(31, 0)) * MICRO) >> 32; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_PHASE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 = FIELD_GET(AD9910_PROFILE_ST_POW_MSK, + ad9910_st_profile_val(st, tmp32)); + break; + case AD9910_CHANNEL_RAM: + tmp64 = st->reg[AD9910_REG_POW].val16; + break; + default: + return -EINVAL; + } + tmp32 = (tmp64 * AD9910_MAX_PHASE_MICRORAD) >> 16; + *val = tmp32 / MICRO; + *val2 = tmp32 % MICRO; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + *val = FIELD_GET(AD9910_PROFILE_ST_ASF_MSK, + ad9910_st_profile_val(st, tmp32)); + return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp64 = FIELD_GET(AD9910_DRG_LIMIT_UPPER_MSK, + st->reg[AD9910_REG_DRG_LIMIT].val64); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_INT_64; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp64 = FIELD_GET(AD9910_DRG_LIMIT_LOWER_MSK, + st->reg[AD9910_REG_DRG_LIMIT].val64); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_INT_64; + case AD9910_CHANNEL_OSK: + *val = FIELD_GET(AD9910_ASF_SCALE_FACTOR_MSK, + st->reg[AD9910_REG_ASF].val32); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->channel) { + case AD9910_CHANNEL_PHY: + *val = st->data.sysclk_freq_hz; + return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp32 = FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp32 = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + case AD9910_CHANNEL_RAM: + tmp32 = FIELD_GET(AD9910_PROFILE_RAM_STEP_RATE_MSK, + ad9910_ram_profile_val(st)); + break; + case AD9910_CHANNEL_OSK: + tmp32 = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, + st->reg[AD9910_REG_ASF].val32); + break; + default: + return -EINVAL; + } + if (!tmp32) + return -ERANGE; + tmp32 *= 4; + *val = st->data.sysclk_freq_hz / tmp32; + *val2 = div_u64((u64)(st->data.sysclk_freq_hz % tmp32) * MICRO, tmp32); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case AD9910_CHAN_IDX_PHY: + tmp64 = (u64)st->data.output_current_uA * + AD9910_NANO_MILLIAMP_PER_MICROAMP; + *val = 0; + *val2 = tmp64 >> 14; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_PHASE: + *val = 0; + *val2 = AD9910_PI_NANORAD >> 15; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp32 = FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32); + tmp64 = (u64)st->data.sysclk_freq_hz << tmp32; + tmp64 = ad9910_rational_scale(tmp64, NANO, BIT_ULL(32)); + *val = div_s64_rem(tmp64, NANO, val2); + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp64 = (u64)st->data.output_current_uA * + AD9910_NANO_MILLIAMP_PER_MICROAMP; + *val = 0; + *val2 = tmp64 >> 8; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + *val = 0; + *val2 = AD9910_PI_NANORAD >> 7; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_DRG_FREQ: + tmp64 = ad9910_rational_scale(st->data.sysclk_freq_hz, + PICO, BIT_ULL(32)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; + case AD9910_CHAN_IDX_DRG_PHASE: + tmp64 = DIV_U64_ROUND_CLOSEST(AD9910_PI_PICORAD, BIT(31)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; + case AD9910_CHAN_IDX_DRG_AMP: + tmp64 = (u64)st->data.output_current_uA * + AD9910_PICO_MILLIAMP_PER_MICROAMP; + tmp64 = DIV_U64_ROUND_CLOSEST(tmp64 >> 1, BIT(31)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->address) { + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp64 = (u64)st->reg[AD9910_REG_FTW].val32 * MICRO; + tmp64 >>= FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp32 = FIELD_GET(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, + st->reg[AD9910_REG_ASF].val32); + iio_val_s64_decompose(MICRO * tmp32 >> 6, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + tmp32 = FIELD_GET(AD9910_POW_PP_LSB_MSK, + st->reg[AD9910_REG_POW].val16); + iio_val_s64_decompose(MICRO * tmp32 >> 8, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad9910_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad9910_state *st = iio_priv(indio_dev); + u64 tmp64; + u32 tmp32; + int ret, i; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_ENABLE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + if (!val) { + if (tmp32 != st->profile) + return 0; /* nothing to do */ + + return ad9910_sw_powerdown_set(st, true); + } + + ret = ad9910_sw_powerdown_set(st, false); + if (ret) + return ret; + + return ad9910_profile_set(st, tmp32); + case AD9910_CHANNEL_DRG: + tmp32 = chan->address - AD9910_CHAN_IDX_DRG_FREQ; + if (val) { + tmp32 = AD9910_CFR2_DRG_ENABLE_MSK | + FIELD_PREP(AD9910_CFR2_DRG_DEST_MSK, tmp32); + } else { + if (tmp32 != FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return 0; /* nothing to do */ + tmp32 = 0; + } + + return ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_DEST_MSK | + AD9910_CFR2_DRG_ENABLE_MSK, + tmp32, true); + case AD9910_CHANNEL_RAM: + if (AD9910_RAM_ENABLED(st) == !!val) + return 0; + + /* swap profile configs */ + for (i = 0; i < AD9910_NUM_PROFILES; i++) { + tmp64 = st->reg[AD9910_REG_PROFILE(i)].val64; + ret = ad9910_reg64_write(st, + AD9910_REG_PROFILE(i), + st->reg_profile[i], + false); + if (ret) + break; + st->reg_profile[i] = tmp64; + } + + if (ret) { + /* + * After the write failure, profiles 0..i-1 were + * already swapped in SW, but Hw registers are + * still pending an IO update, so swap them back + * in SW to keep the state consistent. + */ + while (i--) { + tmp64 = st->reg[AD9910_REG_PROFILE(i)].val64; + st->reg[AD9910_REG_PROFILE(i)].val64 = st->reg_profile[i]; + st->reg_profile[i] = tmp64; + } + return ret; + } + + tmp32 = FIELD_PREP(AD9910_CFR1_RAM_ENABLE_MSK, !!val); + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_RAM_ENABLE_MSK, + tmp32, true); + case AD9910_CHANNEL_OSK: + tmp32 = FIELD_PREP(AD9910_CFR1_OSK_ENABLE_MSK, !!val); + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_OSK_ENABLE_MSK, + tmp32, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_FREQUENCY: + if (val < 0 || val2 < 0 || val >= st->data.sysclk_freq_hz / 2) + return -EINVAL; + + tmp64 = ad9910_rational_scale((u64)val * MICRO + val2, BIT_ULL(32), + (u64)MICRO * st->data.sysclk_freq_hz); + tmp64 = min_t(u64, tmp64, U32_MAX); + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_FTW_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } + tmp64 = FIELD_PREP(AD9910_PROFILE_ST_FTW_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_FTW_MSK, + tmp64, true); + case AD9910_CHANNEL_RAM: + return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_PHASE: + if (val < 0 || val2 < 0) + return -EINVAL; + + tmp64 = (u64)val * MICRO + val2; + if (tmp64 >= AD9910_MAX_PHASE_MICRORAD) + return -EINVAL; + + tmp64 = DIV_U64_ROUND_CLOSEST(tmp64 << 16, AD9910_MAX_PHASE_MICRORAD); + tmp64 = min(tmp64, AD9910_POW_MAX); + + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_POW_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } + + tmp64 = FIELD_PREP(AD9910_PROFILE_ST_POW_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_POW_MSK, + tmp64, true); + case AD9910_CHANNEL_RAM: + return ad9910_reg16_write(st, AD9910_REG_POW, tmp64, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + if (val < 0) + return -EINVAL; + + tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 = min_t(u64, val, AD9910_ASF_MAX); + + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_ASF_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } + + tmp64 = FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_ASF_MSK, + tmp64, true); + case AD9910_CHANNEL_DRG_RAMP_UP: { + s64 val64 = iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 = min_t(u64, val64, U32_MAX); + tmp64 = FIELD_PREP(AD9910_DRG_LIMIT_UPPER_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, + AD9910_DRG_LIMIT_UPPER_MSK, + tmp64, true); + } + case AD9910_CHANNEL_DRG_RAMP_DOWN: { + s64 val64 = iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 = min_t(u64, val64, U32_MAX); + tmp64 = FIELD_PREP(AD9910_DRG_LIMIT_LOWER_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, + AD9910_DRG_LIMIT_LOWER_MSK, + tmp64, true); + } + case AD9910_CHANNEL_OSK: + if (val < 0) + return -EINVAL; + + tmp32 = min_t(u32, val, AD9910_ASF_MAX); + tmp32 = FIELD_PREP(AD9910_ASF_SCALE_FACTOR_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_SCALE_FACTOR_MSK, + tmp32, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->channel == AD9910_CHANNEL_PHY) + return ad9910_set_sysclk_freq(st, val, true); + + if (val < 0 || val2 < 0 || val > st->data.sysclk_freq_hz / 4) + return -EINVAL; + + tmp64 = ((u64)val * MICRO + val2) * 4; + if (!tmp64) + return -EINVAL; + + tmp64 = DIV64_U64_ROUND_CLOSEST((u64)st->data.sysclk_freq_hz * MICRO, tmp64); + tmp32 = clamp(tmp64, 1U, AD9910_STEP_RATE_MAX); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp32 = FIELD_PREP(AD9910_DRG_RATE_INC_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, + AD9910_DRG_RATE_INC_MSK, + tmp32, true); + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp32 = FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, + AD9910_DRG_RATE_DEC_MSK, + tmp32, true); + case AD9910_CHANNEL_RAM: + if (!AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_RAM_STEP_RATE_MSK, + &st->reg_profile[st->profile], tmp32); + return 0; + } + + tmp64 = FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, tmp32); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(st->profile), + AD9910_PROFILE_RAM_STEP_RATE_MSK, + tmp64, true); + case AD9910_CHANNEL_OSK: + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_RAMP_RATE_MSK, + FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, tmp32), + true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case AD9910_CHAN_IDX_PHY: + if (val != 0 || val2 < 0) + return -EINVAL; + + tmp32 = DIV_U64_ROUND_CLOSEST((u64)val2 << 14, + AD9910_NANO_MILLIAMP_PER_MICROAMP); + return ad9910_set_dac_current(st, tmp32, true); + case AD9910_CHAN_IDX_PARALLEL_FREQ: + if (val < 0 || val2 < 0) + return -EINVAL; + + tmp64 = ad9910_rational_scale((u64)val * NANO + val2, BIT_ULL(32), + (u64)st->data.sysclk_freq_hz * NANO); + tmp64 = roundup_pow_of_two(clamp(tmp64, 1ULL, BIT_ULL(15))); + tmp32 = FIELD_PREP(AD9910_CFR2_FM_GAIN_MSK, ilog2(tmp64)); + return ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_FM_GAIN_MSK, + tmp32, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: { + s64 val64 = iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 = val64; + switch (chan->address) { + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp32 = BIT(FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32)); + tmp64 = ad9910_rational_scale(tmp64, tmp32, MICRO); + tmp64 = min_t(u64, tmp64, U32_MAX); + return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp64 = ad9910_rational_scale(tmp64, BIT(6), MICRO); + tmp64 = min_t(u64, tmp64, AD9910_ASF_PP_LSB_MAX); + tmp32 = FIELD_PREP(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, tmp64); + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, + tmp32, true); + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + tmp64 = ad9910_rational_scale(tmp64, BIT(8), MICRO); + tmp64 = min_t(u64, tmp64, AD9910_POW_PP_LSB_MAX); + tmp32 = FIELD_PREP(AD9910_POW_PP_LSB_MSK, tmp64); + return ad9910_reg16_update(st, AD9910_REG_POW, + AD9910_POW_PP_LSB_MSK, + tmp32, true); + default: + return -EINVAL; + } + } + default: + return -EINVAL; + } +} + +static int ad9910_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_ENABLE: + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + case IIO_CHAN_INFO_PHASE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + case AD9910_CHANNEL_OSK: + return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + case AD9910_CHANNEL_DRG_RAMP_DOWN: + return IIO_VAL_INT_64; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->channel == AD9910_CHANNEL_PHY) + return IIO_VAL_INT; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int ad9910_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, u64 writeval, + u64 *readval) +{ + struct ad9910_state *st = iio_priv(indio_dev); + union ad9910_reg tmp; + int ret; + + if (reg >= AD9910_REG_RAM) + return -EINVAL; + + guard(mutex)(&st->lock); + + switch (reg) { + case AD9910_REG_DRG_LIMIT: + case AD9910_REG_DRG_STEP: + case AD9910_REG_PROFILE0 ... AD9910_REG_PROFILE7: + if (!readval) + return ad9910_reg64_write(st, reg, writeval, true); + + ret = ad9910_reg64_read(st, reg, &tmp.val64); + if (ret) + return ret; + *readval = tmp.val64; + return 0; + case AD9910_REG_POW: + if (!readval) + return ad9910_reg16_write(st, reg, writeval, true); + + ret = ad9910_reg16_read(st, reg, &tmp.val16); + if (ret) + return ret; + *readval = tmp.val16; + return 0; + default: + if (!readval) + return ad9910_reg32_write(st, reg, writeval, true); + + ret = ad9910_reg32_read(st, reg, &tmp.val32); + if (ret) + return ret; + *readval = tmp.val32; + return 0; + } +} + +static const char * const ad9910_channel_str[] = { + [AD9910_CHAN_IDX_PHY] = "phy", + [AD9910_CHAN_IDX_PROFILE_0] = "profile0", + [AD9910_CHAN_IDX_PROFILE_1] = "profile1", + [AD9910_CHAN_IDX_PROFILE_2] = "profile2", + [AD9910_CHAN_IDX_PROFILE_3] = "profile3", + [AD9910_CHAN_IDX_PROFILE_4] = "profile4", + [AD9910_CHAN_IDX_PROFILE_5] = "profile5", + [AD9910_CHAN_IDX_PROFILE_6] = "profile6", + [AD9910_CHAN_IDX_PROFILE_7] = "profile7", + [AD9910_CHAN_IDX_PARALLEL_AMP] = "parallel_amplitude", + [AD9910_CHAN_IDX_PARALLEL_PHASE] = "parallel_phase", + [AD9910_CHAN_IDX_PARALLEL_FREQ] = "parallel_frequency", + [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] = "parallel_polar_amplitude", + [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] = "parallel_polar_phase", + [AD9910_CHAN_IDX_DRG_FREQ] = "drg_frequency", + [AD9910_CHAN_IDX_DRG_PHASE] = "drg_phase", + [AD9910_CHAN_IDX_DRG_AMP] = "drg_amplitude", + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] = "drg_rising_frequency", + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] = "drg_falling_frequency", + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] = "drg_rising_phase", + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] = "drg_falling_phase", + [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] = "drg_rising_amplitude", + [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] = "drg_falling_amplitude", + [AD9910_CHAN_IDX_RAM] = "ram", + [AD9910_CHAN_IDX_OSK] = "osk", +}; + +static int ad9910_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", ad9910_channel_str[chan->address]); +} + +static enum fw_upload_err ad9910_ram_fwu_prepare(struct fw_upload *fw_upload, + const u8 *data, u32 size) +{ + struct ad9910_state *st = fw_upload->dd_handle; + const struct ad9910_ram_fw *fw_data = (const struct ad9910_ram_fw *)data; + size_t wcount, bcount; + + scoped_guard(mutex, &st->lock) { + /* cancel requests before prepare() are ignored */ + st->ram_fwu_cancel = false; + } + + if (size < sizeof(struct ad9910_ram_fw)) + return FW_UPLOAD_ERR_INVALID_SIZE; + + if (get_unaligned_be32(&fw_data->magic) != AD9910_RAM_FW_MAGIC) + return FW_UPLOAD_ERR_FW_INVALID; + + if (get_unaligned_be16(&fw_data->version) != AD9910_RAM_FW_V1) + return FW_UPLOAD_ERR_FW_INVALID; + + wcount = get_unaligned_be16(&fw_data->wcount); + bcount = size - sizeof(struct ad9910_ram_fw); + if (wcount > AD9910_RAM_SIZE_MAX_WORDS || + bcount != (wcount * AD9910_RAM_WORD_SIZE)) + return FW_UPLOAD_ERR_INVALID_SIZE; + + bcount += sizeof(fw_data->cfr1) + sizeof(fw_data->profiles); + if (crc32(0, &fw_data->cfr1, bcount) != get_unaligned_be32(&fw_data->crc)) + return FW_UPLOAD_ERR_FW_INVALID; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err ad9910_ram_fwu_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, + u32 size, u32 *written) +{ + const struct ad9910_ram_fw *fw_data = (const struct ad9910_ram_fw *)data; + struct ad9910_state *st = fw_upload->dd_handle; + int ret, ret2, idx, wcount; + u64 tmp64, backup; + + if (offset != 0) + return FW_UPLOAD_ERR_INVALID_SIZE; + + guard(mutex)(&st->lock); + + if (st->ram_fwu_cancel) + return FW_UPLOAD_ERR_CANCELED; + + if (AD9910_RAM_ENABLED(st)) + return FW_UPLOAD_ERR_HW_ERROR; + + for (idx = 0; idx < AD9910_NUM_PROFILES; idx++) + st->reg_profile[idx] = get_unaligned_be64(&fw_data->profiles[idx]) | + AD9910_PROFILE_RAM_OPEN_MSK; + + ret = ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_RAM_PLAYBACK_DEST_MSK | + AD9910_CFR1_INT_PROFILE_CTL_MSK, + get_unaligned_be32(&fw_data->cfr1), true); + if (ret) + return FW_UPLOAD_ERR_RW_ERROR; + + wcount = get_unaligned_be16(&fw_data->wcount); + if (!wcount) { + *written = size; + return FW_UPLOAD_ERR_NONE; /* nothing else to write */ + } + + ret = ad9910_profile_set(st, st->profile); + if (ret) + return FW_UPLOAD_ERR_HW_ERROR; + + /* backup profile register and update it with required address range */ + backup = st->reg[AD9910_REG_PROFILE(st->profile)].val64; + tmp64 = AD9910_PROFILE_RAM_STEP_RATE_MSK | + FIELD_PREP(AD9910_PROFILE_RAM_START_ADDR_MSK, 0) | + FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, wcount - 1); + ret = ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), tmp64, true); + if (ret) + return FW_UPLOAD_ERR_RW_ERROR; + + /* copy data words into spi buffer and transfer */ + memcpy(&st->tx_buf[1], fw_data->words, wcount * AD9910_RAM_WORD_SIZE); + ret = ad9910_spi_write(st, AD9910_REG_RAM, + wcount * AD9910_RAM_WORD_SIZE, false); + + /* restore active single tone profile regardless */ + st->reg[AD9910_REG_PROFILE(st->profile)].val64 = backup; + ret2 = ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), backup, true); + if (ret || ret2) + return FW_UPLOAD_ERR_RW_ERROR; + + *written = size; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err ad9910_ram_fwu_poll_complete(struct fw_upload *fw_upload) +{ + return FW_UPLOAD_ERR_NONE; +} + +static void ad9910_ram_fwu_cancel(struct fw_upload *fw_upload) +{ + struct ad9910_state *st = fw_upload->dd_handle; + + guard(mutex)(&st->lock); + st->ram_fwu_cancel = true; +} + +static void ad9910_ram_fwu_unregister(void *data) +{ + firmware_upload_unregister(data); +} + +static const struct fw_upload_ops ad9910_ram_fwu_ops = { + .prepare = ad9910_ram_fwu_prepare, + .write = ad9910_ram_fwu_write, + .poll_complete = ad9910_ram_fwu_poll_complete, + .cancel = ad9910_ram_fwu_cancel, +}; + +static const struct iio_info ad9910_info = { + .read_raw = ad9910_read_raw, + .write_raw = ad9910_write_raw, + .write_raw_get_fmt = ad9910_write_raw_get_fmt, + .read_label = ad9910_read_label, + .debugfs_reg64_access = &ad9910_debugfs_reg_access, +}; + +static int ad9910_cfg_sysclk(struct ad9910_state *st, bool update) +{ + u32 cfr3 = AD9910_CFR3_OPEN_MSK; + u32 tmp32; + + cfr3 |= FIELD_PREP(AD9910_CFR3_DRV0_MSK, st->data.refclk_out_drv); + + if (st->data.pll_enabled) { + tmp32 = st->data.pll_charge_pump_current - AD9910_ICP_MIN_uA; + tmp32 = DIV_ROUND_CLOSEST(tmp32, AD9910_ICP_STEP_uA); + cfr3 |= FIELD_PREP(AD9910_CFR3_ICP_MSK, tmp32) | + AD9910_CFR3_PLL_EN_MSK; + } else { + cfr3 |= AD9910_CFR3_ICP_MSK | + AD9910_CFR3_REFCLK_DIV_RESETB_MSK | + AD9910_CFR3_PFD_RESET_MSK; + } + st->reg[AD9910_REG_CFR3].val32 = cfr3; + + return ad9910_set_sysclk_freq(st, AD9910_MAX_SYSCLK_HZ, update); +} + +static int ad9910_parse_fw(struct ad9910_state *st) +{ + static const char * const refclk_out_drv0[] = { + "disabled", "low", "medium", "high", + }; + struct device *dev = &st->spi->dev; + const char *prop; + u32 tmp; + int ret; + + st->data.pll_enabled = device_property_read_bool(dev, "adi,pll-enable"); + if (st->data.pll_enabled) { + prop = "adi,charge-pump-current-microamp"; + if (device_property_present(dev, prop)) { + ret = device_property_read_u32(dev, prop, &tmp); + if (ret) + return dev_err_probe(dev, ret, "property read: %s\n", prop); + + if (tmp < AD9910_ICP_MIN_uA || tmp > AD9910_ICP_MAX_uA) + return dev_err_probe(dev, -ERANGE, + "invalid charge pump current %u\n", tmp); + } else { + tmp = AD9910_ICP_MIN_uA; + } + st->data.pll_charge_pump_current = tmp; + + prop = "adi,refclk-out-drive-strength"; + if (device_property_present(dev, prop)) { + ret = device_property_match_property_string(dev, prop, + refclk_out_drv0, + ARRAY_SIZE(refclk_out_drv0)); + if (ret < 0) + return dev_err_probe(dev, ret, "property read: %s\n", prop); + + st->data.refclk_out_drv = ret; + } + } + + return 0; +} + +static void ad9910_sw_powerdown_action(void *data) +{ + ad9910_sw_powerdown_set(data, true); +} + +static void ad9910_hw_powerdown_action(void *data) +{ + struct ad9910_state *st = data; + + gpiod_set_value_cansleep(st->gpio_pwdown, 1); +} + +static int ad9910_setup(struct device *dev, struct ad9910_state *st, + struct reset_control *dev_rst) +{ + int ret; + + ret = reset_control_assert(dev_rst); + if (ret) + return ret; + + fsleep(AD9910_RESET_DELAY_us); + + ret = reset_control_deassert(dev_rst); + if (ret) + return ret; + + ret = ad9910_reg32_write(st, AD9910_REG_CFR1, + (st->spi->mode & SPI_3WIRE ? 0 : + AD9910_CFR1_SDIO_INPUT_ONLY_MSK), false); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad9910_sw_powerdown_action, st); + if (ret) + return ret; + + ret = ad9910_reg32_write(st, AD9910_REG_CFR2, + AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK | + AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK | + AD9910_CFR2_DRG_NO_DWELL_MSK | + AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK | + AD9910_CFR2_SYNC_CLK_EN_MSK | + AD9910_CFR2_PDCLK_ENABLE_MSK, false); + if (ret) + return ret; + + ret = ad9910_cfg_sysclk(st, false); + if (ret) + return ret; + + ret = ad9910_set_dac_current(st, AD9910_DAC_IOUT_DEFAULT_uA, false); + if (ret) + return ret; + + /* configure step rate with default values */ + ret = ad9910_reg32_write(st, AD9910_REG_ASF, + FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, 1), + false); + if (ret) + return ret; + + ret = ad9910_reg32_write(st, AD9910_REG_DRG_RATE, + FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, 1) | + FIELD_PREP(AD9910_DRG_RATE_INC_MSK, 1), + false); + if (ret) + return ret; + + for (unsigned int i = 0; i < AD9910_NUM_PROFILES; i++) { + st->reg_profile[i] = AD9910_PROFILE_RAM_OPEN_MSK; + st->reg_profile[i] |= FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, 1); + st->reg_profile[i] |= FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, + AD9910_RAM_ADDR_MAX); + } + + return ad9910_io_update(st); +} + +static inline const char *ad9910_frequency_source_get(struct ad9910_state *st) +{ + bool ram_en, mode_en; + + guard(mutex)(&st->lock); + + /* RAM enabled and data destination is frequency */ + ram_en = AD9910_RAM_ENABLED(st); + if (ram_en && AD9910_DEST_FREQUENCY == + FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* DRG enabled and data destination is frequency */ + mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_FREQUENCY == + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_FREQ]; + + /* FTW: RAM enabled and data destination is phase, amplitude, or polar */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_frequency_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_frequency_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_frequency_source); + +static inline const char *ad9910_phase_source_get(struct ad9910_state *st) +{ + bool ram_en, mode_en; + u32 destination; + + guard(mutex)(&st->lock); + + /* RAM enabled and data destination is phase or polar */ + ram_en = AD9910_RAM_ENABLED(st); + if (ram_en) { + destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (destination == AD9910_DEST_PHASE || + destination == AD9910_DEST_POLAR) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + } + + /* DRG enabled and data destination is phase */ + mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_PHASE == + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_PHASE]; + + /* POW: RAM enabled and data destination is frequency or amplitude */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_phase_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_phase_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_phase_source); + +static inline const char *ad9910_amplitude_source_get(struct ad9910_state *st) +{ + bool ram_en, mode_en; + u32 destination; + + guard(mutex)(&st->lock); + + /* OSK enabled */ + mode_en = FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (mode_en) + return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; + + /* RAM enabled and data destination is amplitude or polar */ + ram_en = AD9910_RAM_ENABLED(st); + if (ram_en) { + destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (destination == AD9910_DEST_AMPLITUDE || + destination == AD9910_DEST_POLAR) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + } + + /* DRG enabled and data destination is amplitude */ + mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_AMPLITUDE == + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_AMP]; + + /* only way to control amplitude at this point is through OSK */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_amplitude_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_amplitude_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_amplitude_source); + +static inline void ad9910_debugfs_init(struct ad9910_state *st, + struct iio_dev *indio_dev) +{ + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + + debugfs_create_file("frequency_source", 0400, d, st, + &ad9910_frequency_source_fops); + debugfs_create_file("phase_source", 0400, d, st, + &ad9910_phase_source_fops); + debugfs_create_file("amplitude_source", 0400, d, st, + &ad9910_amplitude_source_fops); +} + +static int ad9910_probe(struct spi_device *spi) +{ + static const char * const supplies[] = { + "dvdd-io33", "avdd33", "dvdd18", "avdd18", + }; + struct device *dev = &spi->dev; + struct reset_control *dev_rst; + struct gpio_desc *io_rst_gpio; + struct iio_dev *indio_dev; + struct ad9910_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + indio_dev->name = "ad9910"; + indio_dev->info = &ad9910_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad9910_channels; + indio_dev->num_channels = ARRAY_SIZE(ad9910_channels); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + st->refclk = devm_clk_get_enabled(dev, "ref_clk"); + if (IS_ERR(st->refclk)) + return dev_err_probe(dev, PTR_ERR(st->refclk), + "Failed to get reference clock\n"); + + dev_rst = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(dev_rst)) + return dev_err_probe(dev, PTR_ERR(dev_rst), + "failed to get device reset control\n"); + + /* + * The IO RESET pin is not used in this driver, as we assume that all + * SPI transfers are complete, but if it is wired up, we need to make + * sure it is not floating. We can use either a reset controller or a + * GPIO for this. + */ + io_rst_gpio = devm_gpiod_get_optional(dev, "io-reset", GPIOD_OUT_LOW); + if (IS_ERR(io_rst_gpio)) + return dev_err_probe(dev, PTR_ERR(io_rst_gpio), + "failed to get io reset gpio\n"); + + st->gpio_update = devm_gpiod_get_optional(dev, "update", GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_update)) + return dev_err_probe(dev, PTR_ERR(st->gpio_update), + "failed to get update gpio\n"); + + st->gpio_profile = devm_gpiod_get_array_optional(dev, "profile", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_profile)) + return dev_err_probe(dev, PTR_ERR(st->gpio_profile), + "failed to get profile gpios\n"); + + if (st->gpio_profile && st->gpio_profile->ndescs != 3) + return dev_err_probe(dev, -EINVAL, + "invalid number of profile gpios\n"); + + st->gpio_pwdown = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_pwdown)) + return dev_err_probe(dev, PTR_ERR(st->gpio_pwdown), + "failed to get powerdown gpio\n"); + + ret = devm_add_action_or_reset(dev, ad9910_hw_powerdown_action, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to add hw powerdown action\n"); + + fsleep(AD9910_WAKEUP_DELAY_us); + + ret = ad9910_parse_fw(st); + if (ret) + return ret; + + ret = ad9910_setup(dev, st, dev_rst); + if (ret) + return dev_err_probe(dev, ret, "device setup failed\n"); + + snprintf(st->ram_fwu_name, sizeof(st->ram_fwu_name), "%s:ram", + dev_name(&indio_dev->dev)); + st->ram_fwu = firmware_upload_register(THIS_MODULE, dev, st->ram_fwu_name, + &ad9910_ram_fwu_ops, st); + if (IS_ERR(st->ram_fwu)) + return dev_err_probe(dev, PTR_ERR(st->ram_fwu), + "failed to register ram upload ops\n"); + + ret = devm_add_action_or_reset(dev, ad9910_ram_fwu_unregister, st->ram_fwu); + if (ret) + return dev_err_probe(dev, ret, + "failed to add ram upload unregister action\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + ad9910_debugfs_init(st, indio_dev); + + return 0; +} + +static const struct spi_device_id ad9910_id[] = { + { .name = "ad9910" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad9910_id); + +static const struct of_device_id ad9910_of_match[] = { + { .compatible = "adi,ad9910" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad9910_of_match); + +static struct spi_driver ad9910_driver = { + .driver = { + .name = "ad9910", + .of_match_table = ad9910_of_match, + }, + .probe = ad9910_probe, + .id_table = ad9910_id, +}; +module_spi_driver(ad9910_driver); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Analog Devices AD9910 DDS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 315317d6eec4..ec45c0f65fe3 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -461,7 +461,7 @@ static const struct clk_ops adf4350_clk_ops = { static int adf4350_clk_register(struct adf4350_state *st) { struct spi_device *spi = st->spi; - struct clk_init_data init; + struct clk_init_data init = { }; struct clk *clk; const char *parent_name; int ret; diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index 4dd19a9aa994..d1e5bb062474 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -1060,6 +1060,9 @@ static int adf4377_probe(struct spi_device *spi) st->regmap = regmap; st->spi = spi; st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODATA; + mutex_init(&st->lock); ret = adf4377_properties_parse(st); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 563de2724396..b61cc81122a6 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -502,8 +502,8 @@ static irqreturn_t adxrs290_trigger_handler(int irq, void *p) if (ret < 0) break; - iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &st->buffer, + sizeof(st->buffer), pf->timestamp); } while (0); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index d611341a0e2a..b007f46d3fd6 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -897,8 +897,8 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, + sizeof(data->scan), pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 87efa2c74ca4..426d04d496b4 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -59,7 +59,8 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p) if (ret < 0) goto error_ret; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + pf->timestamp); error_ret: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 54f11f000b6f..13e267943f67 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -5,13 +5,15 @@ menu "Humidity sensors" config AM2315 - tristate "Aosong AM2315 relative humidity and temperature sensor" + tristate "Aosong AM2315 and similar relative humidity and temperature sensor" depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - If you say yes here you get support for the Aosong AM2315 - relative humidity and ambient temperature sensor. + If you say yes here you get support for the Aosong relative + humidity and ambient temperature sensors: + - AM2315 + - AM2320 This driver can also be built as a module. If so, the module will be called am2315. diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index f29baa251f9f..5c7582f3a4f9 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Aosong AM2315 relative humidity and temperature + * Aosong AM2315 and similar relative humidity and temperature * * Copyright (c) 2016, Intel Corporation. * @@ -27,7 +27,17 @@ #define AM2315_TEMP_OFFSET 4 #define AM2315_ALL_CHANNEL_MASK GENMASK(1, 0) -#define AM2315_DRIVER_NAME "am2315" +struct am2315_chip_info { + const char *name; +}; + +static const struct am2315_chip_info am2315_chip_info = { + .name = "am2315", +}; + +static const struct am2315_chip_info am2320_chip_info = { + .name = "am2320", +}; struct am2315_data { struct i2c_client *client; @@ -220,9 +230,14 @@ static const struct iio_info am2315_info = { static int am2315_probe(struct i2c_client *client) { int ret; + const struct am2315_chip_info *chip_info; struct iio_dev *indio_dev; struct am2315_data *data; + chip_info = i2c_get_match_data(client); + if (!chip_info) + return -ENODATA; + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -233,7 +248,7 @@ static int am2315_probe(struct i2c_client *client) mutex_init(&data->lock); indio_dev->info = &am2315_info; - indio_dev->name = AM2315_DRIVER_NAME; + indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = am2315_channels; indio_dev->num_channels = ARRAY_SIZE(am2315_channels); @@ -249,8 +264,16 @@ static int am2315_probe(struct i2c_client *client) return devm_iio_device_register(&client->dev, indio_dev); } +static const struct of_device_id am2315_of_match[] = { + { .compatible = "aosong,am2315", .data = &am2315_chip_info }, + { .compatible = "aosong,am2320", .data = &am2320_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, am2315_of_match); + static const struct i2c_device_id am2315_i2c_id[] = { - { .name = "am2315" }, + { .name = "am2315", .driver_data = (kernel_ulong_t)&am2315_chip_info }, + { .name = "am2320", .driver_data = (kernel_ulong_t)&am2320_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); @@ -258,6 +281,7 @@ MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); static struct i2c_driver am2315_driver = { .driver = { .name = "am2315", + .of_match_table = am2315_of_match, }, .probe = am2315_probe, .id_table = am2315_i2c_id, @@ -266,5 +290,5 @@ static struct i2c_driver am2315_driver = { module_i2c_driver(am2315_driver); MODULE_AUTHOR("Tiberiu Breana "); -MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature"); +MODULE_DESCRIPTION("Aosong AM2315 and similar relative humidity and temperature"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/humidity/ens210.c b/drivers/iio/humidity/ens210.c index 81276195152b..4bdd37e7227b 100644 --- a/drivers/iio/humidity/ens210.c +++ b/drivers/iio/humidity/ens210.c @@ -202,8 +202,8 @@ static int ens210_probe(struct i2c_client *client) int ret; if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { return dev_err_probe(&client->dev, -EOPNOTSUPP, "adapter does not support some i2c transactions\n"); diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index bfeb0a60d3af..47a10884d812 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -49,7 +48,7 @@ struct hts221_odr { struct hts221_avg { u8 addr; u8 mask; - u16 avg_avl[HTS221_AVG_DEPTH]; + int avg_avl[HTS221_AVG_DEPTH]; }; static const struct hts221_odr hts221_odr_table[] = { @@ -58,6 +57,8 @@ static const struct hts221_odr hts221_odr_table[] = { { 13, 0x03 }, /* 12.5Hz */ }; +static const int hts221_odr_avail[] = { 1, 7, 13 }; + static const struct hts221_avg hts221_avg_list[] = { { .addr = 0x10, @@ -97,7 +98,11 @@ static const struct iio_chan_spec hts221_channels[] = { BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ), .scan_index = 0, .scan_type = { .sign = 's', @@ -113,7 +118,11 @@ static const struct iio_chan_spec hts221_channels[] = { BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ), .scan_index = 1, .scan_type = { .sign = 's', @@ -127,19 +136,15 @@ static const struct iio_chan_spec hts221_channels[] = { static int hts221_check_whoami(struct hts221_hw *hw) { + struct device *dev = hw->dev; int err, data; err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data); - if (err < 0) { - dev_err(hw->dev, "failed to read whoami register\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to read whoami register\n"); - if (data != HTS221_REG_WHOAMI_VAL) { - dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n", - data, HTS221_REG_WHOAMI_VAL); - return -ENODEV; - } + if (data != HTS221_REG_WHOAMI_VAL) + dev_info(dev, "unexpected whoami 0x%02x, continuing\n", data); return 0; } @@ -192,53 +197,35 @@ static int hts221_update_avg(struct hts221_hw *hw, return 0; } -static ssize_t hts221_sysfs_sampling_freq(struct device *dev, - struct device_attribute *attr, - char *buf) +static int hts221_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) { - int i; - ssize_t len = 0; - - for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++) - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - hts221_odr_table[i].hz); - buf[len - 1] = '\n'; - - return len; -} - -static ssize_t -hts221_sysfs_rh_oversampling_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H]; - ssize_t len = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - avg->avg_avl[i]); - buf[len - 1] = '\n'; - - return len; -} - -static ssize_t -hts221_sysfs_temp_oversampling_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T]; - ssize_t len = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - avg->avg_avl[i]); - buf[len - 1] = '\n'; - - return len; + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + *vals = hts221_avg_list[HTS221_SENSOR_H].avg_avl; + *length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_H].avg_avl); + break; + case IIO_TEMP: + *vals = hts221_avg_list[HTS221_SENSOR_T].avg_avl; + *length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_T].avg_avl); + break; + default: + return -EINVAL; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = hts221_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(hts221_odr_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } } int hts221_set_enable(struct hts221_hw *hw, bool enable) @@ -258,6 +245,7 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable) static int hts221_parse_temp_caldata(struct hts221_hw *hw) { + struct device *dev = hw->dev; int err, *slope, *b_gen, cal0, cal1; s16 cal_x0, cal_x1, cal_y0, cal_y1; __le16 val; @@ -288,10 +276,20 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw) return err; cal_x1 = le16_to_cpu(val); + if (cal_x1 == cal_x0) + return dev_err_probe(dev, -EINVAL, + "invalid temperature calibration points (x0 %d, x1 %d)\n", + cal_x0, cal_x1); + slope = &hw->sensors[HTS221_SENSOR_T].slope; b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen; *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0); + if (!*slope) + return dev_err_probe(dev, -EINVAL, + "invalid temperature calibration slope (y0 %d, y1 %d)\n", + cal_y0, cal_y1); + *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) / (cal_x1 - cal_x0); *b_gen *= 8; @@ -301,6 +299,7 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw) static int hts221_parse_rh_caldata(struct hts221_hw *hw) { + struct device *dev = hw->dev; int err, *slope, *b_gen, data; s16 cal_x0, cal_x1, cal_y0, cal_y1; __le16 val; @@ -327,10 +326,20 @@ static int hts221_parse_rh_caldata(struct hts221_hw *hw) return err; cal_x1 = le16_to_cpu(val); + if (cal_x1 == cal_x0) + return dev_err_probe(dev, -EINVAL, + "invalid rh calibration points (x0 %d, x1 %d)\n", + cal_x0, cal_x1); + slope = &hw->sensors[HTS221_SENSOR_H].slope; b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen; *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0); + if (!*slope) + return dev_err_probe(dev, -EINVAL, + "invalid rh calibration slope (y0 %d, y1 %d)\n", + cal_y0, cal_y1); + *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) / (cal_x1 - cal_x0); *b_gen *= 8; @@ -521,27 +530,10 @@ static int hts221_validate_trigger(struct iio_dev *iio_dev, return hw->trig == trig ? 0 : -EINVAL; } -static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO, - hts221_sysfs_rh_oversampling_avail, NULL, 0); -static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO, - hts221_sysfs_temp_oversampling_avail, NULL, 0); -static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq); - -static struct attribute *hts221_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr, - &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group hts221_attribute_group = { - .attrs = hts221_attributes, -}; - static const struct iio_info hts221_info = { - .attrs = &hts221_attribute_group, .read_raw = hts221_read_raw, .write_raw = hts221_write_raw, + .read_avail = hts221_read_avail, .validate_trigger = hts221_validate_trigger, }; @@ -608,33 +600,23 @@ int hts221_probe(struct device *dev, int irq, const char *name, /* configure humidity sensor */ err = hts221_parse_rh_caldata(hw); - if (err < 0) { - dev_err(hw->dev, "failed to get rh calibration data\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to get rh calibration data\n"); data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3]; err = hts221_update_avg(hw, HTS221_SENSOR_H, data); - if (err < 0) { - dev_err(hw->dev, "failed to set rh oversampling ratio\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to set rh oversampling ratio\n"); /* configure temperature sensor */ err = hts221_parse_temp_caldata(hw); - if (err < 0) { - dev_err(hw->dev, - "failed to get temperature calibration data\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to get temperature calibration data\n"); data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3]; err = hts221_update_avg(hw, HTS221_SENSOR_T, data); - if (err < 0) { - dev_err(hw->dev, - "failed to set temperature oversampling ratio\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to set temperature oversampling ratio\n"); if (hw->irq > 0) { err = hts221_allocate_buffers(iio_dev); diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c index 40276abc5d2e..e6dc9532cfbd 100644 --- a/drivers/iio/humidity/hts221_i2c.c +++ b/drivers/iio/humidity/hts221_i2c.c @@ -26,17 +26,14 @@ static const struct regmap_config hts221_i2c_regmap_config = { static int hts221_i2c_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct regmap *regmap; regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register i2c regmap\n"); - return hts221_probe(&client->dev, client->irq, - client->name, regmap); + return hts221_probe(dev, client->irq, client->name, regmap); } static const struct acpi_device_id hts221_acpi_match[] = { diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index f962842cc71d..f2c0b1a81a8f 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -27,17 +27,14 @@ static const struct regmap_config hts221_spi_regmap_config = { static int hts221_spi_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct regmap *regmap; regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register spi regmap\n"); - return hts221_probe(&spi->dev, spi->irq, - spi->modalias, regmap); + return hts221_probe(dev, spi->irq, spi->modalias, regmap); } static const struct of_device_id hts221_spi_of_match[] = { diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index b7d5f4f0fada..59fa1bd9924b 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -109,4 +109,11 @@ void iio_device_wakeup_eventset(struct iio_dev *indio_dev); struct iio_event_interface; bool iio_event_enabled(const struct iio_event_interface *ev_int); +#if IS_ENABLED(CONFIG_KUNIT) +ssize_t __iio_chan_prefix_emit(struct device *dev, + const struct iio_chan_spec *chan, + enum iio_shared_by shared_by, + char *buf, size_t len); +#endif + #endif diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 4842346f9f0c..d6c0a544ee9b 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -420,7 +420,10 @@ static int adis16400_initial_setup(struct iio_dev *indio_dev) else st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST; st->adis.spi->mode = SPI_MODE_3; - spi_setup(st->adis.spi); + + ret = spi_setup(st->adis.spi); + if (ret) + return ret; ret = __adis_initial_startup(&st->adis); if (ret) @@ -1150,6 +1153,9 @@ static int adis16400_probe(struct spi_device *spi) /* setup the industrialio driver allocated elements */ st->variant = spi_get_device_match_data(spi); + if (!st->variant) + return -ENODATA; + indio_dev->name = spi_get_device_id(spi)->name; indio_dev->channels = st->variant->channels; indio_dev->num_channels = st->variant->num_channels; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index b55d993f0264..e56943931b6a 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -179,6 +179,7 @@ struct inv_icm42600_state { struct { s64 gyro; s64 accel; + bool accel_force_odr; } timestamp; struct inv_icm42600_apex apex; struct inv_icm42600_fifo fifo; @@ -356,8 +357,8 @@ struct inv_icm42600_sensor_state { cpu_to_le16((_wm) & GENMASK(11, 0)) /* FIFO is 2048 bytes, let 12 samples for reading latency */ #define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16) -/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */ -#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232 +/* INV_ICM42600_FIFO_WATERMARK_MAX / 16 = 116 */ +#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 116 #define INV_ICM42600_REG_INT_CONFIG1 0x0064 #define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 4b0e3cd8a506..cc8c074777c9 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -254,15 +254,23 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, fifo_en |= INV_ICM42600_SENSOR_ACCEL; } + /* + * Sleep maximum stabilization time before enabling data in FIFO. + * We need to release the driver lock to not block gyro data processing. + * There is no possible race here since we are under IIO mutex locked. + */ + sleep = max(sleep_accel, sleep_temp); + if (sleep) { + mutex_unlock(&st->lock); + msleep(sleep); + mutex_lock(&st->lock); + } + /* update data FIFO write */ ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); out_unlock: mutex_unlock(&st->lock); - /* sleep maximum required time */ - sleep = max(sleep_accel, sleep_temp); - if (sleep) - msleep(sleep); return ret; } @@ -1227,15 +1235,17 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) if (size <= 0) return size; + /* update odr */ + if (odr & INV_ICM42600_SENSOR_ACCEL) { + inv_sensors_timestamp_apply_odr(ts, st->fifo.period, + st->fifo.nb.total, no); + st->timestamp.accel_force_odr = false; + } + /* skip packet if no accel data or data is invalid */ if (accel == NULL || !inv_icm42600_fifo_is_data_valid(accel)) continue; - /* update odr */ - if (odr & INV_ICM42600_SENSOR_ACCEL) - inv_sensors_timestamp_apply_odr(ts, st->fifo.period, - st->fifo.nb.total, no); - memcpy(&buffer.accel, accel, sizeof(buffer.accel)); /* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp * 64) : 0; @@ -1243,5 +1253,10 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); } + if (st->timestamp.accel_force_odr) { + inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + st->timestamp.accel_force_odr = false; + } + return 0; } diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 998d312f7bde..2bf9fc29d7d2 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -131,13 +132,16 @@ int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st, INV_ICM42600_FIFO_CONFIG1_GYRO_EN | INV_ICM42600_FIFO_CONFIG1_ACCEL_EN; - val = 0; - if (fifo_en & INV_ICM42600_SENSOR_GYRO) - val |= INV_ICM42600_FIFO_CONFIG1_GYRO_EN; - if (fifo_en & INV_ICM42600_SENSOR_ACCEL) - val |= INV_ICM42600_FIFO_CONFIG1_ACCEL_EN; - if (fifo_en & INV_ICM42600_SENSOR_TEMP) - val |= INV_ICM42600_FIFO_CONFIG1_TEMP_EN; + /* + * Always enable/disable all bits to ensure we can flawlessly add + * accel/gyro data in the FIFO while it is running. + */ + if (fifo_en) + val = INV_ICM42600_FIFO_CONFIG1_TEMP_EN | + INV_ICM42600_FIFO_CONFIG1_GYRO_EN | + INV_ICM42600_FIFO_CONFIG1_ACCEL_EN; + else + val = 0; ret = regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1, mask, val); if (ret) @@ -149,19 +153,6 @@ int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st, return 0; } -static size_t inv_icm42600_get_packet_size(unsigned int fifo_en) -{ - size_t packet_size; - - if ((fifo_en & INV_ICM42600_SENSOR_GYRO) && - (fifo_en & INV_ICM42600_SENSOR_ACCEL)) - packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE; - else - packet_size = INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE; - - return packet_size; -} - static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, size_t packet_size) { @@ -185,15 +176,14 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, * * FIFO watermark threshold is computed based on the required watermark values * set for gyro and accel sensors. Since watermark is all about acceptable data - * latency, use the smallest setting between the 2. It means choosing the - * smallest latency but this is not as simple as choosing the smallest watermark - * value. Latency depends on watermark and ODR. It requires several steps: - * 1) compute gyro and accel latencies and choose the smallest value. - * 2) adapt the chosen latency so that it is a multiple of both gyro and accel - * ones. Otherwise it is possible that you don't meet a requirement. (for - * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the - * value of 4 will not meet accel latency requirement because 6 is not a - * multiple of 4. You need to use the value 2.) + * latency, we should need to use the smallest latency value. But it is not as + * simple as choosing the smallest watermark value. Latency depends on watermark + * and ODR and IIO buffer watermark adds another requirement. The required steps: + * 1) compute gyro and accel periods and latencies + * 2) Use the smallest period and the GCD of the latencies. GCD is required + * because of the IIO buffer watermark that will prevent send of data if not + * crossed. Thus accel and gyro watermarks must be a multiple of the watermark + * value. Computing the GCD gives us the biggest value that meets this criteria. * 3) Since all periods are multiple of each others, watermark is computed by * dividing this computed latency by the smallest period, which corresponds * to the FIFO frequency. Beware that this is only true because we are not @@ -203,13 +193,13 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) { size_t packet_size, wm_size; unsigned int wm_gyro, wm_accel, watermark; - u32 period_gyro, period_accel; + u32 period_gyro, period_accel, period; u32 latency_gyro, latency_accel, latency; bool restore; __le16 raw_wm; int ret; - packet_size = inv_icm42600_get_packet_size(st->fifo.en); + packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE; /* compute sensors latency, depending on sensor watermark and odr */ wm_gyro = inv_icm42600_wm_truncate(st->fifo.watermark.gyro, packet_size); @@ -231,22 +221,17 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) watermark = wm_gyro; st->fifo.watermark.eff_gyro = wm_gyro; } else { - /* compute the smallest latency that is a multiple of both */ - if (latency_gyro <= latency_accel) - latency = latency_gyro - (latency_accel % latency_gyro); - else - latency = latency_accel - (latency_gyro % latency_accel); - /* all this works because periods are multiple of each others */ - watermark = latency / min(period_gyro, period_accel); - if (watermark < 1) - watermark = 1; - /* update effective watermark */ - st->fifo.watermark.eff_gyro = latency / period_gyro; - if (st->fifo.watermark.eff_gyro < 1) - st->fifo.watermark.eff_gyro = 1; - st->fifo.watermark.eff_accel = latency / period_accel; - if (st->fifo.watermark.eff_accel < 1) - st->fifo.watermark.eff_accel = 1; + /* + * In case of both accel and gyro enabled, we need to use the + * shortest period and the gcd of the latencies. Gcd is required + * because of the IIO buffer watermark that will prevent data + * sending if we are not crossing the watermark level. + */ + period = min(period_gyro, period_accel); + latency = gcd(latency_gyro, latency_accel); + watermark = max(latency / period, 1); + st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1); + st->fifo.watermark.eff_accel = max(latency / period_accel, 1); } /* compute watermark value in bytes */ @@ -399,6 +384,8 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) mutex_lock(&st->lock); inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + if (sensor == INV_ICM42600_SENSOR_ACCEL) + st->timestamp.accel_force_odr = false; ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor); if (ret) @@ -492,24 +479,21 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, st->fifo.nb.accel = 0; st->fifo.nb.total = 0; - /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ - if (max == 0) - max = st->fifo.watermark.value; - max_count = max * inv_icm42600_get_packet_size(st->fifo.en); - - /* read FIFO count value */ - raw_fifo_count = (__be16 *)st->buffer; - ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT, - raw_fifo_count, sizeof(*raw_fifo_count)); - if (ret) - return ret; - st->fifo.count = be16_to_cpup(raw_fifo_count); - - /* check and clamp FIFO count value */ - if (st->fifo.count == 0) - return 0; - if (st->fifo.count > max_count) - st->fifo.count = max_count; + /* read watermark samples for interrupt case (max = 0) or read FIFO count */ + if (max == 0) { + st->fifo.count = st->fifo.watermark.value * + INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE; + } else { + raw_fifo_count = (__be16 *)st->buffer; + ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT, + raw_fifo_count, sizeof(*raw_fifo_count)); + if (ret) + return ret; + max_count = max * INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE; + st->fifo.count = min(be16_to_cpup(raw_fifo_count), max_count); + if (st->fifo.count == 0) + return 0; + } /* read all FIFO data in internal buffer */ ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA, diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index dc97d8a274e3..cd2e53416f5b 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -324,6 +325,15 @@ int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, break; } + /* + * If we change mode from low-power to low-noise because of odr, + * we need to force odr change. + */ + if (oldconf->mode == INV_ICM42600_SENSOR_MODE_LOW_POWER && + conf->mode == INV_ICM42600_SENSOR_MODE_LOW_NOISE && + conf->odr != oldconf->odr) + st->timestamp.accel_force_odr = true; + /* set ACCEL_CONFIG0 register (accel fullscale & odr) */ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) { val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) | @@ -509,10 +519,20 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st, ret = regmap_read(st->map, INV_ICM42600_REG_WHOAMI, &val); if (ret) return ret; + + /* + * SPI interface has no ack mechanism. + * 0xFF or 0x00 whoami means no response from the device. + */ + if (val == U8_MAX || val == 0) + return dev_err_probe(dev, -ENODEV, + "invalid whoami %#04x expected %#04x (%s)\n", + val, hw->whoami, hw->name); + if (val != hw->whoami) { - dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n", - val, hw->whoami, hw->name); - return -ENODEV; + dev_info(dev, + "device id %#04x is not the %#04x associated with the FW-specified device (%s), probably using a valid fallback compatible\n", + val, hw->whoami, hw->name); } st->name = hw->name; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 253bf571439d..4e20d61d897b 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -126,15 +126,23 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, fifo_en |= INV_ICM42600_SENSOR_GYRO; } + /* + * Sleep maximum stabilization time before enabling data in FIFO. + * We need to release the driver lock to not block accel data processing. + * There is no possible race here since we are under IIO mutex locked. + */ + sleep = max(sleep_gyro, sleep_temp); + if (sleep) { + mutex_unlock(&st->lock); + msleep(sleep); + mutex_lock(&st->lock); + } + /* update data FIFO write */ ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); out_unlock: mutex_unlock(&st->lock); - /* sleep maximum required time */ - sleep = max(sleep_gyro, sleep_temp); - if (sleep) - msleep(sleep); return ret; } @@ -808,15 +816,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) if (size <= 0) return size; - /* skip packet if no gyro data or data is invalid */ - if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro)) - continue; - /* update odr */ if (odr & INV_ICM42600_SENSOR_GYRO) inv_sensors_timestamp_apply_odr(ts, st->fifo.period, st->fifo.nb.total, no); + /* skip packet if no gyro data or data is invalid */ + if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro)) + continue; + 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_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 28552d2db91d..e44ed6dcde02 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -77,10 +77,10 @@ static const struct i2c_device_id inv_icm42600_id[] = { { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, - { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, - { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, { } }; MODULE_DEVICE_TABLE(i2c, inv_icm42600_id); @@ -95,18 +95,18 @@ static const struct of_device_id inv_icm42600_of_matches[] = { }, { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, - }, { - .compatible = "invensense,icm42686", - .data = (void *)INV_CHIP_ICM42686, }, { .compatible = "invensense,icm42622", .data = (void *)INV_CHIP_ICM42622, - }, { - .compatible = "invensense,icm42688", - .data = (void *)INV_CHIP_ICM42688, }, { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, + }, { + .compatible = "invensense,icm42686", + .data = (void *)INV_CHIP_ICM42686, + }, { + .compatible = "invensense,icm42688", + .data = (void *)INV_CHIP_ICM42688, }, { } }; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index faf743bc6444..3e2f4eb42e97 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -74,10 +74,10 @@ static const struct spi_device_id inv_icm42600_id[] = { { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, - { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, - { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm42600_id); @@ -92,18 +92,18 @@ static const struct of_device_id inv_icm42600_of_matches[] = { }, { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, - }, { - .compatible = "invensense,icm42686", - .data = (void *)INV_CHIP_ICM42686, }, { .compatible = "invensense,icm42622", .data = (void *)INV_CHIP_ICM42622, - }, { - .compatible = "invensense,icm42688", - .data = (void *)INV_CHIP_ICM42688, }, { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, + }, { + .compatible = "invensense,icm42686", + .data = (void *)INV_CHIP_ICM42686, + }, { + .compatible = "invensense,icm42688", + .data = (void *)INV_CHIP_ICM42688, }, { } }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 5796896d54cd..38c3f3b398d4 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1303,8 +1304,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, struct inv_mpu6050_state *st = iio_priv(indio_dev); struct device *pdev = regmap_get_device(st->map); - if (kstrtoint(buf, 10, &fifo_rate)) - return -EINVAL; + result = kstrtoint(buf, 10, &fifo_rate); + if (result) + return result; if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE || fifo_rate > INV_MPU6050_MAX_FIFO_RATE) return -EINVAL; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index d6a1eeb151ca..cbc47fa5b101 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -766,8 +766,8 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, IIO_CHAN_SOFT_TIMESTAMP(3), }; - ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels), - GFP_KERNEL); + ext_channels = devm_kcalloc(hw->dev, ARRAY_SIZE(magn_channels), + sizeof(*ext_channels), GFP_KERNEL); if (!ext_channels) return NULL; diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index f7a4be8ec320..fbf5c39d7655 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -886,6 +886,39 @@ int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes) } EXPORT_SYMBOL_NS_GPL(iio_backend_num_lanes_set, "IIO_BACKEND"); +/** + * iio_backend_crc_enable - Enable CRC handling + * @back: Backend device + * + * On input data paths, enable backend handling of CRC data generated by the + * frontend. On output data paths, enable the backend to generate CRC data for + * the frontend to verify. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_crc_enable(struct iio_backend *back) +{ + return iio_backend_op_call(back, crc_enable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_crc_enable, "IIO_BACKEND"); + +/** + * iio_backend_crc_disable - Disable CRC handling + * @back: Backend device + * + * On input data paths, disable backend handling of CRC data generated by the + * frontend. On output data paths, disable CRC generation by the backend. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_crc_disable(struct iio_backend *back) +{ + return iio_backend_op_call(back, crc_disable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_crc_disable, "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 767a7794624a..d73f2541375f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -9,12 +9,15 @@ #define pr_fmt(fmt) "iio-core: " fmt +#include + #include #include #include #include #include #include +#include #include #include #include @@ -25,6 +28,7 @@ #include #include #include +#include #include #include @@ -100,6 +104,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", [IIO_VOLUMEFLOW] = "volumeflow", + [IIO_FREQUENCY] = "frequency", }; static const char * const iio_modifier_names[] = { @@ -199,6 +204,63 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_CONVDELAY] = "convdelay", [IIO_CHAN_INFO_POWERFACTOR] = "powerfactor", }; + +VISIBLE_IF_KUNIT +ssize_t __iio_chan_prefix_emit(struct device *dev, + const struct iio_chan_spec *chan, + enum iio_shared_by shared_by, + char *buf, size_t len) +{ + const char *type = iio_chan_type_name_spec[chan->type]; + const char *dir = iio_direction[chan->output]; + struct seq_buf s; + + seq_buf_init(&s, buf, len); + + switch (shared_by) { + case IIO_SHARED_BY_ALL: + break; + case IIO_SHARED_BY_DIR: + seq_buf_printf(&s, "%s", dir); + break; + case IIO_SHARED_BY_TYPE: + seq_buf_printf(&s, "%s_%s", dir, type); + if (chan->differential) + seq_buf_printf(&s, "-%s", type); + break; + case IIO_SEPARATE: + /* Validate differential channel settings */ + if (chan->differential) { + if (!chan->indexed) { + dev_err(dev, "Differential channels must be indexed\n"); + return -EINVAL; + } + if (chan->modified) { + dev_err(dev, "Differential channels can not have modifier\n"); + return -EINVAL; + } + } + + seq_buf_printf(&s, "%s_%s", dir, type); + + if (chan->indexed) + seq_buf_printf(&s, "%d", chan->channel); + + if (chan->differential) + seq_buf_printf(&s, "-%s%d", type, chan->channel2); + + if (chan->modified) + seq_buf_printf(&s, "_%s", iio_modifier_names[chan->channel2]); + + if (chan->extend_name) + seq_buf_printf(&s, "_%s", chan->extend_name); + break; + } + + return seq_buf_has_overflowed(&s) ? -EOVERFLOW : s.len; +} +EXPORT_SYMBOL_IF_KUNIT(__iio_chan_prefix_emit); + /** * iio_device_id() - query the unique ID for the device * @indio_dev: Device structure whose ID is being queried @@ -341,7 +403,8 @@ s64 iio_get_time_ns(const struct iio_dev *indio_dev) case CLOCK_TAI: return ktime_get_clocktai_ns(); default: - BUG(); + WARN_ON_ONCE(1); + return 0; } } EXPORT_SYMBOL(iio_get_time_ns); @@ -388,6 +451,7 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, struct iio_dev *indio_dev = file->private_data; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); unsigned int val = 0; + u64 val64 = 0; int ret; if (*ppos > 0) @@ -395,9 +459,17 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, iio_dev_opaque->read_buf, iio_dev_opaque->read_buf_len); - ret = indio_dev->info->debugfs_reg_access(indio_dev, - iio_dev_opaque->cached_reg_addr, - 0, &val); + if (indio_dev->info->debugfs_reg64_access) { + ret = indio_dev->info->debugfs_reg64_access(indio_dev, + iio_dev_opaque->cached_reg_addr, + 0, &val64); + } else { + ret = indio_dev->info->debugfs_reg_access(indio_dev, + iio_dev_opaque->cached_reg_addr, + 0, &val); + val64 = val; + } + if (ret) { dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__); return ret; @@ -405,7 +477,7 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, iio_dev_opaque->read_buf_len = snprintf(iio_dev_opaque->read_buf, sizeof(iio_dev_opaque->read_buf), - "0x%X\n", val); + "0x%llX\n", val64); return simple_read_from_buffer(userbuf, count, ppos, iio_dev_opaque->read_buf, @@ -417,8 +489,9 @@ static ssize_t iio_debugfs_write_reg(struct file *file, { struct iio_dev *indio_dev = file->private_data; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - unsigned int reg, val; + unsigned int reg; char buf[80]; + u64 val64; int ret; if (*ppos != 0 || count >= sizeof(buf)) @@ -431,7 +504,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, buf[ret] = '\0'; - ret = sscanf(buf, "%i %i", ®, &val); + ret = sscanf(buf, "%i %lli", ®, &val64); switch (ret) { case 1: @@ -439,8 +512,12 @@ static ssize_t iio_debugfs_write_reg(struct file *file, break; case 2: iio_dev_opaque->cached_reg_addr = reg; - ret = indio_dev->info->debugfs_reg_access(indio_dev, reg, - val, NULL); + if (indio_dev->info->debugfs_reg64_access) + ret = indio_dev->info->debugfs_reg64_access(indio_dev, reg, + val64, NULL); + else + ret = indio_dev->info->debugfs_reg_access(indio_dev, reg, + val64, NULL); if (ret) { dev_err(indio_dev->dev.parent, "%s: write failed\n", __func__); @@ -471,7 +548,8 @@ static void iio_device_register_debugfs(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque; - if (indio_dev->info->debugfs_reg_access == NULL) + if (!indio_dev->info->debugfs_reg_access && + !indio_dev->info->debugfs_reg64_access) return; if (!iio_debugfs_dentry) @@ -781,6 +859,20 @@ static ssize_t iio_read_channel_label(struct device *dev, to_iio_dev_attr(attr)->c, buf); } +static ssize_t iio_read_channel_parent(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct iio_chan_spec *chan = to_iio_dev_attr(attr)->c->parent; + ssize_t len; + + len = __iio_chan_prefix_emit(dev, chan, IIO_SEPARATE, buf, PAGE_SIZE - 1); + if (len < 0) + return len; + + return len + sysfs_emit_at(buf, len, "\n"); +} + static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf) @@ -1075,7 +1167,8 @@ static ssize_t iio_write_channel_info(struct device *dev, } static -int __iio_device_attr_init(struct device_attribute *dev_attr, +int __iio_device_attr_init(struct device *dev, + struct device_attribute *dev_attr, const char *postfix, struct iio_chan_spec const *chan, ssize_t (*readfunc)(struct device *dev, @@ -1087,106 +1180,22 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, size_t len), enum iio_shared_by shared_by) { - int ret = 0; - char *name = NULL; - char *full_postfix; + char prefix[NAME_MAX + 1]; + ssize_t ret; sysfs_attr_init(&dev_attr->attr); - /* Build up postfix of __postfix */ - if (chan->modified && (shared_by == IIO_SEPARATE)) { - if (chan->extend_name) - full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_modifier_names[chan->channel2], - chan->extend_name, - postfix); - else - full_postfix = kasprintf(GFP_KERNEL, "%s_%s", - iio_modifier_names[chan->channel2], - postfix); - } else { - if (chan->extend_name == NULL || shared_by != IIO_SEPARATE) - full_postfix = kstrdup(postfix, GFP_KERNEL); - else - full_postfix = kasprintf(GFP_KERNEL, - "%s_%s", - chan->extend_name, - postfix); - } - if (full_postfix == NULL) + ret = __iio_chan_prefix_emit(dev, chan, shared_by, prefix, sizeof(prefix)); + if (ret < 0) + return ret; + + if (ret) + dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s_%s", prefix, postfix); + else + dev_attr->attr.name = kstrdup(postfix, GFP_KERNEL); + if (!dev_attr->attr.name) return -ENOMEM; - if (chan->differential) { /* Differential can not have modifier */ - switch (shared_by) { - case IIO_SHARED_BY_ALL: - name = kasprintf(GFP_KERNEL, "%s", full_postfix); - break; - case IIO_SHARED_BY_DIR: - name = kasprintf(GFP_KERNEL, "%s_%s", - iio_direction[chan->output], - full_postfix); - break; - case IIO_SHARED_BY_TYPE: - name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - case IIO_SEPARATE: - if (!chan->indexed) { - WARN(1, "Differential channels must be indexed\n"); - ret = -EINVAL; - goto error_free_full_postfix; - } - name = kasprintf(GFP_KERNEL, - "%s_%s%d-%s%d_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - chan->channel, - iio_chan_type_name_spec[chan->type], - chan->channel2, - full_postfix); - break; - } - } else { /* Single ended */ - switch (shared_by) { - case IIO_SHARED_BY_ALL: - name = kasprintf(GFP_KERNEL, "%s", full_postfix); - break; - case IIO_SHARED_BY_DIR: - name = kasprintf(GFP_KERNEL, "%s_%s", - iio_direction[chan->output], - full_postfix); - break; - case IIO_SHARED_BY_TYPE: - name = kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - - case IIO_SEPARATE: - if (chan->indexed) - name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - chan->channel, - full_postfix); - else - name = kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - } - } - if (name == NULL) { - ret = -ENOMEM; - goto error_free_full_postfix; - } - dev_attr->attr.name = name; - if (readfunc) { dev_attr->attr.mode |= 0444; dev_attr->show = readfunc; @@ -1197,10 +1206,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, dev_attr->store = writefunc; } -error_free_full_postfix: - kfree(full_postfix); - - return ret; + return 0; } static void __iio_device_attr_deinit(struct device_attribute *dev_attr) @@ -1229,7 +1235,7 @@ int __iio_add_chan_devattr(const char *postfix, iio_attr = kzalloc_obj(*iio_attr); if (iio_attr == NULL) return -ENOMEM; - ret = __iio_device_attr_init(&iio_attr->dev_attr, + ret = __iio_device_attr_init(dev, &iio_attr->dev_attr, postfix, chan, readfunc, writefunc, shared_by); if (ret) @@ -1281,6 +1287,25 @@ static int iio_device_add_channel_label(struct iio_dev *indio_dev, return 1; } +static int iio_device_add_channel_parent_attr(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + int ret; + + if (!chan->parent) + return 0; + + ret = __iio_add_chan_devattr("parent", chan, + &iio_read_channel_parent, NULL, + 0, IIO_SEPARATE, &indio_dev->dev, NULL, + &iio_dev_opaque->channel_attr_list); + if (ret < 0) + return ret; + + return 1; +} + static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, @@ -1419,6 +1444,11 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_channel_parent_attr(indio_dev, chan); + if (ret < 0) + return ret; + attrcount += ret; + if (chan->ext_info) { unsigned int i = 0; @@ -1518,7 +1548,8 @@ static ssize_t current_timestamp_clock_show(struct device *dev, case CLOCK_TAI: break; default: - BUG(); + WARN_ON_ONCE(1); + return -EINVAL; } return sysfs_emit(buf, "%s\n", clock_names[clk]); diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index 4f52dc373abf..af6ed37fb34a 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -5,7 +5,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index f23bbce12c72..462fff1ddef2 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -741,6 +741,20 @@ config VEML6030 To compile this driver as a module, choose M here: the module will be called veml6030. +config VEML6031X00 + tristate "VEML6031X00 ambient light sensor series" + select REGMAP_I2C + select IIO_GTS_HELPER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VEML6031X00 + ambient light sensor series. + + To compile this driver as a module, choose M here: the + module will be called veml6031x00. + config VEML6040 tristate "VEML6040 RGBW light sensor" select REGMAP_I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 64e354c49ed8..6672ed01a819 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_VCNL4035) += vcnl4035.o obj-$(CONFIG_VEML3235) += veml3235.o obj-$(CONFIG_VEML3328) += veml3328.o obj-$(CONFIG_VEML6030) += veml6030.o +obj-$(CONFIG_VEML6031X00) += veml6031x00.o obj-$(CONFIG_VEML6040) += veml6040.o obj-$(CONFIG_VEML6046X00) += veml6046x00.o obj-$(CONFIG_VEML6070) += veml6070.o diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index f681b02e4922..7dd41136734f 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -61,7 +61,7 @@ #define APDS9306_NUM_REPEAT_RATES 7 #define APDS9306_INT_SRC_CLEAR 0 #define APDS9306_INT_SRC_ALS 1 -#define APDS9306_SAMP_FREQ_10HZ 0 +#define APDS9306_SAMP_FREQ_10HZ 2 /** * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier diff --git a/drivers/iio/light/apds9999.c b/drivers/iio/light/apds9999.c index 43fa9992c9c2..62620c4a2fbc 100644 --- a/drivers/iio/light/apds9999.c +++ b/drivers/iio/light/apds9999.c @@ -100,10 +100,6 @@ static int apds9999_init(struct apds9999_data *data) u8 regval; int ret; - ret = devm_add_action_or_reset(dev, apds9999_standby, client); - if (ret) - return ret; - guard(mutex)(&data->lock); regval = FIELD_PREP(APDS9999_LS_RES_MASK, APDS9999_RES_18BIT) | @@ -121,8 +117,12 @@ static int apds9999_init(struct apds9999_data *data) return ret; data->als_gain_idx = APDS9999_GAIN_3X; - return i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL, - APDS9999_MAIN_CTRL_LS_EN); + ret = i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL, + APDS9999_MAIN_CTRL_LS_EN); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, apds9999_standby, client); } static int apds9999_read_channel(struct apds9999_data *data, u8 reg, diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 7ab565b1fb9f..b1c92de7922a 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -249,7 +249,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids); static struct platform_driver cros_ec_light_prox_platform_driver = { .driver = { .name = "cros-ec-light-prox", - .pm = &cros_ec_sensors_pm_ops, + .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops), }, .probe = cros_ec_light_prox_probe, .id_table = cros_ec_light_prox_ids, diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c index cd5843e3e2c3..f9d215ef1970 100644 --- a/drivers/iio/light/iqs621-als.c +++ b/drivers/iio/light/iqs621-als.c @@ -179,19 +179,6 @@ static int iqs621_als_notifier(struct notifier_block *notifier, return NOTIFY_OK; } -static void iqs621_als_notifier_unregister(void *context) -{ - struct iqs621_als_private *iqs621_als = context; - struct iio_dev *indio_dev = iqs621_als->indio_dev; - int ret; - - ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh, - &iqs621_als->notifier); - if (ret) - dev_err(indio_dev->dev.parent, - "Failed to unregister notifier: %d\n", ret); -} - static int iqs621_als_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -563,19 +550,14 @@ static int iqs621_als_probe(struct platform_device *pdev) mutex_init(&iqs621_als->lock); iqs621_als->notifier.notifier_call = iqs621_als_notifier; - ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh, - &iqs621_als->notifier); + ret = devm_blocking_notifier_chain_register(&pdev->dev, + &iqs621_als->iqs62x->nh, + &iqs621_als->notifier); if (ret) { dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret); return ret; } - ret = devm_add_action_or_reset(&pdev->dev, - iqs621_als_notifier_unregister, - iqs621_als); - if (ret) - return ret; - return devm_iio_device_register(&pdev->dev, indio_dev); } diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index 759cb71ed1c5..7f7a563df4d9 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -339,9 +340,11 @@ static ssize_t proximity_on_chip_ambient_infrared_suppression_store struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct isl29018_chip *chip = iio_priv(indio_dev); int val; + int ret; - if (kstrtoint(buf, 10, &val)) - return -EINVAL; + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; if (!(val == 0 || val == 1)) return -EINVAL; diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 33deb1726689..e481ac908fc1 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -342,8 +342,9 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret; - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) return ret; mutex_lock(&chip->lock); @@ -392,14 +393,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - - ret = pm_runtime_put_autosuspend(dev); - if (ret < 0) - return ret; - - return 0; + return ret; } static int isl29028_read_raw(struct iio_dev *indio_dev, @@ -408,10 +402,11 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, { struct isl29028_chip *chip = iio_priv(indio_dev); struct device *dev = regmap_get_device(chip->regmap); - int ret, pm_ret; + int ret; - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) return ret; mutex_lock(&chip->lock); @@ -461,18 +456,6 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - - /** - * Preserve the ret variable if the call to - * pm_runtime_put_autosuspend() is successful so the reading - * (if applicable) is returned to user space. - */ - pm_ret = pm_runtime_put_autosuspend(dev); - if (pm_ret < 0) - return pm_ret; - return ret; } diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 99f0b903018c..5db9591a85b9 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -434,8 +435,9 @@ static ssize_t store_thresh_either_en(struct device *dev, if (!als->irq) return -EBUSY; - if (kstrtoul(buf, 0, &enable)) - return -EINVAL; + ret = kstrtoul(buf, 0, &enable); + if (ret) + return ret; int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); @@ -542,8 +544,9 @@ static ssize_t store_als_attr(struct device *dev, u8 val; int ret; - if (kstrtou8(buf, 0, &val)) - return -EINVAL; + ret = kstrtou8(buf, 0, &val); + if (ret) + return ret; switch (als_attr->type) { case LM3533_ATTR_TYPE_TARGET: diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index bc031f2c3141..f1bb066248e6 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -10,12 +10,6 @@ * * Datasheet: * https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf - * - * TODO: - * - Support for configurable gain and resolution - * - Sensor suspend/resume support - * - Add support for reading the ALS - * - Interrupt support */ #include diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index fa0d16890c8b..302be2fc314c 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -9,6 +9,7 @@ * TODO: IR LED characteristics */ +#include #include #include #include @@ -90,10 +91,11 @@ struct ltr501_samp_table { #define LTR501_RESERVED_GAIN -1 enum { - ltr501 = 0, - ltr559, ltr301, ltr303, + ltr329, + ltr501, + ltr559, }; struct ltr501_gain { @@ -140,6 +142,7 @@ struct ltr501_chip_info { u8 als_mode_active; u8 als_gain_mask; u8 als_gain_shift; + bool no_irq_support; struct iio_chan_spec const *channels; const int no_channels; const struct iio_info *info; @@ -178,6 +181,11 @@ static const struct ltr501_samp_table ltr501_ps_samp_table[] = { {500000, 2000000} }; +static bool ltr501_has_irq_support(const struct ltr501_chip_info *info) +{ + return !info->no_irq_support; +} + static int ltr501_match_samp_freq(const struct ltr501_samp_table *tab, int len, int val, int val2) { @@ -478,7 +486,7 @@ static int ltr501_write_intr_prst(struct ltr501_data *data, if (ret < 0) return ret; - /* period should be atleast equal to sampling period */ + /* period should be at least equal to sampling period */ if (period < samp_period) return -EINVAL; @@ -498,7 +506,7 @@ static int ltr501_write_intr_prst(struct ltr501_data *data, if (ret < 0) return ret; - /* period should be atleast equal to rate */ + /* period should be at least equal to rate */ if (period < samp_period) return -EINVAL; @@ -821,6 +829,9 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; + if (!ltr501_has_irq_support(info)) + return ret; + /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->als_period); @@ -840,6 +851,9 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; + if (!ltr501_has_irq_support(info)) + return ret; + /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->ps_period); @@ -1205,6 +1219,42 @@ static const struct iio_info ltr301_info = { }; static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { + [ltr301] = { + .partid = 0x08, + .als_gain = ltr501_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl), + .als_mode_active = BIT(0) | BIT(1), + .als_gain_mask = BIT(3), + .als_gain_shift = 3, + .info = <r301_info, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, + [ltr303] = { + .partid = 0x0A, + .als_gain = ltr559_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), + .als_mode_active = BIT(0), + .als_gain_mask = BIT(2) | BIT(3) | BIT(4), + .als_gain_shift = 2, + .info = <r301_info, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, + [ltr329] = { + .partid = 0x0A, + .als_gain = ltr559_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), + .als_mode_active = BIT(0), + .als_gain_mask = BIT(2) | BIT(3) | BIT(4), + .als_gain_shift = 2, + .no_irq_support = true, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, [ltr501] = { .partid = 0x08, .als_gain = ltr501_als_gain_tbl, @@ -1233,30 +1283,6 @@ static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { .channels = ltr501_channels, .no_channels = ARRAY_SIZE(ltr501_channels), }, - [ltr301] = { - .partid = 0x08, - .als_gain = ltr501_als_gain_tbl, - .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl), - .als_mode_active = BIT(0) | BIT(1), - .als_gain_mask = BIT(3), - .als_gain_shift = 3, - .info = <r301_info, - .info_no_irq = <r301_info_no_irq, - .channels = ltr301_channels, - .no_channels = ARRAY_SIZE(ltr301_channels), - }, - [ltr303] = { - .partid = 0x0A, - .als_gain = ltr559_als_gain_tbl, - .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), - .als_mode_active = BIT(0), - .als_gain_mask = BIT(2) | BIT(3) | BIT(4), - .als_gain_shift = 2, - .info = <r301_info, - .info_no_irq = <r301_info_no_irq, - .channels = ltr301_channels, - .no_channels = ARRAY_SIZE(ltr301_channels), - }, }; static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val) @@ -1369,13 +1395,15 @@ static int ltr501_init(struct ltr501_data *data) data->ps_contr = status | LTR501_CONTR_ACTIVE; - ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period); - if (ret < 0) - return ret; + if (ltr501_has_irq_support(data->chip_info)) { + ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period); + if (ret < 0) + return ret; - ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period); - if (ret < 0) - return ret; + ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period); + if (ret < 0) + return ret; + } return ltr501_write_contr(data, data->als_contr, data->ps_contr); } @@ -1530,6 +1558,11 @@ static int ltr501_probe(struct i2c_client *client) if (ret < 0) return ret; + if (!ltr501_has_irq_support(data->chip_info) && client->irq > 0) { + client->irq = 0; + dev_warn(&client->dev, "chip doesn't support IRQ"); + } + if (client->irq > 0) { ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, ltr501_interrupt_handler, @@ -1538,7 +1571,7 @@ static int ltr501_probe(struct i2c_client *client) "ltr501_thresh_event", indio_dev); if (ret) - return ret; + goto powerdown_on_error; } else { indio_dev->info = data->chip_info->info_no_irq; } @@ -1597,19 +1630,21 @@ static const struct acpi_device_id ltr_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); static const struct i2c_device_id ltr501_id[] = { - { .name = "ltr501", .driver_data = ltr501 }, - { .name = "ltr559", .driver_data = ltr559 }, { .name = "ltr301", .driver_data = ltr301 }, { .name = "ltr303", .driver_data = ltr303 }, + { .name = "ltr329", .driver_data = ltr329 }, + { .name = "ltr501", .driver_data = ltr501 }, + { .name = "ltr559", .driver_data = ltr559 }, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); static const struct of_device_id ltr501_of_match[] = { - { .compatible = "liteon,ltr501", }, - { .compatible = "liteon,ltr559", }, { .compatible = "liteon,ltr301", }, { .compatible = "liteon,ltr303", }, + { .compatible = "liteon,ltr329", }, + { .compatible = "liteon,ltr501", }, + { .compatible = "liteon,ltr559", }, { } }; MODULE_DEVICE_TABLE(of, ltr501_of_match); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 7c8a1d2b2ed0..0a16e326e939 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -40,7 +40,13 @@ #define STK3310_REG_PS_DATA_LSB 0x12 #define STK3310_REG_ALS_DATA_MSB 0x13 #define STK3310_REG_ALS_DATA_LSB 0x14 +#define STK36C61_REG_RED_DATA_MSB 0x15 +#define STK36C61_REG_GREEN_DATA_MSB 0x17 +#define STK36C61_REG_BLUE_DATA_MSB 0x19 +#define STK36C61_REG_CLEAR_DATA_MSB 0x1B +#define STK36C61_REG_CLEAR_DATA_LSB 0x1C #define STK3310_REG_ID 0x3E +#define STK36C61_REG_GAINCTRL 0x4E #define STK3310_MAX_REG 0x80 #define STK3310_STATE_EN_PS BIT(0) @@ -54,6 +60,7 @@ #define STK3311S34_CHIP_ID_VAL 0x1E #define STK3311X_CHIP_ID_VAL 0x12 #define STK3335_CHIP_ID_VAL 0x51 +#define STK36C61_CHIP_ID_VAL 0x95 #define STK3310_PSINT_EN 0x01 #define STK3310_PS_MAX_VAL 0xFFFF @@ -83,6 +90,8 @@ static const struct reg_field stk3310_reg_field_als_gain = REG_FIELD(STK3310_REG_ALSCTRL, 4, 5); static const struct reg_field stk3310_reg_field_ps_gain = REG_FIELD(STK3310_REG_PSCTRL, 4, 5); +static const struct reg_field stk3310_reg_field_clear_gain = + REG_FIELD(STK36C61_REG_GAINCTRL, 4, 5); static const struct reg_field stk3310_reg_field_als_it = REG_FIELD(STK3310_REG_ALSCTRL, 0, 3); static const struct reg_field stk3310_reg_field_ps_it = @@ -102,6 +111,7 @@ static const u8 stk3310_chip_ids[] = { STK3311X_CHIP_ID_VAL, STK3311_CHIP_ID_VAL, STK3335_CHIP_ID_VAL, + STK36C61_CHIP_ID_VAL, }; /* Estimate maximum proximity values with regard to measurement scale. */ @@ -132,6 +142,7 @@ struct stk3310_data { struct regmap_field *reg_state; struct regmap_field *reg_als_gain; struct regmap_field *reg_ps_gain; + struct regmap_field *reg_clear_gain; struct regmap_field *reg_als_it; struct regmap_field *reg_ps_it; struct regmap_field *reg_int_ps; @@ -181,24 +192,78 @@ static const struct iio_chan_spec_ext_info stk3310_ext_info[] = { { } }; +#define STK3310_LIGHT_CHANNEL { \ + .type = IIO_LIGHT, \ + .address = STK3310_REG_ALS_DATA_MSB, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ +} + +#define STK3310_PROXIMITY_CHANNEL { \ + .type = IIO_PROXIMITY, \ + .address = STK3310_REG_PS_DATA_MSB, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .event_spec = stk3310_events, \ + .num_event_specs = ARRAY_SIZE(stk3310_events), \ + .ext_info = stk3310_ext_info, \ +} + +#define STK36C61_INTENSITY_CHANNEL(_mod, _reg) { \ + .type = IIO_INTENSITY, \ + .address = _reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_mod, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ +} + static const struct iio_chan_spec stk3310_channels[] = { - { - .type = IIO_LIGHT, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_INT_TIME), - }, - { - .type = IIO_PROXIMITY, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_INT_TIME), - .event_spec = stk3310_events, - .num_event_specs = ARRAY_SIZE(stk3310_events), - .ext_info = stk3310_ext_info, - } + STK3310_LIGHT_CHANNEL, + STK3310_PROXIMITY_CHANNEL, +}; + +static const struct iio_chan_spec stk36c61_channels[] = { + STK3310_LIGHT_CHANNEL, + STK3310_PROXIMITY_CHANNEL, + STK36C61_INTENSITY_CHANNEL(RED, STK36C61_REG_RED_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(GREEN, STK36C61_REG_GREEN_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(BLUE, STK36C61_REG_BLUE_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(CLEAR, STK36C61_REG_CLEAR_DATA_MSB), +}; + +/** + * struct stk3310_chip_info - chip-specific data + * @name: device name reported to the IIO core + * @channels: channel specification + * @num_channels: number of channels + */ +struct stk3310_chip_info { + const char *name; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + unsigned int num_channels; +}; + +static const struct stk3310_chip_info stk3310_chip_info = { + .name = STK3310_DRIVER_NAME, + .channels = stk3310_channels, + .num_channels = ARRAY_SIZE(stk3310_channels), +}; + +static const struct stk3310_chip_info stk36c61_chip_info = { + .name = "stk36c61", + .channels = stk36c61_channels, + .num_channels = ARRAY_SIZE(stk36c61_channels), }; static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE); @@ -370,25 +435,21 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - u8 reg; __be16 buf; int ret; unsigned int index; struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; + struct regmap *map = data->regmap; - if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) + if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY && + chan->type != IIO_INTENSITY) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (chan->type == IIO_LIGHT) - reg = STK3310_REG_ALS_DATA_MSB; - else - reg = STK3310_REG_PS_DATA_MSB; - mutex_lock(&data->lock); - ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + ret = regmap_bulk_read(map, chan->address, &buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "register read failed\n"); mutex_unlock(&data->lock); @@ -398,10 +459,10 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: - if (chan->type == IIO_LIGHT) - ret = regmap_field_read(data->reg_als_it, &index); - else + if (chan->type == IIO_PROXIMITY) ret = regmap_field_read(data->reg_ps_it, &index); + else + ret = regmap_field_read(data->reg_als_it, &index); if (ret < 0) return ret; @@ -409,10 +470,12 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, *val2 = stk3310_it_table[index][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) - ret = regmap_field_read(data->reg_als_gain, &index); - else + if (chan->type == IIO_PROXIMITY) ret = regmap_field_read(data->reg_ps_gain, &index); + else if (chan->channel2 == IIO_MOD_LIGHT_CLEAR) + ret = regmap_field_read(data->reg_clear_gain, &index); + else + ret = regmap_field_read(data->reg_als_gain, &index); if (ret < 0) return ret; @@ -432,7 +495,8 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, int index; struct stk3310_data *data = iio_priv(indio_dev); - if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) + if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY && + chan->type != IIO_INTENSITY) return -EINVAL; switch (mask) { @@ -443,10 +507,10 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, if (index < 0) return -EINVAL; mutex_lock(&data->lock); - if (chan->type == IIO_LIGHT) - ret = regmap_field_write(data->reg_als_it, index); - else + if (chan->type == IIO_PROXIMITY) ret = regmap_field_write(data->reg_ps_it, index); + else + ret = regmap_field_write(data->reg_als_it, index); if (ret < 0) dev_err(&data->client->dev, "sensor configuration failed\n"); @@ -460,10 +524,12 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, if (index < 0) return -EINVAL; mutex_lock(&data->lock); - if (chan->type == IIO_LIGHT) - ret = regmap_field_write(data->reg_als_gain, index); - else + if (chan->type == IIO_PROXIMITY) ret = regmap_field_write(data->reg_ps_gain, index); + else if (chan->channel2 == IIO_MOD_LIGHT_CLEAR) + ret = regmap_field_write(data->reg_clear_gain, index); + else + ret = regmap_field_write(data->reg_als_gain, index); if (ret < 0) dev_err(&data->client->dev, "sensor configuration failed\n"); @@ -474,9 +540,31 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int stk3310_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 = (const int *)stk3310_scale_table; + *length = 2 * ARRAY_SIZE(stk3310_scale_table); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_INT_TIME: + *vals = (const int *)stk3310_it_table; + *length = 2 * ARRAY_SIZE(stk3310_it_table); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_info stk3310_info = { .read_raw = stk3310_read_raw, .write_raw = stk3310_write_raw, + .read_avail = stk3310_read_avail, .attrs = &stk3310_attribute_group, .read_event_value = stk3310_read_event, .write_event_value = stk3310_write_event, @@ -548,6 +636,7 @@ static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case STK3310_REG_ALS_DATA_MSB: case STK3310_REG_ALS_DATA_LSB: + case STK36C61_REG_RED_DATA_MSB ... STK36C61_REG_CLEAR_DATA_LSB: case STK3310_REG_PS_DATA_LSB: case STK3310_REG_PS_DATA_MSB: case STK3310_REG_FLAG: @@ -582,6 +671,7 @@ static int stk3310_regmap_init(struct stk3310_data *data) STK3310_REGFIELD(state); STK3310_REGFIELD(als_gain); STK3310_REGFIELD(ps_gain); + STK3310_REGFIELD(clear_gain); STK3310_REGFIELD(als_it); STK3310_REGFIELD(ps_it); STK3310_REGFIELD(int_ps); @@ -635,10 +725,16 @@ static irqreturn_t stk3310_irq_event_handler(int irq, void *private) static int stk3310_probe(struct i2c_client *client) { + const struct stk3310_chip_info *chip_info; + struct device *dev = &client->dev; int ret; struct iio_dev *indio_dev; struct stk3310_data *data; + chip_info = i2c_get_match_data(client); + if (!chip_info) + return dev_err_probe(dev, -ENODATA, "missing driver data\n"); + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -657,10 +753,10 @@ static int stk3310_probe(struct i2c_client *client) return ret; indio_dev->info = &stk3310_info; - indio_dev->name = STK3310_DRIVER_NAME; + indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = stk3310_channels; - indio_dev->num_channels = ARRAY_SIZE(stk3310_channels); + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; ret = stk3310_init(indio_dev); if (ret < 0) @@ -766,28 +862,29 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); static const struct i2c_device_id stk3310_i2c_id[] = { - { .name = "STK3013" }, - { .name = "STK3310" }, - { .name = "STK3311" }, - { .name = "STK3335" }, + { .name = "stk3013", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .name = "stk3310", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .name = "stk3311", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .name = "stk3335", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .name = "stk36c61", .driver_data = (kernel_ulong_t)&stk36c61_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); static const struct acpi_device_id stk3310_acpi_id[] = { - {"STK3013", 0}, - {"STK3310", 0}, - {"STK3311", 0}, + { .id = "STK3013", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .id = "STK3310", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .id = "STK3311", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, { } }; - MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id); static const struct of_device_id stk3310_of_match[] = { - { .compatible = "sensortek,stk3013", }, - { .compatible = "sensortek,stk3310", }, - { .compatible = "sensortek,stk3311", }, - { .compatible = "sensortek,stk3335", }, + { .compatible = "sensortek,stk3013", .data = &stk3310_chip_info }, + { .compatible = "sensortek,stk3310", .data = &stk3310_chip_info }, + { .compatible = "sensortek,stk3311", .data = &stk3310_chip_info }, + { .compatible = "sensortek,stk3335", .data = &stk3310_chip_info }, + { .compatible = "sensortek,stk36c61", .data = &stk36c61_chip_info }, { } }; MODULE_DEVICE_TABLE(of, stk3310_of_match); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index 53fd423aa7ae..2d041b7530e6 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -7,10 +7,11 @@ * Copyright (c) 2016-2017 Brian Masney */ -#include #include #include #include +#include +#include #include #include #include @@ -483,9 +484,12 @@ static ssize_t in_illuminance_input_target_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct tsl2583_chip *chip = iio_priv(indio_dev); - int value; + int value, ret; - if (kstrtoint(buf, 0, &value) || !value) + ret = kstrtoint(buf, 0, &value); + if (ret) + return ret; + if (!value) return -EINVAL; mutex_lock(&chip->als_mutex); @@ -503,7 +507,10 @@ static ssize_t in_illuminance_calibrate_store(struct device *dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int value, ret; - if (kstrtoint(buf, 0, &value) || value != 1) + ret = kstrtoint(buf, 0, &value); + if (ret) + return ret; + if (value != 1) return -EINVAL; mutex_lock(&chip->als_mutex); diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 4486a1d9d84d..c8a7afc65c1f 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -951,8 +952,9 @@ static ssize_t in_illuminance0_target_input_store(struct device *dev, u16 value; int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; chip->settings.als_cal_target = value; ret = tsl2772_invoke_change(indio_dev); @@ -970,7 +972,10 @@ static ssize_t in_illuminance0_calibrate_store(struct device *dev, bool value; int ret; - if (kstrtobool(buf, &value) || !value) + ret = kstrtobool(buf, &value); + if (ret) + return ret; + if (!value) return -EINVAL; ret = tsl2772_als_calibrate(indio_dev); @@ -1061,7 +1066,10 @@ static ssize_t in_proximity0_calibrate_store(struct device *dev, bool value; int ret; - if (kstrtobool(buf, &value) || !value) + ret = kstrtobool(buf, &value); + if (ret) + return ret; + if (!value) return -EINVAL; ret = tsl2772_prox_cal(indio_dev); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 336468d59ea7..911f8d1c74d4 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1504,7 +1505,7 @@ static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, { struct vcnl4000_data *data = iio_priv(indio_dev); - return sprintf(buf, "%u\n", data->near_level); + return sysfs_emit(buf, "%u\n", data->near_level); } static irqreturn_t vcnl4010_irq_thread(int irq, void *p) diff --git a/drivers/iio/light/veml3328.c b/drivers/iio/light/veml3328.c index 7ff1753925c4..df36c48204be 100644 --- a/drivers/iio/light/veml3328.c +++ b/drivers/iio/light/veml3328.c @@ -32,6 +32,11 @@ #define VEML3328_REG_DATA_B 0x07 #define VEML3328_REG_DATA_IR 0x08 +#define VEML3328_CONT_IT_50MSECS 0 +#define VEML3328_CONT_IT_100MSECS 1 +#define VEML3328_CONT_IT_200MSECS 2 +#define VEML3328_CONT_IT_400MSECS 3 + #define VEML3328_CONF_IT_MASK GENMASK(5, 4) #define VEML3328_CONF_GAIN_MASK GENMASK(11, 10) @@ -89,21 +94,40 @@ static const struct iio_chan_spec veml3328_channels[] = { * Precomputed scale values (micro units). * Formula for calculation: 0.384 * (50000 / IT_us) * (1 / Gain) * Gain indexes: 0 (x0.5), 1 (x1), 2 (x2), 3 (x4) - * IT indexes: 0 (50ms), 1 (100ms), 2 (200ms), 3 (400ms) */ -static const int veml3328_scale_vals[4][8] = { - { 0, 768000, 0, 384000, 0, 192000, 0, 96000 }, - { 0, 384000, 0, 192000, 0, 96000, 0, 48000 }, - { 0, 192000, 0, 96000, 0, 48000, 0, 24000 }, - { 0, 96000, 0, 48000, 0, 24000, 0, 12000 }, +static const int veml3328_scale_vals[4][4][2] = { + [VEML3328_CONT_IT_50MSECS] = { + { 0, 768000 }, + { 0, 384000 }, + { 0, 192000 }, + { 0, 96000 }, + }, + [VEML3328_CONT_IT_100MSECS] = { + { 0, 384000 }, + { 0, 192000 }, + { 0, 96000 }, + { 0, 48000 }, + }, + [VEML3328_CONT_IT_200MSECS] = { + { 0, 192000 }, + { 0, 96000 }, + { 0, 48000 }, + { 0, 24000 }, + }, + [VEML3328_CONT_IT_400MSECS] = { + { 0, 96000 }, + { 0, 48000 }, + { 0, 24000 }, + { 0, 12000 }, + }, }; /* integration times in microseconds */ static const int veml3328_it_times[][2] = { - { 0, 50 * USEC_PER_MSEC }, - { 0, 100 * USEC_PER_MSEC }, - { 0, 200 * USEC_PER_MSEC }, - { 0, 400 * USEC_PER_MSEC }, + [VEML3328_CONT_IT_50MSECS] = { 0, 50 * USEC_PER_MSEC }, + [VEML3328_CONT_IT_100MSECS] = { 0, 100 * USEC_PER_MSEC }, + [VEML3328_CONT_IT_200MSECS] = { 0, 200 * USEC_PER_MSEC }, + [VEML3328_CONT_IT_400MSECS] = { 0, 400 * USEC_PER_MSEC }, }; static int veml3328_power_down(struct veml3328_data *data) @@ -184,9 +208,8 @@ static int veml3328_read_raw(struct iio_dev *indio_dev, if (it_inx >= ARRAY_SIZE(veml3328_it_times) || gain_inx >= 4) return -EINVAL; - /* Stride by 2 through the flattened array to match (val, val2) */ - *val = veml3328_scale_vals[it_inx][gain_inx * 2]; - *val2 = veml3328_scale_vals[it_inx][gain_inx * 2 + 1]; + *val = veml3328_scale_vals[it_inx][gain_inx][0]; + *val2 = veml3328_scale_vals[it_inx][gain_inx][1]; return IIO_VAL_INT_PLUS_MICRO; @@ -282,8 +305,8 @@ static int veml3328_write_raw(struct iio_dev *indio_dev, return -EINVAL; for (i = 0; i < 4; i++) { - if (val == veml3328_scale_vals[it_inx][i * 2] && - val2 == veml3328_scale_vals[it_inx][i * 2 + 1]) + if (val == veml3328_scale_vals[it_inx][i][0] && + val2 == veml3328_scale_vals[it_inx][i][1]) break; } diff --git a/drivers/iio/light/veml6031x00.c b/drivers/iio/light/veml6031x00.c new file mode 100644 index 000000000000..f6308498e422 --- /dev/null +++ b/drivers/iio/light/veml6031x00.c @@ -0,0 +1,1387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * VEML6031X00 Ambient Light Sensor + * + * Copyright (c) 2026, Javier Carrasco + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Device registers */ +#define VEML6031X00_REG_CONF0 0x00 +#define VEML6031X00_REG_CONF1 0x01 +#define VEML6031X00_REG_WH_L 0x04 +#define VEML6031X00_REG_WH_H 0x05 +#define VEML6031X00_REG_WL_L 0x06 +#define VEML6031X00_REG_WL_H 0x07 +#define VEML6031X00_REG_ALS_L 0x10 +#define VEML6031X00_REG_ALS_H 0x11 +#define VEML6031X00_REG_IR_L 0x12 +#define VEML6031X00_REG_IR_H 0x13 +#define VEML6031X00_REG_ID_L 0x14 +#define VEML6031X00_REG_ID_H 0x15 +#define VEML6031X00_REG_INT 0x17 + +/* Bit masks for specific functionality */ +#define VEML6031X00_CONF0_SD BIT(0) +#define VEML6031X00_CONF0_AF_TRIG BIT(2) +#define VEML6031X00_CONF0_AF BIT(3) +#define VEML6031X00_CONF1_ALS_CAL BIT(0) +#define VEML6031X00_CONF1_IR_SD BIT(7) +#define VEML6031X00_INT_TH_H BIT(1) +#define VEML6031X00_INT_TH_L BIT(2) +#define VEML6031X00_INT_DRDY BIT(3) +#define VEML6031X00_INT_MASK \ + (VEML6031X00_INT_TH_L | VEML6031X00_INT_TH_H | VEML6031X00_INT_DRDY) + +#define VEML6031X00_GAIN_SEL(pd_div4, gain) (((pd_div4) << 2) | (gain)) + +enum veml6031x00_scan { + VEML6031X00_SCAN_ALS, + VEML6031X00_SCAN_IR, + VEML6031X00_SCAN_TIMESTAMP, +}; + +struct veml6031x00_rf { + struct regmap_field *gain; + struct regmap_field *int_en; + struct regmap_field *it; + struct regmap_field *pd_div4; + struct regmap_field *pers; +}; + +struct veml6031x00_chip { + const char *name; + const unsigned int part_id; +}; + +struct veml6031x00_data { + struct iio_gts gts; + struct regmap *regmap; + struct iio_trigger *trig; + struct veml6031x00_rf rf; + const struct veml6031x00_chip *chip; + /* + * Serialize access to scale register fields scattered across multiple + * registers (rf.gain, rf.pd_div4, rf.it) to read and write them as a + * consistent set. + */ + struct mutex scale_lock; + /* + * Serialize access to irq enable/disable by events and trigger + * (shared line). + */ + struct mutex irq_lock; + unsigned int interrupt_users; + bool ev_en; + bool trig_en; +}; + +static const struct iio_itime_sel_mul veml6031x00_it_sel[] = { + GAIN_SCALE_ITIME_US(3125, 0, 1), + GAIN_SCALE_ITIME_US(6250, 1, 2), + GAIN_SCALE_ITIME_US(12500, 2, 4), + GAIN_SCALE_ITIME_US(25000, 3, 8), + GAIN_SCALE_ITIME_US(50000, 4, 16), + GAIN_SCALE_ITIME_US(100000, 5, 32), + GAIN_SCALE_ITIME_US(200000, 6, 64), + GAIN_SCALE_ITIME_US(400000, 7, 128), +}; + +/* + * The gain selector encodes (PD_D4 << 2) | GAIN to identify each gain setting. + * Gains are multiplied by 8 to work with integers. The values in the iio-gts + * tables don't need corrections because the maximum value of the scale refers + * to GAIN = x1, and the rest of the values are obtained from the resulting + * linear function. + * TODO: add support for GAIN_0_165 and GAIN_0_660 + */ +#define VEML6031X00_SEL_GAIN_0_125 0x07 +#define VEML6031X00_SEL_GAIN_0_250 0x04 +#define VEML6031X00_SEL_GAIN_0_500 0x03 +#define VEML6031X00_SEL_GAIN_1_000 0x00 +#define VEML6031X00_SEL_GAIN_2_000 0x01 +static const struct iio_gain_sel_pair veml6031x00_gain_sel[] = { + GAIN_SCALE_GAIN(1, VEML6031X00_SEL_GAIN_0_125), + GAIN_SCALE_GAIN(2, VEML6031X00_SEL_GAIN_0_250), + GAIN_SCALE_GAIN(4, VEML6031X00_SEL_GAIN_0_500), + GAIN_SCALE_GAIN(8, VEML6031X00_SEL_GAIN_1_000), + GAIN_SCALE_GAIN(16, VEML6031X00_SEL_GAIN_2_000), +}; + +static IIO_CONST_ATTR(in_illuminance_thresh_either_period_available, "1 2 4 8"); + +static struct attribute *veml6031x00_event_attributes[] = { + &iio_const_attr_in_illuminance_thresh_either_period_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group veml6031x00_event_attr_group = { + .attrs = veml6031x00_event_attributes, +}; + +/* + * The shutdown bits (SD and ALS_IR_SD) are in different registers, and both + * must be updated when changing the device power state. + */ +static int veml6031x00_set_power(struct veml6031x00_data *data, bool state) +{ + int ret; + + ret = regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_SD, !state); + if (ret) + return ret; + + return regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF1, + VEML6031X00_CONF1_IR_SD, !state); +} + +static void veml6031x00_als_shutdown_action(void *data) +{ + struct veml6031x00_data *priv = data; + struct device *dev = regmap_get_device(priv->regmap); + int ret; + + ret = veml6031x00_set_power(priv, false); + if (ret) + dev_warn(dev, "Failed to shut down device: %d\n", ret); +} + +static const struct iio_event_spec veml6031x00_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), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec veml6031x00_channels[] = { + { + .type = IIO_LIGHT, + .address = VEML6031X00_REG_ALS_L, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6031X00_SCAN_ALS, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_INTENSITY, + .address = VEML6031X00_REG_IR_L, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = VEML6031X00_SCAN_IR, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6031X00_SCAN_TIMESTAMP), +}; + +static const struct iio_chan_spec veml6031x00_channels_irq[] = { + { + .type = IIO_LIGHT, + .address = VEML6031X00_REG_ALS_L, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .event_spec = veml6031x00_event_spec, + .num_event_specs = ARRAY_SIZE(veml6031x00_event_spec), + .scan_index = VEML6031X00_SCAN_ALS, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_INTENSITY, + .address = VEML6031X00_REG_IR_L, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = VEML6031X00_SCAN_IR, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6031X00_SCAN_TIMESTAMP), +}; + +static const struct regmap_range veml6031x00_readable_ranges[] = { + regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1), + regmap_reg_range(VEML6031X00_REG_WH_L, VEML6031X00_REG_WL_H), + regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_ID_H), + regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT), +}; + +static const struct regmap_access_table veml6031x00_readable_table = { + .yes_ranges = veml6031x00_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6031x00_readable_ranges), +}; + +static const struct regmap_range veml6031x00_writable_ranges[] = { + regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1), + regmap_reg_range(VEML6031X00_REG_WH_L, VEML6031X00_REG_WL_H), +}; + +static const struct regmap_access_table veml6031x00_writable_table = { + .yes_ranges = veml6031x00_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6031x00_writable_ranges), +}; + +static const struct regmap_range veml6031x00_volatile_ranges[] = { + /* AF_TRIG in CONF0 is volatile */ + regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF0), + regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_IR_H), + regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT), +}; + +static const struct regmap_access_table veml6031x00_volatile_table = { + .yes_ranges = veml6031x00_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6031x00_volatile_ranges), +}; + +static const struct regmap_range veml6031x00_precious_ranges[] = { + regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT), +}; + +static const struct regmap_access_table veml6031x00_precious_table = { + .yes_ranges = veml6031x00_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6031x00_precious_ranges), +}; + +static const struct regmap_config veml6031x00_regmap_config = { + .name = "veml6031x00_regmap", + .reg_bits = 8, + .val_bits = 8, + .rd_table = &veml6031x00_readable_table, + .wr_table = &veml6031x00_writable_table, + .volatile_table = &veml6031x00_volatile_table, + .precious_table = &veml6031x00_precious_table, + .max_register = VEML6031X00_REG_INT, + .cache_type = REGCACHE_MAPLE, +}; + +static const struct reg_field veml6031x00_rf_int_en = + REG_FIELD(VEML6031X00_REG_CONF0, 1, 1); + +static const struct reg_field veml6031x00_rf_it = + REG_FIELD(VEML6031X00_REG_CONF0, 4, 6); + +static const struct reg_field veml6031x00_rf_pers = + REG_FIELD(VEML6031X00_REG_CONF1, 1, 2); + +static const struct reg_field veml6031x00_rf_gain = + REG_FIELD(VEML6031X00_REG_CONF1, 3, 4); + +static const struct reg_field veml6031x00_rf_pd_div4 = + REG_FIELD(VEML6031X00_REG_CONF1, 6, 6); + +static int veml6031x00_regfield_init(struct veml6031x00_data *data) +{ + struct regmap *map = data->regmap; + struct device *dev = regmap_get_device(map); + struct regmap_field *rm_field; + struct veml6031x00_rf *rf = &data->rf; + + rm_field = devm_regmap_field_alloc(dev, map, veml6031x00_rf_gain); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->gain = rm_field; + + rm_field = devm_regmap_field_alloc(dev, map, veml6031x00_rf_it); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->it = rm_field; + + rm_field = devm_regmap_field_alloc(dev, map, veml6031x00_rf_int_en); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->int_en = rm_field; + + rm_field = devm_regmap_field_alloc(dev, map, veml6031x00_rf_pd_div4); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->pd_div4 = rm_field; + + rm_field = devm_regmap_field_alloc(dev, map, veml6031x00_rf_pers); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->pers = rm_field; + + return 0; +} + +static int __veml6031x00_get_it(struct veml6031x00_data *data, int *val2) +{ + unsigned int it_idx; + int ret; + + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; + + ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx); + if (ret < 0) + return ret; + + *val2 = ret; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int veml6031x00_get_it(struct veml6031x00_data *data, int *val2) +{ + guard(mutex)(&data->scale_lock); + + return __veml6031x00_get_it(data, val2); +} + +static int veml6031x00_write_gain(struct veml6031x00_data *data, unsigned int gain) +{ + int ret; + + ret = regmap_field_write(data->rf.pd_div4, gain >> 2); + if (ret) + return ret; + + return regmap_field_write(data->rf.gain, gain & 0x03); +} + +static int veml6031x00_set_it(struct iio_dev *iio, int val, int val2) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + int ret, gain_sel, new_gain, prev_gain, prev_it; + unsigned int gain_reg, it_idx, pd_div4; + struct iio_gts *gts = &data->gts; + bool gain_in_range; + + if (val || !iio_gts_valid_time(gts, val2)) + return -EINVAL; + + guard(mutex)(&data->scale_lock); + + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; + + ret = regmap_field_read(data->rf.gain, &gain_reg); + if (ret) + return ret; + + ret = regmap_field_read(data->rf.pd_div4, &pd_div4); + if (ret) + return ret; + + prev_it = iio_gts_find_int_time_by_sel(gts, it_idx); + if (prev_it < 0) + return prev_it; + + if (prev_it == val2) + return 0; + + gain_sel = VEML6031X00_GAIN_SEL(pd_div4, gain_reg); + prev_gain = iio_gts_find_gain_by_sel(gts, gain_sel); + if (prev_gain < 0) + return prev_gain; + + ret = iio_gts_find_new_gain_by_gain_time_min(gts, prev_gain, prev_it, val2, + &new_gain, &gain_in_range); + if (ret) + return ret; + + if (!gain_in_range) + dev_dbg(dev, "Optimal gain out of range\n"); + + ret = iio_gts_find_sel_by_int_time(gts, val2); + if (ret < 0) + return ret; + + ret = regmap_field_write(data->rf.it, ret); + if (ret) + return ret; + + gain_sel = iio_gts_find_sel_by_gain(gts, new_gain); + if (gain_sel < 0) + return gain_sel; + + return veml6031x00_write_gain(data, gain_sel); +} + +static int veml6031x00_read_period(struct iio_dev *iio, int *val) +{ + struct veml6031x00_data *data = iio_priv(iio); + unsigned int regval; + int ret; + + ret = regmap_field_read(data->rf.pers, ®val); + if (ret) + return ret; + + *val = BIT(regval); + + return IIO_VAL_INT; +} + +static int veml6031x00_write_period(struct iio_dev *iio, int val) +{ + struct veml6031x00_data *data = iio_priv(iio); + + if (val < 0 || val > 8 || !is_power_of_2(val)) + return -EINVAL; + + return regmap_field_write(data->rf.pers, ilog2(val)); +} + +static int veml6031x00_set_scale(struct iio_dev *iio, int val, int val2) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct iio_gts *gts = &data->gts; + int gain_sel, it_sel, ret; + + ret = iio_gts_find_gain_time_sel_for_scale(gts, val, val2, &gain_sel, &it_sel); + if (ret) + return ret; + + guard(mutex)(&data->scale_lock); + + ret = regmap_field_write(data->rf.it, it_sel); + if (ret) + return ret; + + return veml6031x00_write_gain(data, gain_sel); +} + +static int veml6031x00_get_scale(struct veml6031x00_data *data, int *val, int *val2) +{ + unsigned int gain_reg, pd_div4, it_reg; + int gain, it, ret, sel; + + scoped_guard(mutex, &data->scale_lock) { + ret = regmap_field_read(data->rf.gain, &gain_reg); + if (ret) + return ret; + + ret = regmap_field_read(data->rf.pd_div4, &pd_div4); + if (ret) + return ret; + + sel = VEML6031X00_GAIN_SEL(pd_div4, gain_reg); + gain = iio_gts_find_gain_by_sel(&data->gts, sel); + if (gain < 0) + return gain; + + ret = regmap_field_read(data->rf.it, &it_reg); + if (ret) + return ret; + } + + it = iio_gts_find_int_time_by_sel(&data->gts, it_reg); + if (it < 0) + return it; + + ret = iio_gts_get_scale(&data->gts, gain, it, val, val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int veml6031x00_read_th(struct iio_dev *iio, enum iio_event_direction dir, + int *val, int *val2) +{ + struct veml6031x00_data *data = iio_priv(iio); + __le16 regval; + int ret; + + if (dir == IIO_EV_DIR_RISING) + ret = regmap_bulk_read(data->regmap, VEML6031X00_REG_WH_L, + ®val, sizeof(regval)); + else + ret = regmap_bulk_read(data->regmap, VEML6031X00_REG_WL_L, + ®val, sizeof(regval)); + if (ret) + return ret; + + *val = le16_to_cpu(regval); + + return IIO_VAL_INT; +} + +static int veml6031x00_write_th(struct iio_dev *iio, enum iio_event_direction dir, + int val, int val2) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + __le16 regval; + int ret; + + if (val < 0 || val > U16_MAX || val2) + return -EINVAL; + + regval = cpu_to_le16(val); + + if (dir == IIO_EV_DIR_RISING) { + ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WH_L, + ®val, sizeof(regval)); + if (ret) + dev_dbg(dev, "Failed to set high threshold %d\n", ret); + } else { + ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WL_L, + ®val, sizeof(regval)); + if (ret) + dev_dbg(dev, "Failed to set low threshold %d\n", ret); + } + + return ret; +} + +static int veml6031x00_single_read(struct iio_dev *iio, enum iio_chan_type type, + int *val) +{ + struct veml6031x00_data *data = iio_priv(iio); + unsigned int addr; + int it_usec, ret; + __le16 regval; + + switch (type) { + case IIO_LIGHT: + addr = VEML6031X00_REG_ALS_L; + break; + case IIO_INTENSITY: + addr = VEML6031X00_REG_IR_L; + break; + default: + return -EINVAL; + } + + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + guard(mutex)(&data->scale_lock); + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(data->regmap), pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = __veml6031x00_get_it(data, &it_usec); + if (ret < 0) + return ret; + + /* integration time + 10% to ensure completion */ + fsleep(it_usec + (it_usec / 10)); + + ret = regmap_bulk_read(data->regmap, addr, ®val, sizeof(regval)); + if (ret) + return ret; + + *val = le16_to_cpu(regval); + + return IIO_VAL_INT; +} + +static int veml6031x00_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct veml6031x00_data *data = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return veml6031x00_single_read(iio, chan->type, val); + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + return veml6031x00_get_it(data, val2); + case IIO_CHAN_INFO_SCALE: + return veml6031x00_get_scale(data, val, val2); + default: + return -EINVAL; + } +} + +static int veml6031x00_read_avail(struct iio_dev *iio, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct veml6031x00_data *data = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static int veml6031x00_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return veml6031x00_set_it(iio, val, val2); + case IIO_CHAN_INFO_SCALE: + return veml6031x00_set_scale(iio, val, val2); + default: + return -EINVAL; + } +} + +static int veml6031x00_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int veml6031x00_set_interrupt(struct veml6031x00_data *data, bool state) + __must_hold(&data->irq_lock) +{ + int ret; + + lockdep_assert_held(&data->irq_lock); + + if (state) { + data->interrupt_users++; + if (data->interrupt_users > 1) + return 0; + } else { + if (!data->interrupt_users) + return 0; + + data->interrupt_users--; + if (data->interrupt_users > 0) + return 0; + } + + ret = regmap_field_write(data->rf.int_en, state); + if (ret) { + if (state) + data->interrupt_users--; + else + data->interrupt_users++; + } + + return ret; +} + +static int veml6031x00_read_event_val(struct iio_dev *iio, + 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) +{ + switch (type) { + case IIO_EV_TYPE_THRESH: + if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD) + return veml6031x00_read_period(iio, val); + + return veml6031x00_read_th(iio, dir, val, val2); + default: + return -EINVAL; + } +} + +static int veml6031x00_write_event_val(struct iio_dev *iio, + 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) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + return veml6031x00_write_th(iio, dir, val, val2); + case IIO_EV_INFO_PERIOD: + return veml6031x00_write_period(iio, val); + default: + return -EINVAL; + } +} + +static int veml6031x00_read_event_config(struct iio_dev *iio, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct veml6031x00_data *data = iio_priv(iio); + + guard(mutex)(&data->irq_lock); + + return data->ev_en; +} + +static int veml6031x00_event_enable(struct veml6031x00_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = veml6031x00_set_interrupt(data, true); + if (ret) { + pm_runtime_put_autosuspend(dev); + return ret; + } + + data->ev_en = true; + + return 0; +} + +static int veml6031x00_event_disable(struct veml6031x00_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = veml6031x00_set_interrupt(data, false); + if (ret) + return ret; + + data->ev_en = false; + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int veml6031x00_write_event_config(struct iio_dev *iio, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct veml6031x00_data *data = iio_priv(iio); + + guard(mutex)(&data->irq_lock); + + /* avoid multiple increments/decrements from one source */ + if (state == data->ev_en) + return 0; + + if (state) + return veml6031x00_event_enable(data); + + return veml6031x00_event_disable(data); +} + +static void veml6031x00_disable_event_action(void *arg) +{ + struct veml6031x00_data *data = arg; + struct device *dev = regmap_get_device(data->regmap); + int ret; + + guard(mutex)(&data->irq_lock); + + if (!data->ev_en) + return; + + ret = veml6031x00_set_interrupt(data, false); + if (ret) + dev_err(dev, "Failed to disable events: %d\n", ret); + else + data->ev_en = false; + + pm_runtime_put_sync(dev); +} + +static const struct iio_info veml6031x00_info = { + .read_raw = veml6031x00_read_raw, + .read_avail = veml6031x00_read_avail, + .write_raw = veml6031x00_write_raw, + .write_raw_get_fmt = veml6031x00_write_raw_get_fmt, + .read_event_value = veml6031x00_read_event_val, + .write_event_value = veml6031x00_write_event_val, + .read_event_config = veml6031x00_read_event_config, + .write_event_config = veml6031x00_write_event_config, + .event_attrs = &veml6031x00_event_attr_group, +}; + +static const struct iio_info veml6031x00_info_no_irq = { + .read_raw = veml6031x00_read_raw, + .read_avail = veml6031x00_read_avail, + .write_raw = veml6031x00_write_raw, + .write_raw_get_fmt = veml6031x00_write_raw_get_fmt, +}; + +static irqreturn_t veml6031x00_irq(int irq, void *private) +{ + struct iio_dev *iio = private; + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + s64 timestamp; + unsigned int regval; + int ret; + bool trigger_poll; + + ret = pm_runtime_get_if_active(dev); + if (ret <= 0) + return IRQ_NONE; + + scoped_guard(mutex, &data->irq_lock) { + ret = regmap_read(data->regmap, VEML6031X00_REG_INT, ®val); + if (ret) { + dev_dbg(dev, "Failed to read interrupt register %d\n", ret); + pm_runtime_put(dev); + return IRQ_NONE; + } + + if (!(regval & VEML6031X00_INT_MASK)) { + pm_runtime_put(dev); + return IRQ_NONE; + } + + timestamp = iio_get_time_ns(iio); + + if (data->ev_en && + (regval & (VEML6031X00_INT_TH_H | VEML6031X00_INT_TH_L))) { + if (regval & VEML6031X00_INT_TH_H) + iio_push_event(iio, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + if (regval & VEML6031X00_INT_TH_L) + iio_push_event(iio, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + } + + trigger_poll = data->trig_en && (regval & VEML6031X00_INT_DRDY); + } + + /* + * iio_trigger_poll_nested() must be called with irq_lock released: + * it runs trig_handler() synchronously in this thread, which calls + * reenable() on completion, and that callback also takes irq_lock. + * iio_pollfunc_store_time() is not called for our own trigger, so the + * timestamp is stored here. + */ + if (trigger_poll) { + iio->pollfunc->timestamp = timestamp; + iio_trigger_poll_nested(data->trig); + } + + pm_runtime_put(dev); + + return IRQ_HANDLED; +} + +static int veml6031x00_buffer_preenable(struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + int ret, it_usec; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = veml6031x00_get_it(data, &it_usec); + if (ret < 0) { + pm_runtime_put_autosuspend(dev); + return ret; + } + + /* + * Wait one integration period + 10% margin so the first triggered + * read does not race with the sensor completing its first conversion + * after power-on. + */ + fsleep(it_usec + (it_usec / 10)); + + return 0; +} + +static int veml6031x00_buffer_postdisable(struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); + + return 0; +} + +static int __veml6031x00_set_trigger_state(struct veml6031x00_data *data, + bool state) + __must_hold(&data->irq_lock) +{ + int ret; + + lockdep_assert_held(&data->irq_lock); + + ret = veml6031x00_set_interrupt(data, state); + if (ret) + return ret; + + /* The AF bit must be updated before updating AF_TRIG */ + ret = regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_AF, state); + if (ret) + goto disable_trigger; + + ret = regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_AF_TRIG, state); + if (ret) { + regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_AF, false); + goto disable_trigger; + } + + data->trig_en = state; + + return 0; + +disable_trigger: + data->trig_en = false; + if (state) + veml6031x00_set_interrupt(data, false); + return ret; +} + +static int veml6031x00_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *iio = iio_trigger_get_drvdata(trig); + struct veml6031x00_data *data = iio_priv(iio); + + guard(mutex)(&data->irq_lock); + + if (state == data->trig_en) + return 0; + + return __veml6031x00_set_trigger_state(data, state); +} + +static const struct iio_buffer_setup_ops veml6031x00_buffer_setup_ops = { + .preenable = veml6031x00_buffer_preenable, + .postdisable = veml6031x00_buffer_postdisable, +}; + +static void veml6031x00_trigger_reenable(struct iio_trigger *trig) +{ + struct iio_dev *iio = iio_trigger_get_drvdata(trig); + struct veml6031x00_data *data = iio_priv(iio); + int ret; + + guard(mutex)(&data->irq_lock); + + if (!data->trig_en) + return; + + ret = regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_AF_TRIG, true); + if (ret) + dev_err(regmap_get_device(data->regmap), + "Failed to reenable trigger: %d\n", ret); +} + +static const struct iio_trigger_ops veml6031x00_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = veml6031x00_set_trigger_state, + .reenable = veml6031x00_trigger_reenable, +}; + +static irqreturn_t veml6031x00_trig_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct veml6031x00_data *data = iio_priv(iio); + struct regmap *map = data->regmap; + IIO_DECLARE_BUFFER_WITH_TS(__le16, scan, 2) = { }; + unsigned int i; + int ch, ret; + + if (test_bit(VEML6031X00_SCAN_ALS, iio->active_scan_mask) && + test_bit(VEML6031X00_SCAN_IR, iio->active_scan_mask)) { + ret = regmap_bulk_read(map, VEML6031X00_REG_ALS_L, + scan, 2 * sizeof(*scan)); + if (ret) + goto done; + } else { + i = 0; + iio_for_each_active_channel(iio, ch) { + ret = regmap_bulk_read(map, iio->channels[ch].address, + &scan[i++], sizeof(*scan)); + if (ret) + goto done; + } + } + + iio_push_to_buffers_with_ts(iio, scan, sizeof(scan), pf->timestamp); + +done: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + +static int veml6031x00_validate_part_id(struct veml6031x00_data *data) +{ + struct regmap *map = data->regmap; + struct device *dev = regmap_get_device(map); + unsigned int part_id; + int ret; + __le16 regval; + + ret = regmap_bulk_read(map, VEML6031X00_REG_ID_L, ®val, sizeof(regval)); + if (ret) + return dev_err_probe(dev, ret, "Failed to read ID\n"); + + part_id = le16_to_cpu(regval); + if (part_id != data->chip->part_id) + dev_info(dev, "Unknown or unexpected ID %04x\n", part_id); + + return 0; +} + +static int veml6031x00_hw_init(struct veml6031x00_data *data) +{ + struct regmap *map = data->regmap; + struct device *dev = regmap_get_device(map); + unsigned int val; + __le16 regval; + int ret; + + ret = regmap_assign_bits(data->regmap, VEML6031X00_REG_CONF0, + VEML6031X00_CONF0_AF | VEML6031X00_CONF0_AF_TRIG, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to disable trigger\n"); + + ret = regmap_set_bits(data->regmap, VEML6031X00_REG_CONF1, + VEML6031X00_CONF1_ALS_CAL); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable internal calibration\n"); + + regval = 0; + ret = regmap_bulk_write(map, VEML6031X00_REG_WL_L, ®val, sizeof(regval)); + if (ret) + return dev_err_probe(dev, ret, "Failed to set low threshold\n"); + + regval = cpu_to_le16(U16_MAX); + ret = regmap_bulk_write(map, VEML6031X00_REG_WH_L, ®val, sizeof(regval)); + if (ret) + return dev_err_probe(dev, ret, "Failed to set high threshold\n"); + + ret = regmap_field_write(data->rf.int_en, 0); + if (ret) + return ret; + + ret = regmap_read(map, VEML6031X00_REG_INT, &val); + if (ret) + return dev_err_probe(dev, ret, "Failed to clear interrupts\n"); + + return 0; +} + +static int veml6031x00_setup_trigger(struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + + data->trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", + iio->name, iio_device_id(iio)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &veml6031x00_trigger_ops; + iio_trigger_set_drvdata(data->trig, iio); + + return devm_iio_trigger_register(dev, data->trig); +} + +static int veml6031x00_init_iiodev(struct i2c_client *i2c, struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + iio->name = data->chip->name; + iio->modes = INDIO_DIRECT_MODE; + + if (!i2c->irq) { + iio->channels = veml6031x00_channels; + iio->num_channels = ARRAY_SIZE(veml6031x00_channels); + iio->info = &veml6031x00_info_no_irq; + + return 0; + } + + iio->channels = veml6031x00_channels_irq; + iio->num_channels = ARRAY_SIZE(veml6031x00_channels_irq); + iio->info = &veml6031x00_info; + + ret = veml6031x00_setup_trigger(iio); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, veml6031x00_disable_event_action, data); +} + +static int veml6031x00_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct veml6031x00_data *data; + struct iio_dev *iio; + int ret; + + iio = devm_iio_device_alloc(dev, sizeof(*data)); + if (!iio) + return -ENOMEM; + + data = iio_priv(iio); + i2c_set_clientdata(i2c, iio); + + data->chip = i2c_get_match_data(i2c); + if (!data->chip) + return dev_err_probe(dev, -ENODATA, "Failed to get chip data\n"); + + ret = devm_mutex_init(dev, &data->scale_lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &data->irq_lock); + if (ret) + return ret; + + data->regmap = devm_regmap_init_i2c(i2c, &veml6031x00_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to set regmap\n"); + + ret = veml6031x00_regfield_init(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to init regfield\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + /* The device starts in power down mode by default */ + ret = veml6031x00_set_power(data, true); + if (ret) + return dev_err_probe(dev, ret, "Failed to power on the device\n"); + + ret = devm_add_action_or_reset(dev, veml6031x00_als_shutdown_action, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add shutdown action\n"); + + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + + ret = veml6031x00_validate_part_id(data); + if (ret) + return ret; + + ret = veml6031x00_hw_init(data); + if (ret) + return ret; + + /* Max resolution = 6.9632 lx/cnt for gain = 0.125 and IT = 3.125ms */ + ret = devm_iio_init_iio_gts(dev, 6, 963200000, + veml6031x00_gain_sel, + ARRAY_SIZE(veml6031x00_gain_sel), + veml6031x00_it_sel, + ARRAY_SIZE(veml6031x00_it_sel), + &data->gts); + if (ret) + return dev_err_probe(dev, ret, "Failed to init IIO GTS\n"); + + ret = veml6031x00_init_iiodev(i2c, iio); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize IIO device\n"); + + ret = devm_iio_triggered_buffer_setup(dev, iio, + iio_pollfunc_store_time, + veml6031x00_trig_handler, + &veml6031x00_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to register triggered buffer\n"); + + if (i2c->irq) { + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, veml6031x00_irq, + IRQF_ONESHOT, iio->name, iio); + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ\n"); + } + + ret = devm_iio_device_register(dev, iio); + if (ret) + return dev_err_probe(dev, ret, "Failed to register IIO device\n"); + + return 0; +} + +static int veml6031x00_runtime_suspend(struct device *dev) +{ + struct veml6031x00_data *data = iio_priv(dev_get_drvdata(dev)); + + return veml6031x00_set_power(data, false); +} + +static int veml6031x00_runtime_resume(struct device *dev) +{ + struct veml6031x00_data *data = iio_priv(dev_get_drvdata(dev)); + + return veml6031x00_set_power(data, true); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(veml6031x00_pm_ops, + veml6031x00_runtime_suspend, + veml6031x00_runtime_resume, + NULL); + +static const struct veml6031x00_chip veml6031x00_chip = { + .name = "veml6031x00", + .part_id = 0x0001, +}; + +static const struct veml6031x00_chip veml6031x01_chip = { + .name = "veml6031x01", + .part_id = 0x0001, +}; + +static const struct veml6031x00_chip veml60311x00_chip = { + .name = "veml60311x00", + .part_id = 0x1001, +}; + +static const struct veml6031x00_chip veml60311x01_chip = { + .name = "veml60311x01", + .part_id = 0x1001, +}; + +static const struct of_device_id veml6031x00_of_match[] = { + { + .compatible = "vishay,veml6031x00", + .data = &veml6031x00_chip, + }, + { + .compatible = "vishay,veml6031x01", + .data = &veml6031x01_chip, + }, + { + .compatible = "vishay,veml60311x00", + .data = &veml60311x00_chip, + }, + { + .compatible = "vishay,veml60311x01", + .data = &veml60311x01_chip, + }, + { } +}; +MODULE_DEVICE_TABLE(of, veml6031x00_of_match); + +static const struct i2c_device_id veml6031x00_id[] = { + { + .name = "veml6031x00", + .driver_data = (kernel_ulong_t)&veml6031x00_chip, + }, + { + .name = "veml6031x01", + .driver_data = (kernel_ulong_t)&veml6031x01_chip, + }, + { + .name = "veml60311x00", + .driver_data = (kernel_ulong_t)&veml60311x00_chip, + }, + { + .name = "veml60311x01", + .driver_data = (kernel_ulong_t)&veml60311x01_chip, + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, veml6031x00_id); + +static struct i2c_driver veml6031x00_driver = { + .driver = { + .name = "veml6031x00", + .of_match_table = veml6031x00_of_match, + .pm = pm_ptr(&veml6031x00_pm_ops), + }, + .probe = veml6031x00_probe, + .id_table = veml6031x00_id, +}; +module_i2c_driver(veml6031x00_driver); + +MODULE_AUTHOR("Javier Carrasco "); +MODULE_DESCRIPTION("VEML6031X00 Ambient Light Sensor"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 8b0c07f82602..cc5be1fa91ea 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -44,8 +44,7 @@ #define AK8975_REG_INFO 0x01 #define AK8975_REG_ST1 0x02 -#define AK8975_REG_ST1_DRDY_SHIFT 0 -#define AK8975_REG_ST1_DRDY_MASK (1 << AK8975_REG_ST1_DRDY_SHIFT) +#define AK8975_REG_ST1_DRDY_MASK BIT(0) #define AK8975_REG_HXL 0x03 #define AK8975_REG_HXH 0x04 @@ -54,15 +53,12 @@ #define AK8975_REG_HZL 0x07 #define AK8975_REG_HZH 0x08 #define AK8975_REG_ST2 0x09 -#define AK8975_REG_ST2_DERR_SHIFT 2 -#define AK8975_REG_ST2_DERR_MASK (1 << AK8975_REG_ST2_DERR_SHIFT) +#define AK8975_REG_ST2_DERR_MASK BIT(2) -#define AK8975_REG_ST2_HOFL_SHIFT 3 -#define AK8975_REG_ST2_HOFL_MASK (1 << AK8975_REG_ST2_HOFL_SHIFT) +#define AK8975_REG_ST2_HOFL_MASK BIT(3) #define AK8975_REG_CNTL 0x0A -#define AK8975_REG_CNTL_MODE_SHIFT 0 -#define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT) +#define AK8975_REG_CNTL_MODE_MASK GENMASK(3, 0) #define AK8975_REG_CNTL_MODE_POWER_DOWN 0x00 #define AK8975_REG_CNTL_MODE_ONCE 0x01 #define AK8975_REG_CNTL_MODE_SELF_TEST 0x08 @@ -94,8 +90,7 @@ #define AK09912_REG_ST1 0x10 -#define AK09912_REG_ST1_DRDY_SHIFT 0 -#define AK09912_REG_ST1_DRDY_MASK (1 << AK09912_REG_ST1_DRDY_SHIFT) +#define AK09912_REG_ST1_DRDY_MASK BIT(0) #define AK09912_REG_HXL 0x11 #define AK09912_REG_HXH 0x12 @@ -106,8 +101,7 @@ #define AK09912_REG_TMPS 0x17 #define AK09912_REG_ST2 0x18 -#define AK09912_REG_ST2_HOFL_SHIFT 3 -#define AK09912_REG_ST2_HOFL_MASK (1 << AK09912_REG_ST2_HOFL_SHIFT) +#define AK09912_REG_ST2_HOFL_MASK BIT(3) #define AK09912_REG_CNTL1 0x30 @@ -116,8 +110,7 @@ #define AK09912_REG_CNTL_MODE_ONCE 0x01 #define AK09912_REG_CNTL_MODE_SELF_TEST 0x10 #define AK09912_REG_CNTL_MODE_FUSE_ROM 0x1F -#define AK09912_REG_CNTL2_MODE_SHIFT 0 -#define AK09912_REG_CNTL2_MODE_MASK (0x1F << AK09912_REG_CNTL2_MODE_SHIFT) +#define AK09912_REG_CNTL2_MODE_MASK GENMASK(4, 0) #define AK09912_REG_CNTL3 0x32 @@ -237,6 +230,13 @@ enum ak_ctrl_mode { MODE_END, }; +enum ak_chan_index { + AK8975_CHAN_X, + AK8975_CHAN_Y, + AK8975_CHAN_Z, + AK8975_CHAN_TS, +}; + struct ak_def { enum asahi_compass_chipset type; long (*raw_to_gauss)(u16 data); @@ -418,6 +418,7 @@ struct ak8975_data { wait_queue_head_t data_ready_queue; unsigned long flags; u8 cntl_cache; + bool powered_on; struct iio_mount_matrix orientation; struct regulator *vdd; struct regulator *vid; @@ -430,20 +431,19 @@ struct ak8975_data { }; /* Enable attached power regulator if any. */ -static int ak8975_power_on(const struct ak8975_data *data) +static int ak8975_power_on(struct ak8975_data *data) { + struct device *dev = &data->client->dev; int ret; ret = regulator_enable(data->vdd); if (ret) { - dev_warn(&data->client->dev, - "Failed to enable specified Vdd supply\n"); + dev_warn(dev, "Failed to enable specified Vdd supply\n"); return ret; } ret = regulator_enable(data->vid); if (ret) { - dev_warn(&data->client->dev, - "Failed to enable specified Vid supply\n"); + dev_warn(dev, "Failed to enable specified Vid supply\n"); regulator_disable(data->vdd); return ret; } @@ -457,16 +457,23 @@ static int ak8975_power_on(const struct ak8975_data *data) */ fsleep(500); + data->powered_on = true; + return 0; } /* Disable attached power regulator if any. */ -static void ak8975_power_off(const struct ak8975_data *data) +static void ak8975_power_off(struct ak8975_data *data) { + if (!data->powered_on) + return; + gpiod_set_value_cansleep(data->reset_gpiod, 1); regulator_disable(data->vid); regulator_disable(data->vdd); + + data->powered_on = false; } /* @@ -494,14 +501,10 @@ static int ak8975_who_i_am(const struct ak8975_data *data, AK09912_REG_WIA1, sizeof(wia_val), wia_val); - if (ret < 0) { - dev_err(&client->dev, "Error reading WIA\n"); - return ret; - } - if (ret != sizeof(wia_val)) { - dev_err(&client->dev, "Error reading WIA\n"); - return -EIO; - } + if (ret < 0) + return dev_err_probe(&client->dev, ret, "Error reading WIA\n"); + if (ret != sizeof(wia_val)) + return dev_err_probe(&client->dev, -EIO, "Error reading WIA\n"); if (wia_val[0] != AK8975_DEVICE_ID) return -ENODEV; @@ -577,6 +580,7 @@ static irqreturn_t ak8975_irq_handler(int irq, void *data) static int ak8975_setup_irq(struct ak8975_data *data) { struct i2c_client *client = data->client; + struct device *dev = &client->dev; int irq; int ret; @@ -587,9 +591,8 @@ static int ak8975_setup_irq(struct ak8975_data *data) else irq = gpiod_to_irq(data->eoc_gpiod); - ret = devm_request_irq(&client->dev, irq, ak8975_irq_handler, - IRQF_TRIGGER_RISING, - dev_name(&client->dev), data); + ret = devm_request_irq(dev, irq, ak8975_irq_handler, IRQF_TRIGGER_RISING, + dev_name(dev), data); if (ret) return ret; @@ -605,43 +608,33 @@ static int ak8975_setup_irq(struct ak8975_data *data) static int ak8975_setup(struct ak8975_data *data) { struct i2c_client *client = data->client; + struct device *dev = &client->dev; int ret; /* Write the fused rom access mode. */ ret = ak8975_set_mode(data, FUSE_ROM); - if (ret < 0) { - dev_err(&client->dev, "Error in setting fuse access mode\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error in setting fuse access mode\n"); /* Get asa data and store in the device data. */ ret = i2c_smbus_read_i2c_block_data_or_emulated(client, data->def->ctrl_regs[ASA_BASE], sizeof(data->asa), data->asa); - if (ret < 0) { - dev_err(&client->dev, "Not able to read asa data\n"); - return ret; - } - if (ret != sizeof(data->asa)) { - dev_err(&client->dev, "Error reading asa data\n"); - return -EIO; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Not able to read asa data\n"); + if (ret != sizeof(data->asa)) + return dev_err_probe(dev, -EIO, "Error reading asa data\n"); /* After reading fuse ROM data set power-down mode */ ret = ak8975_set_mode(data, POWER_DOWN); - if (ret < 0) { - dev_err(&client->dev, "Error in setting power-down mode\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error in setting power-down mode\n"); if (data->eoc_gpiod || client->irq > 0) { ret = ak8975_setup_irq(data); - if (ret < 0) { - dev_err(&client->dev, - "Error setting data ready interrupt\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error setting data ready interrupt\n"); } data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]); @@ -745,10 +738,11 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) struct ak8975_data *data = iio_priv(indio_dev); const struct i2c_client *client = data->client; const struct ak_def *def = data->def; + struct device *dev = &data->client->dev; __le16 rval; int ret; - pm_runtime_get_sync(&data->client->dev); + pm_runtime_get_sync(dev); mutex_lock(&data->lock); @@ -770,20 +764,20 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) /* Read out ST2 for release lock on measurement data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); if (ret < 0) { - dev_err(&client->dev, "Error in reading ST2\n"); + dev_err(dev, "Error in reading ST2\n"); goto exit; } if (ret & (data->def->ctrl_masks[ST2_DERR] | data->def->ctrl_masks[ST2_HOFL])) { - dev_err(&client->dev, "ST2 status error 0x%x\n", ret); + dev_err(dev, "ST2 status error 0x%x\n", ret); ret = -EINVAL; goto exit; } mutex_unlock(&data->lock); - pm_runtime_put_autosuspend(&data->client->dev); + pm_runtime_put_autosuspend(dev); /* Swap bytes and convert to valid range. */ *val = clamp_t(s16, le16_to_cpu(rval), -def->range, def->range); @@ -792,8 +786,8 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) exit: mutex_unlock(&data->lock); - pm_runtime_put_autosuspend(&data->client->dev); - dev_err(&client->dev, "Error in reading axis\n"); + pm_runtime_put_autosuspend(dev); + dev_err(dev, "Error in reading axis\n"); return ret; } @@ -848,11 +842,16 @@ static const struct iio_chan_spec_ext_info ak8975_ext_info[] = { } static const struct iio_chan_spec ak8975_channels[] = { - AK8975_CHANNEL(X, 0), AK8975_CHANNEL(Y, 1), AK8975_CHANNEL(Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), + AK8975_CHANNEL(X, AK8975_CHAN_X), + AK8975_CHANNEL(Y, AK8975_CHAN_Y), + AK8975_CHANNEL(Z, AK8975_CHAN_Z), + IIO_CHAN_SOFT_TIMESTAMP(AK8975_CHAN_TS), }; -static const unsigned long ak8975_scan_masks[] = { 0x7, 0 }; +static const unsigned long ak8975_scan_masks[] = { + BIT(AK8975_CHAN_X) | BIT(AK8975_CHAN_Y) | BIT(AK8975_CHAN_Z), + 0 +}; static const struct iio_info ak8975_info = { .read_raw = &ak8975_read_raw, @@ -934,9 +933,23 @@ static const struct iio_buffer_setup_ops ak8975_buffer_setup_ops = { .preenable = ak8975_buffer_preenable, .postdisable = ak8975_buffer_postdisable, }; + +static void devm_ak8975_power_off(void *data) +{ + struct ak8975_data *ak = data; + + if (!ak->powered_on) + return; + + /* Soft-stop the chip before hard-stopping the regulators */ + ak8975_set_mode(data, POWER_DOWN); + ak8975_power_off(data); +} + static int ak8975_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct device *dev = &client->dev; struct ak8975_data *data; struct iio_dev *indio_dev; struct gpio_desc *eoc_gpiod; @@ -949,7 +962,7 @@ static int ak8975_probe(struct i2c_client *client) * We may not have a GPIO based IRQ to scan, that is fine, we will * poll if so. */ - eoc_gpiod = devm_gpiod_get_optional(&client->dev, NULL, GPIOD_IN); + eoc_gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); if (IS_ERR(eoc_gpiod)) return PTR_ERR(eoc_gpiod); gpiod_set_consumer_name(eoc_gpiod, "ak_8975"); @@ -959,13 +972,12 @@ static int ak8975_probe(struct i2c_client *client) * deassert reset on ak8975_power_on() and assert reset on * ak8975_power_off(). */ - reset_gpiod = devm_gpiod_get_optional(&client->dev, - "reset", GPIOD_OUT_HIGH); + reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpiod)) return PTR_ERR(reset_gpiod); /* Register with IIO */ - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (indio_dev == NULL) return -ENOMEM; @@ -977,7 +989,7 @@ static int ak8975_probe(struct i2c_client *client) data->reset_gpiod = reset_gpiod; data->eoc_irq = 0; - ret = iio_read_mount_matrix(&client->dev, &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; @@ -987,16 +999,16 @@ static int ak8975_probe(struct i2c_client *client) return -ENODEV; /* If enumerated via firmware node, fix the ABI */ - if (dev_fwnode(&client->dev)) - name = dev_name(&client->dev); + if (dev_fwnode(dev)) + name = dev_name(dev); else name = id->name; /* Fetch the regulators */ - data->vdd = devm_regulator_get(&client->dev, "vdd"); + data->vdd = devm_regulator_get(dev, "vdd"); if (IS_ERR(data->vdd)) return PTR_ERR(data->vdd); - data->vid = devm_regulator_get(&client->dev, "vid"); + data->vid = devm_regulator_get(dev, "vid"); if (IS_ERR(data->vid)) return PTR_ERR(data->vid); @@ -1004,21 +1016,25 @@ static int ak8975_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(dev, devm_ak8975_power_off, data); + if (ret) + return ret; + ret = ak8975_who_i_am(data, data->def->type); - if (ret) { - dev_err(&client->dev, "Unexpected device\n"); - goto power_off; - } - dev_dbg(&client->dev, "Asahi compass chip %s\n", name); + if (ret) + return dev_err_probe(dev, ret, "Unexpected device\n"); + + dev_dbg(dev, "Asahi compass chip %s\n", name); /* Perform some basic start-of-day setup of the device. */ ret = ak8975_setup(data); - if (ret) { - dev_err(&client->dev, "%s initialization fails\n", name); - goto power_off; - } + if (ret) + return dev_err_probe(dev, ret, "%s initialization fails\n", name); + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; - mutex_init(&data->lock); indio_dev->channels = ak8975_channels; indio_dev->num_channels = ARRAY_SIZE(ak8975_channels); indio_dev->info = &ak8975_info; @@ -1026,52 +1042,29 @@ static int ak8975_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = name; - ret = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger, - &ak8975_buffer_setup_ops); - if (ret) { - dev_err(&client->dev, "triggered buffer setup failed\n"); - goto power_off; - } + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + ak8975_handle_trigger, + &ak8975_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, "triggered buffer setup failed\n"); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&client->dev, "device register failed\n"); - goto cleanup_buffer; - } + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "device register failed\n"); - /* Enable runtime PM */ - pm_runtime_get_noresume(&client->dev); - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); /* * The device comes online in 500us, so add two orders of magnitude * of delay before autosuspending: 50 ms. */ - pm_runtime_set_autosuspend_delay(&client->dev, 50); - pm_runtime_use_autosuspend(&client->dev); - pm_runtime_put(&client->dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); return 0; - -cleanup_buffer: - iio_triggered_buffer_cleanup(indio_dev); -power_off: - ak8975_power_off(data); - return ret; -} - -static void ak8975_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ak8975_data *data = iio_priv(indio_dev); - - pm_runtime_get_sync(&client->dev); - pm_runtime_put_noidle(&client->dev); - pm_runtime_disable(&client->dev); - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - ak8975_set_mode(data, POWER_DOWN); - ak8975_power_off(data); } static int ak8975_runtime_suspend(struct device *dev) @@ -1084,7 +1077,7 @@ static int ak8975_runtime_suspend(struct device *dev) /* Set the device in power down if it wasn't already */ ret = ak8975_set_mode(data, POWER_DOWN); if (ret < 0) { - dev_err(&client->dev, "Error in setting power-down mode\n"); + dev_err(dev, "Error in setting power-down mode\n"); return ret; } /* Next cut the regulators */ @@ -1108,7 +1101,7 @@ static int ak8975_runtime_resume(struct device *dev) */ ret = ak8975_set_mode(data, POWER_DOWN); if (ret < 0) { - dev_err(&client->dev, "Error in setting power-down mode\n"); + dev_err(dev, "Error in setting power-down mode\n"); return ret; } @@ -1165,7 +1158,6 @@ static struct i2c_driver ak8975_driver = { .acpi_match_table = ak_acpi_match, }, .probe = ak8975_probe, - .remove = ak8975_remove, .id_table = ak8975_id, }; module_i2c_driver(ak8975_driver); diff --git a/drivers/iio/position/iqs624-pos.c b/drivers/iio/position/iqs624-pos.c index 8239239c6ee2..d2aa2df71457 100644 --- a/drivers/iio/position/iqs624-pos.c +++ b/drivers/iio/position/iqs624-pos.c @@ -96,19 +96,6 @@ static int iqs624_pos_notifier(struct notifier_block *notifier, return ret; } -static void iqs624_pos_notifier_unregister(void *context) -{ - struct iqs624_pos_private *iqs624_pos = context; - struct iio_dev *indio_dev = iqs624_pos->indio_dev; - int ret; - - ret = blocking_notifier_chain_unregister(&iqs624_pos->iqs62x->nh, - &iqs624_pos->notifier); - if (ret) - dev_err(indio_dev->dev.parent, - "Failed to unregister notifier: %d\n", ret); -} - static int iqs624_pos_angle_get(struct iqs62x_core *iqs62x, unsigned int *val) { int ret; @@ -255,19 +242,14 @@ static int iqs624_pos_probe(struct platform_device *pdev) mutex_init(&iqs624_pos->lock); iqs624_pos->notifier.notifier_call = iqs624_pos_notifier; - ret = blocking_notifier_chain_register(&iqs624_pos->iqs62x->nh, - &iqs624_pos->notifier); + ret = devm_blocking_notifier_chain_register(&pdev->dev, + &iqs624_pos->iqs62x->nh, + &iqs624_pos->notifier); if (ret) { dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret); return ret; } - ret = devm_add_action_or_reset(&pdev->dev, - iqs624_pos_notifier_unregister, - iqs624_pos); - if (ret) - return ret; - return devm_iio_device_register(&pdev->dev, indio_dev); } diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c index 974e4447fc2c..675b78677baa 100644 --- a/drivers/iio/potentiometer/x9250.c +++ b/drivers/iio/potentiometer/x9250.c @@ -155,10 +155,15 @@ static const char *const x9250_regulator_names[] = { static int x9250_probe(struct spi_device *spi) { + const struct x9250_cfg *cfg; struct iio_dev *indio_dev; struct x9250 *x9250; int ret; + cfg = spi_get_device_match_data(spi); + if (!cfg) + return -ENODATA; + ret = devm_regulator_bulk_get_enable(&spi->dev, ARRAY_SIZE(x9250_regulator_names), x9250_regulator_names); if (ret) @@ -176,7 +181,7 @@ static int x9250_probe(struct spi_device *spi) x9250 = iio_priv(indio_dev); x9250->spi = spi; - x9250->cfg = spi_get_device_match_data(spi); + x9250->cfg = cfg; x9250->wp_gpio = devm_gpiod_get_optional(&spi->dev, "wp", GPIOD_OUT_LOW); if (IS_ERR(x9250->wp_gpio)) return dev_err_probe(&spi->dev, PTR_ERR(x9250->wp_gpio), diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 838a8340c4c0..cef8b90b9ae7 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -112,6 +112,8 @@ config DPS310 tristate "Infineon DPS310 pressure and temperature sensor" depends on I2C select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Support for the Infineon DPS310 digital barometric pressure sensor. It can be accessed over I2C bus. diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index bc0d11a20acc..4b1a05f7a0bd 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -3,6 +3,8 @@ # Makefile for industrial I/O pressure drivers # +CONTEXT_ANALYSIS_dps310.o := y + # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ABP060MG) += abp060mg.o obj-$(CONFIG_ABP2030PA) += abp2030pa.o diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index a02a621b8015..95d9635f4711 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -83,6 +83,8 @@ static int bmp280_spi_probe(struct spi_device *spi) struct regmap *regmap; chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return -ENODATA; if (chip_info->spi_read_extra_byte) bmp_regmap_bus = &bmp380_regmap_bus; diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index 6a567b6075d9..e4bc4df48d01 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -192,7 +192,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids); static struct platform_driver cros_ec_baro_platform_driver = { .driver = { .name = "cros-ec-baro", - .pm = &cros_ec_sensors_pm_ops, + .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops), }, .probe = cros_ec_baro_probe, .id_table = cros_ec_baro_ids, diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 45bdb8c7670f..85df58ac1809 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -14,14 +14,19 @@ * - Optionally support the FIFO */ +#include #include #include #include #include #include +#include +#include #include #include +#include +#include #define DPS310_DEV_NAME "dps310" @@ -50,9 +55,9 @@ #define DPS310_CFG_REG 0x09 #define DPS310_INT_HL BIT(7) #define DPS310_TMP_SHIFT_EN BIT(3) -#define DPS310_PRS_SHIFT_EN BIT(4) -#define DPS310_FIFO_EN BIT(5) -#define DPS310_SPI_EN BIT(6) +#define DPS310_PRS_SHIFT_EN BIT(2) +#define DPS310_FIFO_EN BIT(1) +#define DPS310_SPI_EN BIT(0) #define DPS310_RESET 0x0c #define DPS310_RESET_MAGIC 0x09 #define DPS310_COEF_BASE 0x10 @@ -92,19 +97,51 @@ struct dps310_data { bool timeout_recovery_failed; }; +enum dps310_scan_index { + DPS310_SCAN_TEMP, + DPS310_SCAN_PRESSURE, + DPS310_SCAN_TIMESTAMP, +}; + static const struct iio_chan_spec dps310_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = DPS310_SCAN_TEMP, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_PRESSURE, + /* + * _raw here is already compensated (section 4.9.1, which needs + * a temperature too) and in Pa; _scale converts to the kPa the + * ABI wants. _processed predates buffers and has to stay. + * + * Do not copy this into other drivers. A _raw that is not the + * raw register value is only tolerable because the alternative + * is losing resolution or breaking existing _processed users. + */ .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = DPS310_SCAN_PRESSURE, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(DPS310_SCAN_TIMESTAMP), }; /* To be called after checking the COEF_RDY bit in MEAS_CFG */ @@ -286,8 +323,8 @@ static int dps310_get_temp_precision(struct dps310_data *data, int *val) return 0; } -/* Called with lock held */ static int dps310_set_pres_precision(struct dps310_data *data, int val) + __must_hold(&data->lock) { int rc; u8 shift_en; @@ -305,8 +342,8 @@ static int dps310_set_pres_precision(struct dps310_data *data, int val) DPS310_PRS_PRC_BITS, ilog2(val)); } -/* Called with lock held */ static int dps310_set_temp_precision(struct dps310_data *data, int val) + __must_hold(&data->lock) { int rc; u8 shift_en; @@ -324,8 +361,8 @@ static int dps310_set_temp_precision(struct dps310_data *data, int val) DPS310_TMP_PRC_BITS, ilog2(val)); } -/* Called with lock held */ static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq) + __must_hold(&data->lock) { u8 val; @@ -338,8 +375,8 @@ static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq) DPS310_PRS_RATE_BITS, val); } -/* Called with lock held */ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) + __must_hold(&data->lock) { u8 val; @@ -438,6 +475,7 @@ static int dps310_ready_status(struct dps310_data *data, int ready_bit, int time } static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout) + __must_hold(&data->lock) { int rc; @@ -463,82 +501,83 @@ static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout) return 0; } -static int dps310_read_pres_raw(struct dps310_data *data) +static int dps310_read_pres_raw_locked(struct dps310_data *data) + __must_hold(&data->lock) { int rc; int rate; int timeout; - s32 raw; u8 val[3]; - if (mutex_lock_interruptible(&data->lock)) - return -EINTR; - rc = dps310_get_pres_samp_freq(data, &rate); if (rc) - goto done; + return rc; timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ rc = dps310_ready(data, DPS310_PRS_RDY, timeout); if (rc) - goto done; + return rc; rc = regmap_bulk_read(data->regmap, DPS310_PRS_BASE, val, sizeof(val)); if (rc < 0) - goto done; + return rc; - raw = (val[0] << 16) | (val[1] << 8) | val[2]; - data->pressure_raw = sign_extend32(raw, 23); + data->pressure_raw = sign_extend32(get_unaligned_be24(val), 23); -done: - mutex_unlock(&data->lock); - return rc; + return 0; } -/* Called with lock held */ static int dps310_read_temp_ready(struct dps310_data *data) + __must_hold(&data->lock) { int rc; u8 val[3]; - s32 raw; rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val)); if (rc < 0) return rc; - raw = (val[0] << 16) | (val[1] << 8) | val[2]; - data->temp_raw = sign_extend32(raw, 23); + data->temp_raw = sign_extend32(get_unaligned_be24(val), 23); return 0; } -static int dps310_read_temp_raw(struct dps310_data *data) +static int dps310_read_temp_raw_locked(struct dps310_data *data) + __must_hold(&data->lock) { int rc; int rate; int timeout; - if (mutex_lock_interruptible(&data->lock)) - return -EINTR; - rc = dps310_get_temp_samp_freq(data, &rate); if (rc) - goto done; + return rc; timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ rc = dps310_ready(data, DPS310_TMP_RDY, timeout); if (rc) - goto done; + return rc; - rc = dps310_read_temp_ready(data); + return dps310_read_temp_ready(data); +} -done: - mutex_unlock(&data->lock); - return rc; +/* Best effort: on error the previous temperature stands */ +static void dps310_refresh_temp_locked(struct dps310_data *data) + __must_hold(&data->lock) +{ + int rc; + int t_ready; + + rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready); + if (rc) + return; + + if (t_ready & DPS310_TMP_RDY) + dps310_read_temp_ready(data); } static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg) @@ -580,59 +619,52 @@ static int dps310_write_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int rc; struct dps310_data *data = iio_priv(iio); - if (mutex_lock_interruptible(&data->lock)) + /* Reconfiguring mid-capture would change the values being captured */ + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ACQUIRE(mutex_intr, lock)(&data->lock); + if (ACQUIRE_ERR(mutex_intr, &lock)) return -EINTR; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { case IIO_PRESSURE: - rc = dps310_set_pres_samp_freq(data, val); - break; + return dps310_set_pres_samp_freq(data, val); case IIO_TEMP: - rc = dps310_set_temp_samp_freq(data, val); - break; + return dps310_set_temp_samp_freq(data, val); default: - rc = -EINVAL; - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { case IIO_PRESSURE: - rc = dps310_set_pres_precision(data, val); - break; + return dps310_set_pres_precision(data, val); case IIO_TEMP: - rc = dps310_set_temp_precision(data, val); - break; + return dps310_set_temp_precision(data, val); default: - rc = -EINVAL; - break; + return -EINVAL; } - break; default: - rc = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - return rc; } static int dps310_calculate_pressure(struct dps310_data *data, int *val) + __must_hold(&data->lock) { int i; int rc; - int t_ready; int kpi; int kti; s64 rem = 0ULL; @@ -656,15 +688,6 @@ static int dps310_calculate_pressure(struct dps310_data *data, int *val) kp = (s64)kpi; kt = (s64)kti; - /* Refresh temp if it's ready, otherwise just use the latest value */ - if (mutex_trylock(&data->lock)) { - rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready); - if (rc >= 0 && t_ready & DPS310_TMP_RDY) - dps310_read_temp_ready(data); - - mutex_unlock(&data->lock); - } - p = (s64)data->pressure_raw; t = (s64)data->temp_raw; @@ -710,6 +733,23 @@ static int dps310_calculate_pressure(struct dps310_data *data, int *val) return 0; } +static int dps310_read_pressure_value(struct dps310_data *data, int *val) +{ + int rc; + + ACQUIRE(mutex_intr, lock)(&data->lock); + if (ACQUIRE_ERR(mutex_intr, &lock)) + return -EINTR; + + rc = dps310_read_pres_raw_locked(data); + if (rc) + return rc; + + dps310_refresh_temp_locked(data); + + return dps310_calculate_pressure(data, val); +} + static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, long mask) { @@ -723,18 +763,27 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, return IIO_VAL_INT; - case IIO_CHAN_INFO_PROCESSED: - rc = dps310_read_pres_raw(data); + case IIO_CHAN_INFO_RAW: + rc = dps310_read_pressure_value(data, val); if (rc) return rc; - rc = dps310_calculate_pressure(data, val); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + rc = dps310_read_pressure_value(data, val); if (rc) return rc; *val2 = 1000; /* Convert Pa to KPa per IIO ABI */ return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SCALE: + /* The raw value is in Pa, the ABI wants kPa */ + *val = 1; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: rc = dps310_get_pres_precision(data, val); if (rc) @@ -747,6 +796,7 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, } static int dps310_calculate_temp(struct dps310_data *data, int *val) + __must_hold(&data->lock) { s64 c0; s64 t; @@ -768,6 +818,21 @@ static int dps310_calculate_temp(struct dps310_data *data, int *val) return 0; } +static int dps310_read_temp_value(struct dps310_data *data, int *val) +{ + int rc; + + ACQUIRE(mutex_intr, lock)(&data->lock); + if (ACQUIRE_ERR(mutex_intr, &lock)) + return -EINTR; + + rc = dps310_read_temp_raw_locked(data); + if (rc) + return rc; + + return dps310_calculate_temp(data, val); +} + static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, long mask) { @@ -782,11 +847,7 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, return IIO_VAL_INT; case IIO_CHAN_INFO_PROCESSED: - rc = dps310_read_temp_raw(data); - if (rc) - return rc; - - rc = dps310_calculate_temp(data, val); + rc = dps310_read_temp_value(data, val); if (rc) return rc; @@ -804,12 +865,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, } } -static int dps310_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int dps310_read_channel(struct dps310_data *data, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { - struct dps310_data *data = iio_priv(iio); - switch (chan->type) { case IIO_PRESSURE: return dps310_read_pressure(data, val, val2, mask); @@ -822,6 +881,87 @@ static int dps310_read_raw(struct iio_dev *iio, } } +static int dps310_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dps310_data *data = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: { + /* This consumes the measurement the capture path reads */ + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + return dps310_read_channel(data, chan, val, val2, mask); + } + default: + return dps310_read_channel(data, chan, val, val2, mask); + } +} + +static int dps310_fill_channels(struct dps310_data *data, + const unsigned long *scan_mask, + s32 channels[2]) + __must_hold(&data->lock) +{ + unsigned int i; + int rc; + + /* Compensation needs a temperature, so it is sampled either way */ + rc = dps310_read_temp_raw_locked(data); + if (rc) + return rc; + + i = 0; + if (test_bit(DPS310_SCAN_TEMP, scan_mask)) { + /* Millidegrees Celsius */ + rc = dps310_calculate_temp(data, &channels[i++]); + if (rc) + return rc; + } + + if (test_bit(DPS310_SCAN_PRESSURE, scan_mask)) { + rc = dps310_read_pres_raw_locked(data); + if (rc) + return rc; + + /* Pascals, see the channel definition */ + rc = dps310_calculate_pressure(data, &channels[i++]); + if (rc) + return rc; + } + + return 0; +} + +static irqreturn_t dps310_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct dps310_data *data = iio_priv(iio); + struct { + s32 channels[2]; + aligned_s64 timestamp; + } scan = { }; + int rc; + + mutex_lock(&data->lock); + rc = dps310_fill_channels(data, iio->active_scan_mask, scan.channels); + mutex_unlock(&data->lock); + if (rc) + goto err; + + iio_push_to_buffers_with_ts(iio, &scan, sizeof(scan), iio_get_time_ns(iio)); + +err: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + static void dps310_reset(void *action_data) { struct dps310_data *data = action_data; @@ -845,11 +985,12 @@ static const struct iio_info dps310_info = { static int dps310_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct dps310_data *data; struct iio_dev *iio; int rc; - iio = devm_iio_device_alloc(&client->dev, sizeof(*data)); + iio = devm_iio_device_alloc(dev, sizeof(*data)); if (!iio) return -ENOMEM; @@ -868,7 +1009,7 @@ static int dps310_probe(struct i2c_client *client) return PTR_ERR(data->regmap); /* Register to run the device reset when the device is removed */ - rc = devm_add_action_or_reset(&client->dev, dps310_reset, data); + rc = devm_add_action_or_reset(dev, dps310_reset, data); if (rc) return rc; @@ -876,7 +1017,17 @@ static int dps310_probe(struct i2c_client *client) if (rc) return rc; - rc = devm_iio_device_register(&client->dev, iio); + /* + * The device measures continuously in background mode, so a capture is + * just a read of the latest results. The trigger is not aligned with + * the measurements, so the timestamp is taken in the handler. + */ + rc = devm_iio_triggered_buffer_setup(dev, iio, NULL, + dps310_trigger_handler, NULL); + if (rc) + return rc; + + rc = devm_iio_device_register(dev, iio); if (rc) return rc; diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index 57941fb4a535..2a87b113823b 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -267,7 +267,7 @@ static int bm1390_read_data(struct bm1390_data *data, guard(mutex)(&data->mutex); /* * We use 'continuous mode' even for raw read because according to the - * data-sheet an one-shot mode can't be used with IIR filter. + * data-sheet a one-shot mode can't be used with IIR filter. */ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); if (ret) @@ -479,6 +479,7 @@ static const struct iio_info bm1390_info = { static int bm1390_chip_init(struct bm1390_data *data) { + u8 regval; int ret; ret = regmap_write_bits(data->regmap, BM1390_REG_POWER, @@ -512,8 +513,9 @@ static int bm1390_chip_init(struct bm1390_data *data) * Default to use IIR filter in "middle" mode. Also the AVE_NUM must * be fixed when IIR is in use. */ + regval = FIELD_PREP(BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM); ret = regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL, - BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM); + BM1390_MASK_AVE_NUM, regval); if (ret) return ret; diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index 8fbb755dcae0..b1f36a51d56f 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -669,7 +669,7 @@ static irqreturn_t aw96103_irq(int irq, void *data) iio_get_time_ns(indio_dev)); break; default: - return IRQ_HANDLED; + continue; } aw96103->channels_arr[i].old_irq_status = curr_status; } diff --git a/drivers/iio/temperature/iqs620at-temp.c b/drivers/iio/temperature/iqs620at-temp.c index e2f878d57af7..3a3b1f256585 100644 --- a/drivers/iio/temperature/iqs620at-temp.c +++ b/drivers/iio/temperature/iqs620at-temp.c @@ -5,13 +5,18 @@ * Copyright (C) 2019 Jeff LaBundy */ +#include +#include #include -#include -#include #include #include #include #include +#include + +#include + +#include #define IQS620_TEMP_UI_OUT 0x1A diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 6efb5252a773..6655453c9abf 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -5,23 +5,34 @@ * * Copyright 2019 Analog Devices Inc. */ + +#include #include +#include #include +#include +#include #include #include -#include -#include -#include +#include #include #include +#include #include +#include +#include #include #include #include +#include #include +#include +#include +#include #include -#include + +#include /* register map */ #define LTC2983_STATUS_REG 0x0000 diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c index 2fde48f337e2..74ae36f74e8b 100644 --- a/drivers/iio/temperature/mlx90632.c +++ b/drivers/iio/temperature/mlx90632.c @@ -6,21 +6,25 @@ * * Driver for the Melexis MLX90632 I2C 16-bit IR thermopile sensor */ + +#include #include +#include #include +#include #include #include -#include #include #include #include -#include #include -#include #include +#include +#include #include #include #include +#include #include #include diff --git a/drivers/iio/temperature/mlx90635.c b/drivers/iio/temperature/mlx90635.c index 0f31879a4f64..48c760c980f3 100644 --- a/drivers/iio/temperature/mlx90635.c +++ b/drivers/iio/temperature/mlx90635.c @@ -6,21 +6,24 @@ * * Driver for the Melexis MLX90635 I2C 16-bit IR thermopile sensor */ + +#include #include +#include #include +#include #include #include -#include #include #include #include -#include -#include -#include #include +#include +#include #include #include #include +#include #include diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 74cb8d62bef3..2d0e79416434 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -10,16 +10,17 @@ */ #include +#include #include +#include #include #include -#include -#include -#include -#include #include +#include +#include #include #include +#include #include @@ -97,15 +98,20 @@ static int tmp117_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec { struct tmp117_data *data = iio_priv(indio_dev); s16 off; + int ret; switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: off = clamp_t(int, val, S16_MIN, S16_MAX); if (off == data->calibbias) return 0; + + ret = i2c_smbus_write_word_swapped(data->client, TMP117_REG_TEMP_OFFSET, off); + if (ret) + return ret; + data->calibbias = off; - return i2c_smbus_write_word_swapped(data->client, - TMP117_REG_TEMP_OFFSET, off); + return 0; default: return -EINVAL; diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 28b3ce022ce7..b19b4a3ef038 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -8,14 +8,19 @@ * http://www.meas-spec.com/downloads/TSYS01_Digital_Temperature_Sensor.pdf */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include -#include -#include -#include -#include -#include -#include + #include "../common/ms_sensors/ms_sensors_i2c.h" /* TSYS01 Commands */ diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index 3ef72347456e..2c67faebd90c 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -10,11 +10,17 @@ * http://www.meas-spec.com/downloads/Digital_Sensor_TSYS02D.pdf */ -#include +#include +#include +#include #include -#include -#include +#include #include +#include +#include +#include +#include + #include #include diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig index 4fc17dd0dcd7..04930b4fdd5d 100644 --- a/drivers/iio/test/Kconfig +++ b/drivers/iio/test/Kconfig @@ -4,6 +4,19 @@ # # Keep in alphabetical order +config IIO_CHANNEL_PREFIX_KUNIT_TEST + tristate "Test IIO channel prefix" if !KUNIT_ALL_TESTS + depends on KUNIT && IIO + default KUNIT_ALL_TESTS + help + Build unit tests for __iio_chan_prefix_emit(), the helper that + builds IIO sysfs attribute name prefixes from an iio_chan_spec. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config IIO_GTS_KUNIT_TEST tristate "Test IIO gain-time-scale helpers" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile index 0c846bc21acd..ba08afa7f3f2 100644 --- a/drivers/iio/test/Makefile +++ b/drivers/iio/test/Makefile @@ -4,6 +4,7 @@ # # Keep in alphabetical order +obj-$(CONFIG_IIO_CHANNEL_PREFIX_KUNIT_TEST) += iio-test-channel-prefix.o obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) += iio-test-rescale.o obj-$(CONFIG_IIO_FORMAT_KUNIT_TEST) += iio-test-format.o obj-$(CONFIG_IIO_GTS_KUNIT_TEST) += iio-test-gts.o diff --git a/drivers/iio/test/iio-test-channel-prefix.c b/drivers/iio/test/iio-test-channel-prefix.c new file mode 100644 index 000000000000..44c838fcd8d6 --- /dev/null +++ b/drivers/iio/test/iio-test-channel-prefix.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Unit tests for IIO channel prefix generation. + */ + +#include +#include + +#include +#include +#include + +#include "../iio_core.h" + +#define PREFIX_BUF_SIZE (NAME_MAX + 1) + +#define EXPECT_PREFIX(_test, _buf, _ret, _expected) do { \ + struct kunit *__test = (_test); \ + const char *__expected = (_expected); \ + \ + KUNIT_EXPECT_EQ(__test, (_ret), (ssize_t)strlen(__expected)); \ + KUNIT_EXPECT_STREQ(__test, (_buf), __expected); \ + } while (0) + +static char *iio_test_prefix_alloc(struct kunit *test) +{ + char *buf = kunit_kzalloc(test, PREFIX_BUF_SIZE, GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + return buf; +} + +static void iio_test_prefix_shared_by_all(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + }; + const struct iio_chan_spec chan_noisy = { + .type = IIO_ACCEL, + .output = 1, + .indexed = 1, + .modified = 1, + .channel = 7, + .channel2 = IIO_MOD_Z, + .extend_name = "supply", + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_ALL, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, ""); + + ret = __iio_chan_prefix_emit(NULL, &chan_noisy, IIO_SHARED_BY_ALL, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, ""); +} + +static void iio_test_prefix_shared_by_dir(struct kunit *test) +{ + const struct iio_chan_spec chan_in = { + .type = IIO_VOLTAGE, + .output = 0, + }; + const struct iio_chan_spec chan_out = { + .type = IIO_VOLTAGE, + .output = 1, + }; + const struct iio_chan_spec chan_in_noisy = { + .type = IIO_ACCEL, + .indexed = 1, + .modified = 1, + .channel = 5, + .channel2 = IIO_MOD_Y, + .extend_name = "supply", + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan_in, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in"); + + ret = __iio_chan_prefix_emit(NULL, &chan_out, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "out"); + + ret = __iio_chan_prefix_emit(NULL, &chan_in_noisy, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in"); +} + +static void iio_test_prefix_shared_by_type(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + }; + const struct iio_chan_spec chan_diff = { + .type = IIO_VOLTAGE, + .differential = 1, + }; + const struct iio_chan_spec chan_noisy = { + .type = IIO_VOLTAGE, + .indexed = 1, + .modified = 1, + .channel = 4, + .channel2 = IIO_MOD_X, + .extend_name = "supply", + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage"); + + ret = __iio_chan_prefix_emit(NULL, &chan_diff, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage-voltage"); + + ret = __iio_chan_prefix_emit(NULL, &chan_noisy, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage"); +} + +static void iio_test_prefix_separate_simple(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_TEMP, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_temp"); +} + +static void iio_test_prefix_separate_indexed(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 3, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage3"); +} + +static void iio_test_prefix_separate_indexed_diff(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .channel = 0, + .channel2 = 1, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage0-voltage1"); +} + +static void iio_test_prefix_separate_modified(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_accel_x"); +} + +static void iio_test_prefix_separate_indexed_modified(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_ACCEL, + .indexed = 1, + .modified = 1, + .channel = 2, + .channel2 = IIO_MOD_Y, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_accel2_y"); +} + +static void iio_test_prefix_separate_extend_name(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 2, + .extend_name = "supply", + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage2_supply"); +} + +static void iio_test_prefix_output_separate(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .output = 1, + .indexed = 1, + .channel = 0, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "out_voltage0"); +} + +static void iio_test_prefix_diff_unindexed_fails(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .differential = 1, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void iio_test_prefix_diff_modified_fails(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .modified = 1, + .channel = 0, + .channel2 = 1, + }; + char *buf = iio_test_prefix_alloc(test); + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void iio_test_prefix_overflow(struct kunit *test) +{ + const struct iio_chan_spec chan = { + .type = IIO_VOLTAGE, + }; + char small[4]; + ssize_t ret; + + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_TYPE, + small, sizeof(small)); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); +} + +static struct kunit_case iio_chan_prefix_test_cases[] = { + KUNIT_CASE(iio_test_prefix_shared_by_all), + KUNIT_CASE(iio_test_prefix_shared_by_dir), + KUNIT_CASE(iio_test_prefix_shared_by_type), + KUNIT_CASE(iio_test_prefix_separate_simple), + KUNIT_CASE(iio_test_prefix_separate_indexed), + KUNIT_CASE(iio_test_prefix_separate_indexed_diff), + KUNIT_CASE(iio_test_prefix_separate_modified), + KUNIT_CASE(iio_test_prefix_separate_indexed_modified), + KUNIT_CASE(iio_test_prefix_separate_extend_name), + KUNIT_CASE(iio_test_prefix_output_separate), + KUNIT_CASE(iio_test_prefix_diff_unindexed_fails), + KUNIT_CASE(iio_test_prefix_diff_modified_fails), + KUNIT_CASE(iio_test_prefix_overflow), + { } +}; + +static struct kunit_suite iio_channel_prefix_test_suite = { + .name = "iio-channel-prefix", + .test_cases = iio_chan_prefix_test_cases, +}; + +kunit_test_suite(iio_channel_prefix_test_suite); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Test IIO channel prefix generation"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index bcd4a607be19..de4a09f432c0 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -587,7 +587,7 @@ KUNIT_ARRAY_PARAM(iio_rescale_offset, offset_cases, case_to_desc); * @str: The string to parse * @nano: The number as an integer * - * Returns 0 on success, or a negative error code if the string cound not be + * Returns 0 on success, or a negative error code if the string could not be * parsed. */ static int iio_str_to_nano(const char *str, s64 *nano) @@ -617,7 +617,7 @@ static int iio_str_to_nano(const char *str, s64 *nano) * @real_str: The real value as a string * @exp_str: The expected value as a string * - * Returns a negative error code if the strings cound not be parsed, or the + * Returns a negative error code if the strings could not be parsed, or the * relative error in parts-per-million. */ static int iio_test_relative_error_ppm(const char *real_str, const char *exp_str) diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a index 863d3856718a..d19db00acd1d 100644 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a @@ -4,7 +4,7 @@ Contact: linux-iio@vger.kernel.org Description: Writing 1 enables the cell balance output switch corresponding to input Y. Writing 0 disables it. If the inY-inZ_balance_timer - is set to a none zero value, the corresponding switch will + is set to a non-zero value, the corresponding switch will enable for the programmed amount of time, before it automatically disables. diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index a60631c1f449..bf6addf2974f 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -5,7 +5,6 @@ menu "IIO staging drivers" depends on IIO -source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 628583535393..739e4cbf9a7a 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -3,7 +3,6 @@ # Makefile for the industrial I/O core. # -obj-y += accel/ obj-y += adc/ obj-y += addac/ obj-y += frequency/ diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig deleted file mode 100644 index cee51f64bc4b..000000000000 --- a/drivers/staging/iio/accel/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Accelerometer drivers -# -menu "Accelerometers" - -config ADIS16203 - tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" - depends on SPI - select IIO_ADIS_LIB - select IIO_ADIS_LIB_BUFFER if IIO_BUFFER - help - Say Y here to build support for Analog Devices adis16203 Programmable - 360 Degrees Inclinometer. - - To compile this driver as a module, say M here: the module will be - called adis16203. - -endmenu diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile deleted file mode 100644 index acac7bc9b9c0..000000000000 --- a/drivers/staging/iio/accel/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for industrial I/O accelerometer drivers -# - -obj-$(CONFIG_ADIS16203) += adis16203.o diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c deleted file mode 100644 index 830ff38fda92..000000000000 --- a/drivers/staging/iio/accel/adis16203.c +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * ADIS16203 Programmable 360 Degrees Inclinometer - * - * Copyright 2010 Analog Devices Inc. - */ - -#include - -#include -#include - -#include -#include -#include - -#define ADIS16203_STARTUP_DELAY 220 /* ms */ - -/* Flash memory write count */ -#define ADIS16203_FLASH_CNT 0x00 - -/* Output, power supply */ -#define ADIS16203_SUPPLY_OUT 0x02 - -/* Output, auxiliary ADC input */ -#define ADIS16203_AUX_ADC 0x08 - -/* Output, temperature */ -#define ADIS16203_TEMP_OUT 0x0A - -/* Output, x-axis inclination */ -#define ADIS16203_XINCL_OUT 0x0C - -/* Output, y-axis inclination */ -#define ADIS16203_YINCL_OUT 0x0E - -/* Incline null calibration */ -#define ADIS16203_INCL_NULL 0x18 - -/* Alarm 1 amplitude threshold */ -#define ADIS16203_ALM_MAG1 0x20 - -/* Alarm 2 amplitude threshold */ -#define ADIS16203_ALM_MAG2 0x22 - -/* Alarm 1, sample period */ -#define ADIS16203_ALM_SMPL1 0x24 - -/* Alarm 2, sample period */ -#define ADIS16203_ALM_SMPL2 0x26 - -/* Alarm control */ -#define ADIS16203_ALM_CTRL 0x28 - -/* Auxiliary DAC data */ -#define ADIS16203_AUX_DAC 0x30 - -/* General-purpose digital input/output control */ -#define ADIS16203_GPIO_CTRL 0x32 - -/* Miscellaneous control */ -#define ADIS16203_MSC_CTRL 0x34 - -/* Internal sample period (rate) control */ -#define ADIS16203_SMPL_PRD 0x36 - -/* Operation, filter configuration */ -#define ADIS16203_AVG_CNT 0x38 - -/* Operation, sleep mode control */ -#define ADIS16203_SLP_CNT 0x3A - -/* Diagnostics, system status register */ -#define ADIS16203_DIAG_STAT 0x3C - -/* Operation, system command register */ -#define ADIS16203_GLOB_CMD 0x3E - -/* MSC_CTRL */ - -/* Self-test at power-on: 1 = disabled, 0 = enabled */ -#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST BIT(10) - -/* Reverses rotation of both inclination outputs */ -#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN BIT(9) - -/* Self-test enable */ -#define ADIS16203_MSC_CTRL_SELF_TEST_EN BIT(8) - -/* Data-ready enable: 1 = enabled, 0 = disabled */ -#define ADIS16203_MSC_CTRL_DATA_RDY_EN BIT(2) - -/* Data-ready polarity: 1 = active high, 0 = active low */ -#define ADIS16203_MSC_CTRL_ACTIVE_HIGH BIT(1) - -/* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ -#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1 BIT(0) - -/* DIAG_STAT */ - -/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ -#define ADIS16203_DIAG_STAT_ALARM2 BIT(9) - -/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ -#define ADIS16203_DIAG_STAT_ALARM1 BIT(8) - -/* Self-test diagnostic error flag */ -#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 - -/* SPI communications failure */ -#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 - -/* Flash update failure */ -#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 - -/* Power supply above 3.625 V */ -#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 - -/* Power supply below 2.975 V */ -#define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 - -/* GLOB_CMD */ - -#define ADIS16203_GLOB_CMD_SW_RESET BIT(7) -#define ADIS16203_GLOB_CMD_CLEAR_STAT BIT(4) -#define ADIS16203_GLOB_CMD_FACTORY_CAL BIT(1) - -#define ADIS16203_ERROR_ACTIVE BIT(14) - -enum adis16203_scan { - ADIS16203_SCAN_INCLI_X, - ADIS16203_SCAN_INCLI_Y, - ADIS16203_SCAN_SUPPLY, - ADIS16203_SCAN_AUX_ADC, - ADIS16203_SCAN_TEMP, -}; - -#define DRIVER_NAME "adis16203" - -static const u8 adis16203_addresses[] = { - [ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL, -}; - -static int adis16203_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct adis *st = iio_priv(indio_dev); - /* currently only one writable parameter which keeps this simple */ - u8 addr = adis16203_addresses[chan->scan_index]; - - return adis_write_reg_16(st, addr, val & 0x3FFF); -} - -static int adis16203_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) -{ - struct adis *st = iio_priv(indio_dev); - int ret; - u8 addr; - s16 val16; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - return adis_single_conversion(indio_dev, chan, - ADIS16203_ERROR_ACTIVE, val); - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_VOLTAGE: - if (chan->channel == 0) { - *val = 1; - *val2 = 220000; /* 1.22 mV */ - } else { - *val = 0; - *val2 = 610000; /* 0.61 mV */ - } - return IIO_VAL_INT_PLUS_MICRO; - case IIO_TEMP: - *val = -470; /* -0.47 C */ - *val2 = 0; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_INCLI: - *val = 0; - *val2 = 25000; /* 0.025 degree */ - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_OFFSET: - *val = 25000 / -470 - 1278; /* 25 C = 1278 */ - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - addr = adis16203_addresses[chan->scan_index]; - ret = adis_read_reg_16(st, addr, &val16); - if (ret) - return ret; - *val = sign_extend32(val16, 13); - return IIO_VAL_INT; - default: - return -EINVAL; - } -} - -static const struct iio_chan_spec adis16203_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12), - ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12), - ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), - /* Fixme: Not what it appears to be - see data sheet */ - ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, - 0, 0, 14), - ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12), - IIO_CHAN_SOFT_TIMESTAMP(5), -}; - -static const struct iio_info adis16203_info = { - .read_raw = adis16203_read_raw, - .write_raw = adis16203_write_raw, - .update_scan_mode = adis_update_scan_mode, -}; - -static const char * const adis16203_status_error_msgs[] = { - [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", - [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", - [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", - [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", - [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", -}; - -static const struct adis_timeout adis16203_timeouts = { - .reset_ms = ADIS16203_STARTUP_DELAY, - .sw_reset_ms = ADIS16203_STARTUP_DELAY, - .self_test_ms = ADIS16203_STARTUP_DELAY -}; - -static const struct adis_data adis16203_data = { - .read_delay = 20, - .msc_ctrl_reg = ADIS16203_MSC_CTRL, - .glob_cmd_reg = ADIS16203_GLOB_CMD, - .diag_stat_reg = ADIS16203_DIAG_STAT, - - .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, - .self_test_reg = ADIS16203_MSC_CTRL, - .self_test_no_autoclear = true, - .timeouts = &adis16203_timeouts, - - .status_error_msgs = adis16203_status_error_msgs, - .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) | - BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) | - BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) | - BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) | - BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT), -}; - -static int adis16203_probe(struct spi_device *spi) -{ - int ret; - struct iio_dev *indio_dev; - struct adis *st; - - /* setup the industrialio driver allocated elements */ - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) - return -ENOMEM; - st = iio_priv(indio_dev); - /* this is only used for removal purposes */ - spi_set_drvdata(spi, indio_dev); - - indio_dev->name = spi->dev.driver->name; - indio_dev->channels = adis16203_channels; - indio_dev->num_channels = ARRAY_SIZE(adis16203_channels); - indio_dev->info = &adis16203_info; - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = adis_init(st, indio_dev, spi, &adis16203_data); - if (ret) - return ret; - - ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL); - if (ret) - return ret; - - /* Get the device into a sane initial state */ - ret = __adis_initial_startup(st); - if (ret) - return ret; - - return devm_iio_device_register(&spi->dev, indio_dev); -} - -static const struct of_device_id adis16203_of_match[] = { - { .compatible = "adi,adis16203" }, - { } -}; - -MODULE_DEVICE_TABLE(of, adis16203_of_match); - -static struct spi_driver adis16203_driver = { - .driver = { - .name = "adis16203", - .of_match_table = adis16203_of_match, - }, - .probe = adis16203_probe, -}; -module_spi_driver(adis16203_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:adis16203"); -MODULE_IMPORT_NS("IIO_ADISLIB"); diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 30644d2d7c54..2a15d7598abb 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -5,19 +5,21 @@ * Copyright 2010 Analog Devices Inc. */ -#include -#include +#include #include +#include +#include #include -#include -#include #include -#include #include +#include +#include +#include +#include +#include #include #include -#include /* * AD7816 config masks @@ -47,6 +49,7 @@ struct ad7816_chip_info { struct gpio_desc *rdwr_pin; struct gpio_desc *convert_pin; struct gpio_desc *busy_pin; + struct mutex lock; /* protect device state during SPI transfers */ u8 oti_data[AD7816_CS_MAX + 1]; u8 channel_id; /* 0 always be temperature */ u8 mode; @@ -67,9 +70,11 @@ static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data) int ret; __be16 buf; + guard(mutex)(&chip->lock); + gpiod_set_value(chip->rdwr_pin, 1); gpiod_set_value(chip->rdwr_pin, 0); - ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id)); + ret = spi_write_then_read(spi_dev, &chip->channel_id, sizeof(chip->channel_id), NULL, 0); if (ret < 0) { dev_err(&spi_dev->dev, "SPI channel setting error\n"); return ret; @@ -91,7 +96,7 @@ static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data) gpiod_set_value(chip->rdwr_pin, 0); gpiod_set_value(chip->rdwr_pin, 1); - ret = spi_read(spi_dev, &buf, sizeof(*data)); + ret = spi_write_then_read(spi_dev, NULL, 0, &buf, sizeof(buf)); if (ret < 0) { dev_err(&spi_dev->dev, "SPI data read error\n"); return ret; @@ -107,9 +112,11 @@ static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data) struct spi_device *spi_dev = chip->spi_dev; int ret; + guard(mutex)(&chip->lock); + gpiod_set_value(chip->rdwr_pin, 1); gpiod_set_value(chip->rdwr_pin, 0); - ret = spi_write(spi_dev, &data, sizeof(data)); + ret = spi_write_then_read(spi_dev, &data, sizeof(data), NULL, 0); if (ret < 0) dev_err(&spi_dev->dev, "SPI oti data write error\n"); @@ -136,6 +143,8 @@ static ssize_t ad7816_store_mode(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7816_chip_info *chip = iio_priv(indio_dev); + guard(mutex)(&chip->lock); + if (strcmp(buf, "full") == 0) { gpiod_set_value(chip->rdwr_pin, 1); chip->mode = AD7816_FULL; @@ -200,6 +209,8 @@ static ssize_t ad7816_store_channel(struct device *dev, return -EINVAL; } + guard(mutex)(&chip->lock); + chip->channel_id = data; return len; @@ -360,6 +371,10 @@ static int ad7816_probe(struct spi_device *spi_dev) return -ENOMEM; chip = iio_priv(indio_dev); + ret = devm_mutex_init(&spi_dev->dev, &chip->lock); + if (ret) + return ret; + chip->spi_dev = spi_dev; for (i = 0; i <= AD7816_CS_MAX; i++) chip->oti_data[i] = 203; diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index a78249812b26..35f823f24f9b 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -118,6 +118,12 @@ static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout) { u64 freqreg = (u64)fout << AD9832_FREQ_BITS; + /* + * mclk is an unsigned long, which triggers a Coccinelle false positive + * warning about using do_div() for 64-by-32 division. However, mclk + * for this hardware will always fit within 32 bits, so do_div() is + * safe to use here. + */ do_div(freqreg, mclk); return freqreg; } diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 0d20f796e0f0..4cebb9d0d924 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -103,6 +103,12 @@ static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) { unsigned long long freqreg = (u64)fout * (u64)BIT(AD9834_FREQ_BITS); + /* + * mclk is an unsigned long, which triggers a Coccinelle false positive + * warning about using a do_div() for 64-by-32 division. However, mclk + * for this hardware will always fit within 32 bits, so do_div() is + * safe to use here. + */ do_div(freqreg, mclk); return freqreg; } diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h index 39cbfcbdb101..b3e2c2f50e8f 100644 --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h @@ -137,7 +137,7 @@ struct adc5_sdam_data { */ struct adc5_device_data { struct regmap *regmap; - struct adc5_sdam_data *base; + struct adc5_sdam_data *base __counted_by_ptr(num_sdams); int num_sdams; }; diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 3f95ed1fdf9e..9c16b695a602 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -133,6 +133,8 @@ enum iio_backend_capabilities { * @filter_type_set: Set filter type. * @interface_data_align: Perform the data alignment process. * @num_lanes_set: Set the number of lanes enabled. + * @crc_enable: Enable direction-specific CRC handling. + * @crc_disable: Disable direction-specific CRC handling. * @ddr_enable: Enable interface DDR (Double Data Rate) mode. * @ddr_disable: Disable interface DDR (Double Data Rate) mode. * @data_stream_enable: Enable data stream. @@ -189,6 +191,8 @@ struct iio_backend_ops { 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 (*crc_enable)(struct iio_backend *back); + int (*crc_disable)(struct iio_backend *back); int (*ddr_enable)(struct iio_backend *back); int (*ddr_disable)(struct iio_backend *back); int (*data_stream_enable)(struct iio_backend *back); @@ -237,6 +241,8 @@ 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_crc_enable(struct iio_backend *back); +int iio_backend_crc_disable(struct iio_backend *back); 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); diff --git a/include/linux/iio/iio-gts-helper.h b/include/linux/iio/iio-gts-helper.h index 66f830ab9b49..0c4b2e9fbe1e 100644 --- a/include/linux/iio/iio-gts-helper.h +++ b/include/linux/iio/iio-gts-helper.h @@ -7,6 +7,7 @@ #ifndef __IIO_GTS_HELPER__ #define __IIO_GTS_HELPER__ +#include #include struct device; @@ -58,14 +59,14 @@ struct iio_itime_sel_mul { struct iio_gts { u64 max_scale; - const struct iio_gain_sel_pair *hwgain_table; + const struct iio_gain_sel_pair *hwgain_table __counted_by_ptr(num_hwgain); int num_hwgain; - const struct iio_itime_sel_mul *itime_table; + const struct iio_itime_sel_mul *itime_table __counted_by_ptr(num_itime); int num_itime; - int **per_time_avail_scale_tables; - int *avail_all_scales_table; + int **per_time_avail_scale_tables __counted_by_ptr(num_itime); + int *avail_all_scales_table __counted_by_ptr(num_avail_all_scales); int num_avail_all_scales; - int *avail_time_tables; + int *avail_time_tables __counted_by_ptr(num_avail_time_tables); int num_avail_time_tables; }; diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index b87841a355f8..98330385e08d 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -73,7 +73,7 @@ struct iio_dev_opaque { #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned int cached_reg_addr; - char read_buf[20]; + char read_buf[24]; unsigned int read_buf_len; #endif }; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 711c00f67371..9b63785d5aec 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -264,6 +264,10 @@ struct iio_scan_type { * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should * have its name field set to NULL. + * @parent: Optional pointer to the parent channel spec for + * hierarchical channel relationships. When set, a read-only + * "parent" sysfs attribute is created containing the + * parent channel's sysfs name prefix (e.g. "in_voltage0"). * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. @@ -309,6 +313,7 @@ struct iio_chan_spec { const struct iio_event_spec *event_spec; unsigned int num_event_specs; const struct iio_chan_spec_ext_info *ext_info; + const struct iio_chan_spec *parent; const char *extend_name; const char *datasheet_name; unsigned int modified:1; @@ -484,6 +489,10 @@ struct iio_trigger; /* forward declaration */ * @update_scan_mode: function to configure device and scan buffer when * channels have changed * @debugfs_reg_access: function to read or write register value of device + * @debugfs_reg64_access: function to read or write 64-bit register value of + * device. debugfs_reg_access() and debugfs_reg64_access() + * are mutually exclusive, but the 64-bit variant has + * precedence in case both are implemented. * @fwnode_xlate: fwnode based function pointer to obtain channel specifier index. * @hwfifo_set_watermark: function pointer to set the current hardware * fifo watermark level; see hwfifo_* entries in @@ -572,6 +581,9 @@ struct iio_info { int (*debugfs_reg_access)(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval); + int (*debugfs_reg64_access)(struct iio_dev *indio_dev, + unsigned int reg, u64 writeval, + u64 *readval); int (*fwnode_xlate)(struct iio_dev *indio_dev, const struct fwnode_reference_args *iiospec); int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned int val); @@ -648,7 +660,7 @@ struct iio_dev { struct iio_poll_func *pollfunc; struct iio_poll_func *pollfunc_event; - struct iio_chan_spec const *channels; + struct iio_chan_spec const *channels __counted_by_ptr(num_channels); int num_channels; const char *name; @@ -789,7 +801,7 @@ DEFINE_GUARD_COND(__priv__iio_dev_mode_lock, _try_direct, * * Tries to acquire the direct mode lock with cleanup ACQUIRE() semantics and * automatically releases it at the end of the scope. It most be always paired - * with IIO_DEV_ACQUIRE_ERR(), for example (notice the scope braces):: + * with IIO_DEV_ACQUIRE_FAILED(), for example (notice the scope braces):: * * switch() { * case IIO_CHAN_INFO_RAW: { @@ -1083,6 +1095,15 @@ static inline unsigned int iio_get_masklength(const struct iio_dev *indio_dev) return ACCESS_PRIVATE(indio_dev, masklength); } +/** + * iio_scan_timestamp_enabled - Is the timestamp channel in the current scan + * @indio_dev: the IIO device to check + */ +static inline bool iio_scan_timestamp_enabled(const struct iio_dev *indio_dev) +{ + return ACCESS_PRIVATE(indio_dev, scan_timestamp); +} + int iio_active_scan_mask_index(struct iio_dev *indio_dev); /** diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 01b6c9d9956f..4eeae9741a6e 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -46,6 +46,7 @@ * often but notifier_blocks will seldom be removed. */ +struct device; struct notifier_block; typedef int (*notifier_fn_t)(struct notifier_block *nb, @@ -145,8 +146,14 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *nb); +int devm_atomic_notifier_chain_register(struct device *dev, + struct atomic_notifier_head *nh, + struct notifier_block *nb); extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb); +int devm_blocking_notifier_chain_register(struct device *dev, + struct blocking_notifier_head *nh, + struct notifier_block *nb); extern int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb); extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index d0fb714ca0fd..d602b40f14d0 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -55,6 +55,7 @@ enum iio_chan_type { IIO_ALTCURRENT, IIO_COVERAGE, IIO_VOLUMEFLOW, + IIO_FREQUENCY, }; enum iio_modifier { diff --git a/kernel/notifier.c b/kernel/notifier.c index 2f9fe7c30287..55fbd2be7407 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -197,6 +198,58 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, } EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); +struct atomic_notifier_chain_devres { + struct atomic_notifier_head *nh; + struct notifier_block *nb; +}; + +static void devm_atomic_notifier_chain_unregister(struct device *dev, void *res) +{ + struct atomic_notifier_chain_devres *dr = res; + + atomic_notifier_chain_unregister(dr->nh, dr->nb); +} + +/** + * devm_atomic_notifier_chain_register - Device-managed atomic notifier registration + * @dev: Device to tie the notifier lifetime to + * @nh: Pointer to head of the atomic notifier chain + * @nb: New entry in notifier chain + * + * Adds a notifier to an atomic notifier chain and registers a cleanup + * action to automatically unregister it when @dev is unbound. + * + * Must be called in process context. + * + * Return: + * 0 on success, negative errno on error. + */ +int devm_atomic_notifier_chain_register(struct device *dev, + struct atomic_notifier_head *nh, + struct notifier_block *nb) +{ + struct atomic_notifier_chain_devres *dr; + int ret; + + dr = devres_alloc(devm_atomic_notifier_chain_unregister, + sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = atomic_notifier_chain_register(nh, nb); + if (ret) { + devres_free(dr); + return ret; + } + + dr->nh = nh; + dr->nb = nb; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_atomic_notifier_chain_register); + /** * atomic_notifier_call_chain - Call functions in an atomic notifier chain * @nh: Pointer to head of the atomic notifier chain @@ -349,6 +402,58 @@ int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh, } EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust); +struct blocking_notifier_chain_devres { + struct blocking_notifier_head *nh; + struct notifier_block *nb; +}; + +static void devm_blocking_notifier_chain_unregister(struct device *dev, + void *res) +{ + struct blocking_notifier_chain_devres *dr = res; + + blocking_notifier_chain_unregister(dr->nh, dr->nb); +} + +/** + * devm_blocking_notifier_chain_register - Device-managed blocking notifier registration + * @dev: Device to tie the notifier lifetime to + * @nh: Pointer to head of the blocking notifier chain + * @nb: New entry in notifier chain + * + * Adds a notifier to a blocking notifier chain and registers a cleanup + * action to automatically unregister it when @dev is unbound. + * Must be called in process context. + * + * Return: + * 0 on success, negative errno on error. + */ +int devm_blocking_notifier_chain_register(struct device *dev, + struct blocking_notifier_head *nh, + struct notifier_block *nb) +{ + struct blocking_notifier_chain_devres *dr; + int ret; + + dr = devres_alloc(devm_blocking_notifier_chain_unregister, + sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = blocking_notifier_chain_register(nh, nb); + if (ret) { + devres_free(dr); + return ret; + } + + dr->nh = nh; + dr->nb = nb; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_blocking_notifier_chain_register); + /** * blocking_notifier_call_chain - Call functions in a blocking notifier chain * @nh: Pointer to head of the blocking notifier chain diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index b5c5479b9d78..284c1fa067bf 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -67,6 +67,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", [IIO_VOLUMEFLOW] = "volumeflow", + [IIO_FREQUENCY] = "frequency", }; static const char * const iio_ev_type_text[] = { @@ -198,6 +199,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_ALTCURRENT: case IIO_COVERAGE: case IIO_VOLUMEFLOW: + case IIO_FREQUENCY: break; default: return false;