diff options
337 files changed, 21959 insertions, 3114 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-nvmem b/Documentation/ABI/stable/sysfs-bus-nvmem index aa89adf18bc5..0ae8cb074acf 100644 --- a/Documentation/ABI/stable/sysfs-bus-nvmem +++ b/Documentation/ABI/stable/sysfs-bus-nvmem @@ -11,7 +11,7 @@ Description: Read returns '0' or '1' for read-write or read-only modes respectively. Write parses one of 'YyTt1NnFf0', or [oO][NnFf] for "on" - and "off", i.e. what kstrbool() supports. + and "off", i.e. what kstrtobool() supports. Note: This file is only present if CONFIG_NVMEM_SYSFS is enabled. diff --git a/Documentation/ABI/testing/debugfs-iio-ad9467 b/Documentation/ABI/testing/debugfs-iio-ad9467 new file mode 100644 index 000000000000..0352fca1f7f2 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-iio-ad9467 @@ -0,0 +1,39 @@ +What: /sys/kernel/debug/iio/iio:deviceX/calibration_table_dump +KernelVersion: 6.11 +Contact: linux-iio@vger.kernel.org +Description: + This dumps the calibration table that was filled during the + digital interface tuning process. + +What: /sys/kernel/debug/iio/iio:deviceX/in_voltage_test_mode_available +KernelVersion: 6.11 +Contact: linux-iio@vger.kernel.org +Description: + List all the available test tones: + - off + - midscale_short + - pos_fullscale + - neg_fullscale + - checkerboard + - prbs23 + - prbs9 + - one_zero_toggle + - user + - bit_toggle + - sync + - one_bit_high + - mixed_bit_frequency + - ramp + + Note that depending on the actual device being used, some of the + above might not be available (and they won't be listed when + reading the file). + +What: /sys/kernel/debug/iio/iio:deviceX/in_voltageY_test_mode +KernelVersion: 6.11 +Contact: linux-iio@vger.kernel.org +Description: + Writing to this file will initiate one of available test tone on + channel Y. Reading it, shows which test is running. In cases + where an IIO backend is available and supports the test tone, + additional information about the data correctness is given. diff --git a/Documentation/ABI/testing/debugfs-iio-backend b/Documentation/ABI/testing/debugfs-iio-backend new file mode 100644 index 000000000000..01ab94469432 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-iio-backend @@ -0,0 +1,20 @@ +What: /sys/kernel/debug/iio/iio:deviceX/backendY/name +KernelVersion: 6.11 +Contact: linux-iio@vger.kernel.org +Description: + Name of Backend Y connected to device X. + +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: + + 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 diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 7cee78ad4108..89943c2d54e8 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -523,13 +523,27 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_capacitance_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias -What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias -What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_proximity_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_resistance_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibbias KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -541,6 +555,10 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibbias_available What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_calibbias_available +What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibbias_available +What: /sys/bus/iio/devices/iio:deviceX/in_proximity_calibbias_available +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibbias_available +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibbias_available KernelVersion: 5.8 Contact: linux-iio@vger.kernel.org Description: @@ -549,25 +567,34 @@ Description: - a small discrete set of values like "0 2 4 6 8" - a range specified as "[min step max]" -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_capacitance_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_both_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibscale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -575,6 +602,20 @@ Description: production inaccuracies). If shared across all channels, <type>_calibscale is used. +What: /sys/bus/iio/devices/iio:deviceX/in_illuminanceY_calibscale_available +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_calibscale_available +What: /sys/bus/iio/devices/iio:deviceX/in_proximityY_calibscale_available +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale_available +KernelVersion: 4.8 +Contact: linux-iio@vger.kernel.org +Description: + Available values of calibscale. Maybe expressed as either of: + + - a small discrete set of values like "1 8 16" + - a range specified as "[min step max]" + + If shared across all channels, <type>_calibscale_available is used. + What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender @@ -708,6 +749,7 @@ Description: 2.5kohm_to_gnd: connected to ground via a 2.5kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, + 42kohm_to_gnd: connected to ground via a 42kOhm resistor, 90kohm_to_gnd: connected to ground via a 90kOhm resistor, 100kohm_to_gnd: connected to ground via an 100kOhm resistor, 125kohm_to_gnd: connected to ground via an 125kOhm resistor, @@ -2289,3 +2331,11 @@ KernelVersion: 6.7 Contact: linux-iio@vger.kernel.org Description: List of available timeout value for tap gesture confirmation. + +What: /sys/.../iio:deviceX/in_shunt_resistor +What: /sys/.../iio:deviceX/in_current_shunt_resistor +What: /sys/.../iio:deviceX/in_power_shunt_resistor +KernelVersion: 6.10 +Contact: linux-iio@vger.kernel.org +Description: + The value of current sense resistor in Ohms. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 b/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 deleted file mode 100644 index 6d2d2b094941..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 +++ /dev/null @@ -1,17 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_power_shunt_resistor -Date: March 2017 -KernelVersion: 4.12 -Contact: linux-iio@vger.kernel.org -Description: The value of the shunt resistor used to compute power drain on - common input voltage pin (RS+). In Ohms. - -What: /sys/bus/iio/devices/iio:deviceX/in_current_shunt_resistor -Date: March 2017 -KernelVersion: 4.12 -Contact: linux-iio@vger.kernel.org -Description: The value of the shunt resistor used to compute current flowing - between RS+ and RS- voltage sense inputs. In Ohms. - -These attributes describe a single physical component, exposed as two distinct -attributes as it is used to calculate two different values: power load and -current flowing between RS+ and RS- inputs. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 index 469a7c00fad4..a95547e874f1 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 +++ b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 @@ -15,17 +15,3 @@ Description: Set the relative humidity. This value is sent to the sensor for humidity compensation. Default value: 50000 (50 % relative humidity) - -What: /sys/bus/iio/devices/iio:deviceX/in_resistance_calibbias -Date: August 2021 -KernelVersion: 5.15 -Contact: Andreas Klinger <ak@it-klinger.de> -Description: - Set the bias value for the resistance which is used for - calculation of in_concentration_input as follows: - - x = (in_resistance_raw - in_resistance_calibbias) * 0.65 - - in_concentration_input = 500 / (1 + e^x) - - Default value: 30000 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dac b/Documentation/ABI/testing/sysfs-bus-iio-dac new file mode 100644 index 000000000000..810eaac5533c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-dac @@ -0,0 +1,61 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_toggle_en +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + Toggle enable. Write 1 to enable toggle or 0 to disable it. This + is useful when one wants to change the DAC output codes. For + autonomous toggling, the way it should be done is: + + - disable toggle operation; + - change out_currentY_rawN, where N is the integer value of the symbol; + - enable toggle operation. + +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_rawN +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + This attribute has the same meaning as out_currentY_raw. It is + specific to toggle enabled channels and refers to the DAC output + code in INPUT_N (_rawN), where N is the integer value of the symbol. + The same scale and offset as in out_currentY_raw applies. + +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_symbol +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + Performs a SW switch to a predefined output symbol. This attribute + is specific to toggle enabled channels and allows switching between + multiple predefined symbols. Each symbol corresponds to a different + output, denoted as out_currentY_rawN, where N is the integer value + of the symbol. Writing an integer value N will select out_currentY_rawN. + +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_toggle_en +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + Toggle enable. Write 1 to enable toggle or 0 to disable it. This + is useful when one wants to change the DAC output codes. For + autonomous toggling, the way it should be done is: + + - disable toggle operation; + - change out_voltageY_rawN, where N is the integer value of the symbol; + - enable toggle operation. + +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_rawN +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + This attribute has the same meaning as out_currentY_raw. It is + specific to toggle enabled channels and refers to the DAC output + code in INPUT_N (_rawN), where N is the integer value of the symbol. + The same scale and offset as in out_currentY_raw applies. + +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_symbol +KernelVersion: 5.18 +Contact: linux-iio@vger.kernel.org +Description: + Performs a SW switch to a predefined output symbol. This attribute + is specific to toggle enabled channels and allows switching between + multiple predefined symbols. Each symbol corresponds to a different + output, denoted as out_voltageY_rawN, where N is the integer value + of the symbol. Writing an integer value N will select out_voltageY_rawN. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688 b/Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688 index 1c35971277ba..ae95a5477382 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688 +++ b/Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688 @@ -53,34 +53,3 @@ KernelVersion: 5.18 Contact: linux-iio@vger.kernel.org Description: Returns the available values for the dither phase. - -What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_toggle_en -KernelVersion: 5.18 -Contact: linux-iio@vger.kernel.org -Description: - Toggle enable. Write 1 to enable toggle or 0 to disable it. This is - useful when one wants to change the DAC output codes. The way it should - be done is: - - - disable toggle operation; - - change out_voltageY_raw0 and out_voltageY_raw1; - - enable toggle operation. - -What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw0 -What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw1 -KernelVersion: 5.18 -Contact: linux-iio@vger.kernel.org -Description: - It has the same meaning as out_voltageY_raw. This attribute is - specific to toggle enabled channels and refers to the DAC output - code in INPUT_A (_raw0) and INPUT_B (_raw1). The same scale and offset - as in out_voltageY_raw applies. - -What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_symbol -KernelVersion: 5.18 -Contact: linux-iio@vger.kernel.org -Description: - Performs a SW toggle. This attribute is specific to toggle - enabled channels and allows to toggle between out_voltageY_raw0 - and out_voltageY_raw1 through software. Writing 0 will select - out_voltageY_raw0 while 1 selects out_voltageY_raw1. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 b/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 index 31dbb390573f..c431f0a13cf5 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 +++ b/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 @@ -3,7 +3,7 @@ KernelVersion: Contact: linux-iio@vger.kernel.org Description: Reading this returns the valid values that can be written to the - on_altvoltage0_mode attribute: + filter_mode attribute: - auto -> Adjust bandpass filter to track changes in input clock rate. - manual -> disable/unregister the clock rate notifier / input clock tracking. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc b/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc index 8916f7ec6507..8dbca113112d 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc +++ b/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc @@ -13,12 +13,3 @@ Description: available for reading data. However, samples can be occasionally skipped or repeated, depending on the beat between the capture and conversion rates. - -What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistor -Date: December 2015 -KernelVersion: 4.4 -Contact: linux-iio@vger.kernel.org -Description: - The value of the shunt resistor may be known only at runtime fom an - eeprom content read by a client application. This attribute allows to - set its value in ohms. diff --git a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml index d5cfa32ea52d..072b3c0c5fd0 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml +++ b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml @@ -37,6 +37,11 @@ properties: GPIO pin (output) used to control VBUS. If skipped, no such control takes place. + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the usb controller for the dual role switch. + required: - compatible - interrupts @@ -58,5 +63,11 @@ examples: interrupt-parent = <&msmgpio>; interrupts = <78 IRQ_TYPE_LEVEL_HIGH>; vbus-gpios = <&msmgpio 148 GPIO_ACTIVE_HIGH>; + + port { + endpoint { + remote-endpoint = <&usb1_drd_sw>; + }; + }; }; }; diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt deleted file mode 100644 index dfc14f71e81f..000000000000 --- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt +++ /dev/null @@ -1,21 +0,0 @@ -USB GPIO Extcon device - -This is a virtual device used to generate USB cable states from the USB ID pin -connected to a GPIO pin. - -Required properties: -- compatible: Should be "linux,extcon-usb-gpio" - -Either one of id-gpio or vbus-gpio must be present. Both can be present as well. -- id-gpio: gpio for USB ID pin. See gpio binding. -- vbus-gpio: gpio for USB VBUS pin. - -Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below: - extcon_usb1 { - compatible = "linux,extcon-usb-gpio"; - id-gpio = <&gpio6 1 GPIO_ACTIVE_HIGH>; - } - - &omap_dwc3_1 { - extcon = <&extcon_usb1>; - }; diff --git a/Documentation/devicetree/bindings/extcon/linux,extcon-usb-gpio.yaml b/Documentation/devicetree/bindings/extcon/linux,extcon-usb-gpio.yaml new file mode 100644 index 000000000000..8856107bdd33 --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/linux,extcon-usb-gpio.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/extcon/linux,extcon-usb-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: USB GPIO Extcon device + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +description: + This is a virtual device used to generate USB cable states from the USB ID pin + connected to a GPIO pin. + +properties: + compatible: + const: linux,extcon-usb-gpio + + id-gpios: + description: gpio for USB ID pin. See gpio binding. + vbus-gpios: + description: gpio for USB VBUS pin. + +required: + - compatible + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + extcon_usb1 { + compatible = "linux,extcon-usb-gpio"; + id-gpios = <&gpio6 1 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl380.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl380.yaml new file mode 100644 index 000000000000..f1ff5ff4f478 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl380.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/adi,adxl380.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADXL380/382 3-Axis Digital Accelerometer + +maintainers: + - Ramona Gradinariu <ramona.gradinariu@analog.com> + - Antoniu Miclaus <antoniu.miclaus@analog.com> + +description: | + The ADXL380/ADXL382 is a low noise density, low power, 3-axis + accelerometer with selectable measurement ranges. The ADXL380 + supports the ±4 g, ±8 g, and ±16 g ranges, and the ADXL382 supports + ±15 g, ±30 g, and ±60 g ranges. + + https://www.analog.com/en/products/adxl380.html + +properties: + compatible: + enum: + - adi,adxl380 + - adi,adxl382 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - enum: [INT0, INT1] + - const: INT1 + + vddio-supply: true + + vsupply-supply: true + +required: + - compatible + - reg + - interrupts + - interrupt-names + - vddio-supply + - vsupply-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + accelerometer@54 { + compatible = "adi,adxl380"; + reg = <0x54>; + vddio-supply = <&vddio>; + vsupply-supply = <&vsupply>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT0"; + }; + }; + - | + #include <dt-bindings/interrupt-controller/irq.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + accelerometer@0 { + compatible = "adi,adxl380"; + reg = <0>; + spi-max-frequency = <8000000>; + vddio-supply = <&vddio>; + vsupply-supply = <&vsupply>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT0"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml index 6ddb03f61bd9..951a3a2ba8fc 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml @@ -16,6 +16,7 @@ properties: - kionix,kxcj91008 - kionix,kxtj21009 - kionix,kxtf9 + - kionix,kx022-1020 - kionix,kx023-1025 reg: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml new file mode 100644 index 000000000000..310f046e139f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml @@ -0,0 +1,254 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4695.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Easy Drive Multiplexed SAR Analog to Digital Converters + +maintainers: + - Michael Hennerich <Michael.Hennerich@analog.com> + - Nuno Sá <nuno.sa@analog.com> + +description: | + A family of similar multi-channel analog to digital converters with SPI bus. + + * https://www.analog.com/en/products/ad4695.html + * https://www.analog.com/en/products/ad4696.html + * https://www.analog.com/en/products/ad4697.html + * https://www.analog.com/en/products/ad4698.html + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,ad4695 + - adi,ad4696 + - adi,ad4697 + - adi,ad4698 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 80000000 + + spi-cpol: true + spi-cpha: true + + spi-rx-bus-width: + minimum: 1 + maximum: 4 + + avdd-supply: + description: Analog power supply. + + vio-supply: + description: I/O pin power supply. + + ldo-in-supply: + description: Internal LDO Input. Mutually exclusive with vdd-supply. + + vdd-supply: + description: Core power supply. Mutually exclusive with ldo-in-supply. + + ref-supply: + description: + External reference voltage. Mutually exclusive with refin-supply. + + refin-supply: + description: + Internal reference buffer input. Mutually exclusive with ref-supply. + + com-supply: + description: Common voltage supply for pseudo-differential analog inputs. + + adi,no-ref-current-limit: + $ref: /schemas/types.yaml#/definitions/flag + description: + When this flag is present, the REF Overvoltage Reduced Current protection + is disabled. + + adi,no-ref-high-z: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable this flag if the ref-supply requires Reference Input High-Z Mode + to be disabled for proper operation. + + cnv-gpios: + description: The Convert Input (CNV). If omitted, CNV is tied to SPI CS. + maxItems: 1 + + reset-gpios: + description: The Reset Input (RESET). Should be configured GPIO_ACTIVE_LOW. + maxItems: 1 + + interrupts: + minItems: 1 + items: + - description: Signal coming from the BSY_ALT_GP0 pin (ALERT or BUSY). + - description: Signal coming from the GP2 pin (ALERT). + - description: Signal coming from the GP3 pin (BUSY). + + interrupt-names: + minItems: 1 + items: + - const: gp0 + - const: gp2 + - const: gp3 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is the GPn number: 0 to 3. + The second cell takes standard GPIO flags. + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^in(?:[13579]|1[135])-supply$": + description: + Optional voltage supply for odd numbered channels when they are used as + the negative input for a pseudo-differential channel. + + "^channel@[0-9a-f]$": + type: object + $ref: adc.yaml + unevaluatedProperties: false + description: + Describes each individual channel. In addition the properties defined + below, bipolar from adc.yaml is also supported. + + properties: + reg: + maximum: 15 + + common-mode-channel: + description: + Describes the common mode channel for single channels. 0xFF is REFGND + and OxFE is COM. Macros are available for these values in + dt-bindings/iio/adi,ad4695.h. Values 1 to 15 correspond to INx inputs. + Only odd numbered INx inputs can be used as common mode channels. + enum: [1, 3, 5, 7, 9, 11, 13, 15, 0xFE, 0xFF] + default: 0xFF + + adi,no-high-z: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable this flag if the input pin requires the Analog Input High-Z + Mode to be disabled for proper operation. + + required: + - reg + + allOf: + # bipolar mode can't be used with REFGND + - if: + properties: + common-mode-channel: + const: 0xFF + then: + properties: + bipolar: false + +required: + - compatible + - reg + - avdd-supply + - vio-supply + +allOf: + - oneOf: + - required: + - ldo-in-supply + - required: + - vdd-supply + + - oneOf: + - required: + - ref-supply + - required: + - refin-supply + + # the internal reference buffer always requires high-z mode + - if: + required: + - refin-supply + then: + properties: + adi,no-ref-high-z: false + + # limit channels for 8-channel chips + - if: + properties: + compatible: + contains: + enum: + - adi,ad4697 + - adi,ad4698 + then: + patternProperties: + "^in(?:9|1[135])-supply$": false + "^channel@[0-7]$": + properties: + reg: + maximum: 7 + common-mode-channel: + enum: [1, 3, 5, 7, 0xFE, 0xFF] + "^channel@[8-9a-f]$": false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/iio/adi,ad4695.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4695"; + reg = <0>; + spi-cpol; + spi-cpha; + spi-max-frequency = <80000000>; + avdd-supply = <&power_supply>; + ldo-in-supply = <&power_supply>; + vio-supply = <&io_supply>; + refin-supply = <&supply_5V>; + com-supply = <&supply_2V5>; + in3-supply = <&supply_2V5>; + reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + + #address-cells = <1>; + #size-cells = <0>; + + /* Pseudo-differential channel between IN0 and REFGND. */ + channel@0 { + reg = <0>; + }; + + /* Pseudo-differential channel between IN1 and COM. */ + channel@1 { + reg = <1>; + common-mode-channel = <AD4695_COMMON_MODE_COM>; + bipolar; + }; + + /* Pseudo-differential channel between IN2 and IN3. */ + channel@2 { + reg = <2>; + common-mode-channel = <3>; + bipolar; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml index 190889c7b62a..66dd1c549bd3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml @@ -39,11 +39,21 @@ properties: clocks: maxItems: 1 - description: phandle to the master clock (mclk) + description: + Optionally, either a crystal can be attached externally between MCLK1 and + MCLK2 pins, or an external CMOS-compatible clock can drive the MCLK2 + pin. If absent, internal 4.92MHz clock is used, which can be made + available on MCLK2 pin. clock-names: - items: - - const: mclk + enum: + - xtal + - mclk + + "#clock-cells": + const: 0 + description: + If present when internal clock is used, configured as clock provider. interrupts: maxItems: 1 @@ -134,8 +144,6 @@ patternProperties: required: - compatible - reg - - clocks - - clock-names - interrupts - dvdd-supply - avdd-supply @@ -156,6 +164,18 @@ allOf: then: patternProperties: "^channel@[0-9a-f]+$": false + - if: + anyOf: + - required: + - clocks + - required: + - clock-names + then: + properties: + "#clock-cells": false + required: + - clocks + - clock-names unevaluatedProperties: false @@ -201,8 +221,7 @@ examples: spi-max-frequency = <1000000>; spi-cpol; spi-cpha; - clocks = <&ad7192_mclk>; - clock-names = "mclk"; + #clock-cells = <0>; interrupts = <25 0x2>; interrupt-parent = <&gpio>; aincom-supply = <&aincom>; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml index 899b777017ce..bd19abb867d9 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml @@ -15,10 +15,17 @@ description: | * https://www.analog.com/en/products/ad7381.html * https://www.analog.com/en/products/ad7383.html * https://www.analog.com/en/products/ad7384.html + * https://www.analog.com/en/products/ad7386.html + * https://www.analog.com/en/products/ad7387.html + * https://www.analog.com/en/products/ad7388.html * https://www.analog.com/en/products/ad7380-4.html * https://www.analog.com/en/products/ad7381-4.html * https://www.analog.com/en/products/ad7383-4.html * https://www.analog.com/en/products/ad7384-4.html + * https://www.analog.com/en/products/ad7386-4.html + * https://www.analog.com/en/products/ad7387-4.html + * https://www.analog.com/en/products/ad7388-4.html + $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -29,10 +36,16 @@ properties: - adi,ad7381 - adi,ad7383 - adi,ad7384 + - adi,ad7386 + - adi,ad7387 + - adi,ad7388 - adi,ad7380-4 - adi,ad7381-4 - adi,ad7383-4 - adi,ad7384-4 + - adi,ad7386-4 + - adi,ad7387-4 + - adi,ad7388-4 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 00fdaed11cbd..69408cae3db9 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -35,65 +35,83 @@ properties: avcc-supply: true + vdrive-supply: + description: + Determines the voltage level at which the interface logic pins will + operate. + + refin-supply: + description: + The voltage supply for optional external reference voltage. + interrupts: + description: + The BUSY pin falling edge indicates that the conversion is over, and thus + new data is available. maxItems: 1 adi,conversion-start-gpios: description: - Must be the device tree identifier of the CONVST pin. - This logic input is used to initiate conversions on the analog - input channels. As the line is active high, it should be marked - GPIO_ACTIVE_HIGH. - maxItems: 1 + Must be the device tree identifier of the CONVST pin(s). This logic input + is used to initiate conversions on the analog input channels. As the line + is active high, it should be marked GPIO_ACTIVE_HIGH. + minItems: 1 + maxItems: 2 reset-gpios: description: - Must be the device tree identifier of the RESET pin. If specified, - it will be asserted during driver probe. As the line is active high, - it should be marked GPIO_ACTIVE_HIGH. + Must be the device tree identifier of the RESET pin. If specified, it will + be asserted during driver probe. On the AD7606x, as the line is active + high, it should be marked GPIO_ACTIVE_HIGH. On the AD7616, as the line is + active low, it should be marked GPIO_ACTIVE_LOW. maxItems: 1 standby-gpios: description: - Must be the device tree identifier of the STBY pin. This pin is used - to place the AD7606 into one of two power-down modes, Standby mode or + Must be the device tree identifier of the STBY pin. This pin is used to + place the AD7606 into one of two power-down modes, Standby mode or Shutdown mode. As the line is active low, it should be marked GPIO_ACTIVE_LOW. maxItems: 1 adi,first-data-gpios: description: - Must be the device tree identifier of the FRSTDATA pin. - The FRSTDATA output indicates when the first channel, V1, is - being read back on either the parallel, byte or serial interface. - As the line is active high, it should be marked GPIO_ACTIVE_HIGH. + Must be the device tree identifier of the FRSTDATA pin. The FRSTDATA + output indicates when the first channel, V1, is being read back on either + the parallel, byte or serial interface. As the line is active high, it + should be marked GPIO_ACTIVE_HIGH. maxItems: 1 adi,range-gpios: description: - Must be the device tree identifier of the RANGE pin. The polarity on - this pin determines the input range of the analog input channels. If - this pin is tied to a logic high, the analog input range is ±10V for - all channels. If this pin is tied to a logic low, the analog input range + Must be the device tree identifier of the RANGE pin. The state on this + pin determines the input range of the analog input channels. If this pin + is tied to a logic high, the analog input range is ±10V for all channels. + On the AD760X, if this pin is tied to a logic low, the analog input range is ±5V for all channels. As the line is active high, it should be marked - GPIO_ACTIVE_HIGH. - maxItems: 1 + GPIO_ACTIVE_HIGH. On the AD7616, there are 2 pins, and if the 2 pins are + tied to a logic high, software mode is enabled, otherwise one of the 3 + possible range values is selected. + minItems: 1 + maxItems: 2 adi,oversampling-ratio-gpios: description: - Must be the device tree identifier of the over-sampling - mode pins. As the line is active high, it should be marked - GPIO_ACTIVE_HIGH. + Must be the device tree identifier of the over-sampling mode pins. As the + line is active high, it should be marked GPIO_ACTIVE_HIGH. On the AD7606X + parts that support it, if all 3 pins are tied to a logic high, software + mode is enabled. maxItems: 3 adi,sw-mode: description: - Software mode of operation, so far available only for ad7616 and ad7606b. - It is enabled when all three oversampling mode pins are connected to - high level. The device is configured by the corresponding registers. If the - adi,oversampling-ratio-gpios property is defined, then the driver will set the - oversampling gpios to high. Otherwise, it is assumed that the pins are hardwired - to VDD. + Software mode of operation, so far available only for AD7616 and AD7606B. + It is enabled when all three oversampling mode pins are connected to high + level for the AD7606B, or both the range selection are connected to high + level for the AD7616. The device is configured by the corresponding + registers. If the adi,oversampling-ratio-gpios property is defined, then + the driver will set the oversampling gpios to high. Otherwise, it is + assumed that the pins are hardwired to VDD. type: boolean required: @@ -101,12 +119,57 @@ required: - reg - spi-cpha - avcc-supply + - vdrive-supply - interrupts - adi,conversion-start-gpios allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: adi,ad7616 + then: + properties: + adi,first-data-gpios: false + standby-gpios: false + adi,range-gpios: + maxItems: 2 + else: + properties: + adi,range-gpios: + maxItems: 1 + + - if: + properties: + compatible: + contains: + enum: + - adi,ad7605-4 + - adi,ad7616 + then: + properties: + adi,oversampling-ratio-gpios: false + + - if: + properties: + compatible: + contains: + enum: + - adi,ad7605-4 + - adi,ad7606-4 + - adi,ad7606-6 + - adi,ad7606-8 + then: + properties: + adi,sw-mode: false + else: + properties: + adi,conversion-start-gpios: + maxItems: 1 + unevaluatedProperties: false examples: @@ -125,6 +188,7 @@ examples: spi-cpha; avcc-supply = <&adc_vref>; + vdrive-supply = <&vdd_supply>; interrupts = <25 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpio>; @@ -136,7 +200,6 @@ examples: <&gpio 23 GPIO_ACTIVE_HIGH>, <&gpio 26 GPIO_ACTIVE_HIGH>; standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; - adi,sw-mode; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml index eecd5fbab695..2606c0c5dfc6 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml @@ -28,6 +28,9 @@ properties: - adi,ad9265 - adi,ad9434 - adi,ad9467 + - adi,ad9643 + - adi,ad9649 + - adi,ad9652 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,pac1921.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,pac1921.yaml new file mode 100644 index 000000000000..12e56b1b3d3f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/microchip,pac1921.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/microchip,pac1921.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PAC1921 High-Side Power/Current Monitor with Anaog Output + +maintainers: + - Matteo Martelli <matteomartelli3@gmail.com> + +description: | + The PAC1921 is a power/current monitoring device with an analog output + and I2C/SMBus interface. + + Datasheet can be found here: + https://ww1.microchip.com/downloads/en/DeviceDoc/PAC1921-Data-Sheet-DS20005293E.pdf + +properties: + compatible: + const: microchip,pac1921 + + reg: + maxItems: 1 + + vdd-supply: true + + "#io-channel-cells": + const: 1 + + shunt-resistor-micro-ohms: + description: + Value in micro Ohms of the shunt resistor connected between + the SENSE+ and SENSE- inputs, across which the current is measured. + Value is needed to compute the scaling of the measured current. + + label: + description: Unique name to identify which device this is. + + read-integrate-gpios: + description: + READ/INT input pin to control the current state of the device, either in + the INTEGRATE state when driven high, or in the READ state when driven low. + When not connected the pin is floating and it can be overridden by the + INT_EN register bit after asserting the READ/INT_OVR register bit. + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + - shunt-resistor-micro-ohms + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@4c { + compatible = "microchip,pac1921"; + reg = <0x4c>; + vdd-supply = <&vdd>; + #io-channel-cells = <1>; + label = "vbat"; + shunt-resistor-micro-ohms = <10000>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml index aa24b841393c..fd93ed3991e0 100644 --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml @@ -17,6 +17,9 @@ properties: - const: rockchip,rk3399-saradc - const: rockchip,rk3588-saradc - items: + - const: rockchip,rk3576-saradc + - const: rockchip,rk3588-saradc + - items: - enum: - rockchip,px30-saradc - rockchip,rk3308-saradc diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.yaml b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.yaml index cab0d425eaa4..c3a116427dc3 100644 --- a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.yaml +++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.yaml @@ -18,18 +18,39 @@ properties: - sd-modulator - ads1201 + '#io-backend-cells': + const: 0 + '#io-channel-cells': const: 0 + vref-supply: + description: Phandle to the vref input analog reference voltage. + +dependencies: + vref-supply: [ '#io-backend-cells' ] + required: - compatible - - '#io-channel-cells' + +anyOf: + - required: ['#io-backend-cells'] + - required: ['#io-channel-cells'] additionalProperties: false examples: - | - ads1202: adc { + // Backend binding example. SD modulator configured as an IIO backend device + ads1201_0: adc { + compatible = "sd-modulator"; + vref-supply = <&vdd_adc>; + #io-backend-cells = <0>; + }; + + - | + // Legacy binding example. SD modulator configured as an IIO channel provider + ads1201_1: adc { compatible = "sd-modulator"; #io-channel-cells = <0>; }; diff --git a/Documentation/devicetree/bindings/iio/adc/sophgo,cv1800b-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/sophgo,cv1800b-saradc.yaml new file mode 100644 index 000000000000..f652b98615f7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/sophgo,cv1800b-saradc.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/sophgo,cv1800b-saradc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: + Sophgo CV1800B SoC 3 channels Successive Approximation Analog to + Digital Converters + +maintainers: + - Thomas Bonnefille <thomas.bonnefille@bootlin.com> + +description: + Datasheet at https://github.com/sophgo/sophgo-doc/releases + +properties: + compatible: + const: sophgo,cv1800b-saradc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@[0-2]$": + $ref: adc.yaml + + properties: + reg: + items: + - minimum: 0 + maximum: 2 + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/sophgo,cv1800.h> + #include <dt-bindings/interrupt-controller/irq.h> + adc@30f0000 { + compatible = "sophgo,cv1800b-saradc"; + reg = <0x030f0000 0x1000>; + clocks = <&clk CLK_SARADC>; + interrupts = <100 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + }; + + channel@1 { + reg = <1>; + }; + + channel@2 { + reg = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index ec34c48d4878..ef9dcc365eab 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -54,7 +54,9 @@ properties: It's not present on stm32f4. It's required on stm32h7 and stm32mp1. - clock-names: true + clock-names: + minItems: 1 + maxItems: 2 st,max-clk-rate-hz: description: diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml index 2722edab1d9a..c24ac98bbb3d 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml @@ -102,9 +102,11 @@ patternProperties: items: minimum: 0 maximum: 7 + deprecated: true st,adc-channel-names: description: List of single-ended channel names. + deprecated: true st,filter-order: description: | @@ -118,6 +120,12 @@ patternProperties: "#io-channel-cells": const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + st,adc-channel-types: description: | Single-ended channel input type. @@ -128,6 +136,7 @@ patternProperties: items: enum: [ SPI_R, SPI_F, MANCH_R, MANCH_F ] $ref: /schemas/types.yaml#/definitions/non-unique-string-array + deprecated: true st,adc-channel-clk-src: description: | @@ -139,6 +148,7 @@ patternProperties: items: enum: [ CLKIN, CLKOUT, CLKOUT_F, CLKOUT_R ] $ref: /schemas/types.yaml#/definitions/non-unique-string-array + deprecated: true st,adc-alt-channel: description: @@ -147,6 +157,7 @@ patternProperties: If not set, channel n is connected to SPI input n. If set, channel n is connected to SPI input n + 1. type: boolean + deprecated: true st,filter0-sync: description: @@ -165,11 +176,60 @@ patternProperties: - compatible - reg - interrupts - - st,adc-channels - - st,adc-channel-names - st,filter-order - "#io-channel-cells" + patternProperties: + "^channel@[0-7]$": + type: object + $ref: adc.yaml + unevaluatedProperties: false + description: Represents the external channels which are connected to the DFSDM. + + properties: + reg: + maximum: 7 + + label: + description: + Unique name to identify which channel this is. + + st,adc-channel-type: + description: | + Single-ended channel input type. + - "SPI_R": SPI with data on rising edge (default) + - "SPI_F": SPI with data on falling edge + - "MANCH_R": manchester codec, rising edge = logic 0, falling edge = logic 1 + - "MANCH_F": manchester codec, rising edge = logic 1, falling edge = logic 0 + $ref: /schemas/types.yaml#/definitions/string + enum: [ SPI_R, SPI_F, MANCH_R, MANCH_F ] + + st,adc-channel-clk-src: + description: | + Conversion clock source. + - "CLKIN": external SPI clock (CLKIN x) + - "CLKOUT": internal SPI clock (CLKOUT) (default) + - "CLKOUT_F": internal SPI clock divided by 2 (falling edge). + - "CLKOUT_R": internal SPI clock divided by 2 (rising edge). + $ref: /schemas/types.yaml#/definitions/string + enum: [ CLKIN, CLKOUT, CLKOUT_F, CLKOUT_R ] + + st,adc-alt-channel: + description: + Must be defined if two sigma delta modulators are + connected on same SPI input. + If not set, channel n is connected to SPI input n. + If set, channel n is connected to SPI input n + 1. + type: boolean + + io-backends: + description: + Used to pipe external sigma delta modulator or internal ADC backend to DFSDM channel. + maxItems: 1 + + required: + - reg + allOf: - if: properties: @@ -199,9 +259,19 @@ patternProperties: description: From common IIO binding. Used to pipe external sigma delta modulator or internal ADC output to DFSDM channel. + deprecated: true - required: - - io-channels + if: + required: + - st,adc-channels + then: + required: + - io-channels + + patternProperties: + "^channel@[0-7]$": + required: + - io-backends - if: properties: @@ -298,6 +368,7 @@ examples: #address-cells = <1>; #size-cells = <0>; + // Example 1: Audio use case with generic binding dfsdm0: filter@0 { compatible = "st,stm32-dfsdm-dmic"; reg = <0>; @@ -305,12 +376,18 @@ examples: dmas = <&dmamux1 101 0x400 0x01>; dma-names = "rx"; #io-channel-cells = <1>; - st,adc-channels = <1>; - st,adc-channel-names = "dmic0"; - st,adc-channel-types = "SPI_R"; - st,adc-channel-clk-src = "CLKOUT"; + #address-cells = <1>; + #size-cells = <0>; st,filter-order = <5>; + channel@1 { + reg = <1>; + label = "dmic0"; + st,adc-channel-type = "SPI_R"; + st,adc-channel-clk-src = "CLKOUT"; + st,adc-alt-channel; + }; + asoc_pdm0: dfsdm-dai { compatible = "st,stm32h7-dfsdm-dai"; #sound-dai-cells = <0>; @@ -318,19 +395,34 @@ examples: }; }; - dfsdm_pdm1: filter@1 { + // Example 2: Analog use case with generic binding + dfsdm1: filter@1 { compatible = "st,stm32-dfsdm-adc"; reg = <1>; interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>; dmas = <&dmamux1 102 0x400 0x01>; dma-names = "rx"; - #io-channel-cells = <1>; - st,adc-channels = <2 3>; - st,adc-channel-names = "in2", "in3"; - st,adc-channel-types = "SPI_R", "SPI_R"; - st,adc-channel-clk-src = "CLKOUT_F", "CLKOUT_F"; - io-channels = <&sd_adc2 &sd_adc3>; st,filter-order = <1>; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + channel@2 { + reg = <2>; + label = "in2"; + st,adc-channel-type = "SPI_F"; + st,adc-channel-clk-src = "CLKOUT"; + st,adc-alt-channel; + io-backends = <&sd_adc2>; + }; + + channel@3 { + reg = <3>; + label = "in3"; + st,adc-channel-type = "SPI_R"; + st,adc-channel-clk-src = "CLKOUT"; + io-backends = <&sd_adc3>; + }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml index d40689f233f2..1caa896fce82 100644 --- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml @@ -37,6 +37,17 @@ description: | 3 | batt_dischrg_i 4 | ts_v + AXP717 + ------ + 0 | batt_v + 1 | ts_v + 2 | vbus_v + 3 | vsys_v + 4 | pmic_temp + 5 | batt_chrg_i + 6 | vmid_v + 7 | bkup_batt_v + AXP813 ------ 0 | pmic_temp @@ -52,6 +63,7 @@ properties: oneOf: - const: x-powers,axp209-adc - const: x-powers,axp221-adc + - const: x-powers,axp717-adc - const: x-powers,axp813-adc - items: diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ltc2664.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ltc2664.yaml new file mode 100644 index 000000000000..33490853497b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ltc2664.yaml @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ltc2664.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2664 DAC + +maintainers: + - Michael Hennerich <michael.hennerich@analog.com> + - Kim Seer Paller <kimseer.paller@analog.com> + +description: | + Analog Devices LTC2664 4 channel, 12-/16-Bit, +-10V DAC + https://www.analog.com/media/en/technical-documentation/data-sheets/2664fa.pdf + +properties: + compatible: + enum: + - adi,ltc2664 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 50000000 + + vcc-supply: + description: Analog Supply Voltage Input. + + v-pos-supply: + description: Positive Supply Voltage Input. + + v-neg-supply: + description: Negative Supply Voltage Input. + + iovcc-supply: + description: Digital Input/Output Supply Voltage. + + 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 Asynchronous Clear Input. A logic low at this level-triggered + input clears the part to the reset code and range determined by the + hardwired option chosen using the MSPAN pins. The control registers are + cleared to zero. + maxItems: 1 + + adi,manual-span-operation-config: + description: + This property must mimic the MSPAN pin configurations. By tying the MSPAN + pins (MSP2, MSP1 and MSP0) to GND and/or VCC, any output range can be + hardware-configured with different mid-scale or zero-scale reset options. + The hardware configuration is latched during power on reset for proper + operation. + 0 - MPS2=GND, MPS1=GND, MSP0=GND (+-10V, reset to 0V) + 1 - MPS2=GND, MPS1=GND, MSP0=VCC (+-5V, reset to 0V) + 2 - MPS2=GND, MPS1=VCC, MSP0=GND (+-2.5V, reset to 0V) + 3 - MPS2=GND, MPS1=VCC, MSP0=VCC (0V to 10, reset to 0V) + 4 - MPS2=VCC, MPS1=GND, MSP0=GND (0V to 10V, reset to 5V) + 5 - MPS2=VCC, MPS1=GND, MSP0=VCC (0V to 5V, reset to 0V) + 6 - MPS2=VCC, MPS1=VCC, MSP0=GND (0V to 5V, reset to 2.5V) + 7 - MPS2=VCC, MPS1=VCC, MSP0=VCC (0V to 5V, reset to 0V, enables SoftSpan) + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4, 5, 6, 7] + default: 7 + + io-channels: + description: + ADC channel to monitor voltages and temperature at the MUXOUT pin. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@[0-3]$": + $ref: dac.yaml + type: object + additionalProperties: false + + properties: + reg: + description: The channel number representing the DAC output channel. + maximum: 3 + + adi,toggle-mode: + description: + Set the channel as a toggle enabled channel. Toggle operation enables + fast switching of a DAC output between two different DAC codes without + any SPI transaction. + type: boolean + + output-range-microvolt: + description: + This property is only allowed when SoftSpan is enabled. If not present, + [0, 5000000] is the default output range. + oneOf: + - items: + - const: 0 + - enum: [5000000, 10000000] + - items: + - const: -5000000 + - const: 5000000 + - items: + - const: -10000000 + - const: 10000000 + - items: + - const: -2500000 + - const: 2500000 + + required: + - reg + + allOf: + - if: + not: + properties: + adi,manual-span-operation-config: + const: 7 + then: + patternProperties: + "^channel@[0-3]$": + properties: + output-range-microvolt: false + +required: + - compatible + - reg + - spi-max-frequency + - vcc-supply + - iovcc-supply + - v-pos-supply + - v-neg-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + dac@0 { + compatible = "adi,ltc2664"; + reg = <0>; + spi-max-frequency = <10000000>; + + vcc-supply = <&vcc>; + iovcc-supply = <&vcc>; + ref-supply = <&vref>; + v-pos-supply = <&vpos>; + v-neg-supply = <&vneg>; + + io-channels = <&adc 0>; + + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + adi,toggle-mode; + output-range-microvolt = <(-10000000) 10000000>; + }; + + channel@1 { + reg = <1>; + output-range-microvolt= <0 10000000>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ltc2672.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ltc2672.yaml new file mode 100644 index 000000000000..c8c434c10643 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ltc2672.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ltc2672.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2672 DAC + +maintainers: + - Michael Hennerich <michael.hennerich@analog.com> + - Kim Seer Paller <kimseer.paller@analog.com> + +description: | + Analog Devices LTC2672 5 channel, 12-/16-Bit, 300mA DAC + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2672.pdf + +properties: + compatible: + enum: + - adi,ltc2672 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 50000000 + + vcc-supply: + description: Analog Supply Voltage Input. + + v-neg-supply: + description: Negative Supply Voltage Input. + + vdd0-supply: + description: Positive Supply Voltage Input for DAC OUT0. + + vdd1-supply: + description: Positive Supply Voltage Input for DAC OUT1. + + vdd2-supply: + description: Positive Supply Voltage Input for DAC OUT2. + + vdd3-supply: + description: Positive Supply Voltage Input for DAC OUT3. + + vdd4-supply: + description: Positive Supply Voltage Input for DAC OUT4. + + iovcc-supply: + description: Digital Input/Output Supply Voltage. + + 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 Asynchronous Clear Input. A logic low at this level triggered + input clears the device to the default reset code and output range, which + is zero-scale with the outputs off. The control registers are cleared to + zero. + maxItems: 1 + + adi,rfsadj-ohms: + description: + If FSADJ is tied to VCC, an internal RFSADJ (20 kΩ) is selected, which + results in nominal output ranges. When an external resistor of 19 kΩ to + 41 kΩ can be used instead by connecting the resistor between FSADJ and GND + it controls the scaling of the ranges, and the internal resistor is + automatically disconnected. + minimum: 19000 + maximum: 41000 + default: 20000 + + io-channels: + description: + ADC channel to monitor voltages and currents at the MUX pin. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@[0-4]$": + $ref: dac.yaml + type: object + additionalProperties: false + + properties: + reg: + description: The channel number representing the DAC output channel. + maximum: 4 + + adi,toggle-mode: + description: + Set the channel as a toggle enabled channel. Toggle operation enables + fast switching of a DAC output between two different DAC codes without + any SPI transaction. + type: boolean + + output-range-microamp: + items: + - const: 0 + - enum: [3125000, 6250000, 12500000, 25000000, 50000000, 100000000, + 200000000, 300000000] + + required: + - reg + - output-range-microamp + +required: + - compatible + - reg + - spi-max-frequency + - vcc-supply + - iovcc-supply + - v-neg-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + dac@0 { + compatible = "adi,ltc2672"; + reg = <0>; + spi-max-frequency = <10000000>; + + vcc-supply = <&vcc>; + iovcc-supply = <&vcc>; + ref-supply = <&vref>; + v-neg-supply = <&vneg>; + + io-channels = <&adc 0>; + + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + adi,toggle-mode; + output-range-microamp = <0 3125000>; + }; + + channel@1 { + reg = <1>; + output-range-microamp = <0 6250000>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/dac.yaml b/Documentation/devicetree/bindings/iio/dac/dac.yaml new file mode 100644 index 000000000000..daa40724e1cf --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/dac.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/dac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IIO Common Properties for DAC Channels + +maintainers: + - Jonathan Cameron <jic23@kernel.org> + +description: + A few properties are defined in a common way for DAC channels. + +properties: + $nodename: + pattern: "^channel(@[0-9a-f]+)?$" + description: + A channel index should match reg. + + reg: + maxItems: 1 + + label: + description: Unique name to identify which channel this is. + + output-range-microamp: + maxItems: 2 + minItems: 2 + description: + Specify the channel output full scale range in microamperes. + + output-range-microvolt: + maxItems: 2 + minItems: 2 + description: + Specify the channel output full scale range in microvolts. + +anyOf: + - oneOf: + - required: + - reg + - output-range-microamp + - required: + - reg + - output-range-microvolt + - required: + - reg + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml index aa6a3193b4e0..5f950ee9aec7 100644 --- a/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml @@ -17,6 +17,7 @@ description: | applications. https://www.analog.com/en/products/adf4377.html + https://www.analog.com/en/products/adf4378.html properties: compatible: @@ -73,6 +74,15 @@ required: allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - adi,adf4378 + then: + properties: + clk2-enable-gpios: false unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/iio/humidity/sciosense,ens210.yaml b/Documentation/devicetree/bindings/iio/humidity/sciosense,ens210.yaml new file mode 100644 index 000000000000..ed0ea938f7f8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/humidity/sciosense,ens210.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/humidity/sciosense,ens210.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ScioSense ENS210 temperature and humidity sensor + +maintainers: + - Joshua Felmeden <jfelmeden@thegoodpenguin.co.uk> + +description: | + Temperature and Humidity sensor. + + Datasheet: + https://www.sciosense.com/wp-content/uploads/2024/04/ENS21x-Datasheet.pdf + https://www.sciosense.com/wp-content/uploads/2023/12/ENS210-Datasheet.pdf + +properties: + compatible: + oneOf: + - items: + - enum: + - sciosense,ens210a + - sciosense,ens211 + - sciosense,ens212 + - sciosense,ens213a + - sciosense,ens215 + - const: sciosense,ens210 + - const: sciosense,ens210 + + reg: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@43 { + compatible = "sciosense,ens210"; + reg = <0x43>; + }; + }; +... + diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml index 7de1b0e721ca..877e955d4ebd 100644 --- a/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml @@ -14,7 +14,9 @@ description: properties: compatible: - const: liteon,ltrf216a + enum: + - liteon,ltr308 + - liteon,ltrf216a reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bh1745.yaml b/Documentation/devicetree/bindings/iio/light/rohm,bh1745.yaml new file mode 100644 index 000000000000..44896795c67e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/rohm,bh1745.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/rohm,bh1745.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BH1745 colour sensor + +maintainers: + - Mudit Sharma <muditsharma.info@gmail.com> + +description: + BH1745 is an I2C colour sensor with red, green, blue and clear + channels. It has a programmable active low interrupt pin. + Interrupt occurs when the signal from the selected interrupt + source channel crosses set interrupt threshold high/low level. + +properties: + compatible: + const: rohm,bh1745 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + colour-sensor@38 { + compatible = "rohm,bh1745"; + reg = <0x38>; + interrupt-parent = <&gpio>; + interrupts = <19 IRQ_TYPE_LEVEL_LOW>; + vdd-supply = <&vdd>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml b/Documentation/devicetree/bindings/iio/light/rohm,bu27034anuc.yaml index 30a109a1bf3b..29c90ca5b258 100644 --- a/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml +++ b/Documentation/devicetree/bindings/iio/light/rohm,bu27034anuc.yaml @@ -1,23 +1,22 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/iio/light/rohm,bu27034.yaml# +$id: http://devicetree.org/schemas/iio/light/rohm,bu27034anuc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ROHM BU27034 ambient light sensor +title: ROHM BU27034ANUC ambient light sensor maintainers: - Matti Vaittinen <mazziesaccount@gmail.com> description: | - ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes + ROHM BU27034ANUC is an ambient light sensor with 2 channels and 2 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. - https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf properties: compatible: - const: rohm,bu27034 + const: rohm,bu27034anuc reg: maxItems: 1 @@ -37,7 +36,7 @@ examples: #size-cells = <0>; light-sensor@38 { - compatible = "rohm,bu27034"; + compatible = "rohm,bu27034anuc"; reg = <0x38>; vdd-supply = <&vdd>; }; diff --git a/Documentation/devicetree/bindings/iio/light/stk33xx.yaml b/Documentation/devicetree/bindings/iio/light/stk33xx.yaml index f6e22dc9814a..e4341fdced98 100644 --- a/Documentation/devicetree/bindings/iio/light/stk33xx.yaml +++ b/Documentation/devicetree/bindings/iio/light/stk33xx.yaml @@ -18,10 +18,15 @@ allOf: properties: compatible: - enum: - - sensortek,stk3310 - - sensortek,stk3311 - - sensortek,stk3335 + oneOf: + - enum: + - sensortek,stk3310 + - sensortek,stk3311 + - sensortek,stk3335 + - items: + - enum: + - sensortek,stk3013 + - const: sensortek,stk3310 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index 9790f75fc669..e8ca9a234027 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -18,12 +18,15 @@ properties: - asahi-kasei,ak09911 - asahi-kasei,ak09912 - asahi-kasei,ak09916 + - items: + # ak09918 is register compatible with ak09912. + - const: asahi-kasei,ak09918 + - const: asahi-kasei,ak09912 - enum: - ak8975 - ak8963 - ak09911 - ak09912 - - ak09916 deprecated: true reg: diff --git a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml index 2867ab6bf9b0..a3838ab0c524 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml @@ -36,6 +36,9 @@ properties: interrupts: maxItems: 1 + mount-matrix: + description: an optional 3x3 mounting rotation matrix. + additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/iio/pressure/sensirion,sdp500.yaml b/Documentation/devicetree/bindings/iio/pressure/sensirion,sdp500.yaml new file mode 100644 index 000000000000..813239f6879a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/pressure/sensirion,sdp500.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/pressure/sensirion,sdp500.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: sdp500/sdp510 pressure sensor with I2C bus interface + +maintainers: + - Petar Stoykov <petar.stoykov@prodrive-technologies.com> + +description: | + Pressure sensor from Sensirion with I2C bus interface. + There is no software difference between sdp500 and sdp510. + +properties: + compatible: + oneOf: + - items: + - const: sensirion,sdp510 + - const: sensirion,sdp500 + - const: sensirion,sdp500 + + reg: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + pressure@40 { + compatible = "sensirion,sdp500"; + reg = <0x40>; + vdd-supply = <&foo>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/proximity/awinic,aw96103.yaml b/Documentation/devicetree/bindings/iio/proximity/awinic,aw96103.yaml new file mode 100644 index 000000000000..7a83ceced11c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/awinic,aw96103.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/awinic,aw96103.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic's AW96103 capacitive proximity sensor and similar + +maintainers: + - Wang Shuaijie <wangshuaijie@awinic.com> + +description: | + Awinic's AW96103/AW96105 proximity sensor. + The specific absorption rate (SAR) is a metric that measures + the degree of absorption of electromagnetic radiation emitted by + wireless devices, such as mobile phones and tablets, by human tissue. + In mobile phone applications, the proximity sensor is primarily + used to detect the proximity of the human body to the phone. When the + phone approaches the human body, it will actively reduce the transmit + power of the antenna to keep the SAR within a safe range. Therefore, + we also refer to the proximity sensor as a SAR sensor. + +properties: + compatible: + enum: + - awinic,aw96103 + - awinic,aw96105 + + reg: + maxItems: 1 + + interrupts: + description: + Generated by the device to announce that a close/far + proximity event has happened. + maxItems: 1 + + vcc-supply: true + +required: + - compatible + - reg + - interrupts + - vcc-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + proximity@12 { + compatible = "awinic,aw96103"; + reg = <0x12>; + interrupt-parent = <&gpio>; + interrupts = <23 IRQ_TYPE_EDGE_FALLING>; + vcc-supply = <&pp1800_prox>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml new file mode 100644 index 000000000000..64ce8bc8bd36 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/tyhx,hx9023s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TYHX HX9023S capacitive proximity sensor + +maintainers: + - Yasin Lee <yasin.lee.x@gmail.com> + +description: | + TYHX HX9023S proximity sensor. Datasheet can be found here: + http://www.tianyihexin.com/ueditor/php/upload/file/20240614/1718336303992081.pdf + +properties: + compatible: + const: tyhx,hx9023s + + reg: + maxItems: 1 + + interrupts: + description: + Generated by device to announce preceding read request has finished + and data is available or that a close/far proximity event has happened. + maxItems: 1 + + vdd-supply: true + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^channel@[0-4]$": + $ref: /schemas/iio/adc/adc.yaml + type: object + unevaluatedProperties: false + + properties: + reg: + minimum: 0 + maximum: 4 + description: The channel number. + +required: + - compatible + - reg + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + proximity@2a { + compatible = "tyhx,hx9023s"; + reg = <0x2a>; + interrupt-parent = <&pio>; + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pp1800_prox>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + single-channel = <0>; + }; + channel@1 { + reg = <1>; + single-channel = <1>; + }; + channel@2 { + reg = <2>; + single-channel = <2>; + }; + channel@3 { + reg = <3>; + diff-channels = <1 0>; + }; + channel@4 { + reg = <4>; + diff-channels = <2 0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8939.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8939.yaml index fd15ab5014fb..4b08be72bbd7 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8939.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8939.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/interconnect/qcom,msm8939.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm MSM8939 Network-On-Chip interconnect +title: Qualcomm MSM8937/MSM8939/MSM8976 Network-On-Chip interconnect maintainers: - Konrad Dybcio <konradybcio@kernel.org> -description: | - The Qualcomm MSM8939 interconnect providers support adjusting the - bandwidth requirements between the various NoC fabrics. +description: + The Qualcomm MSM8937/MSM8939/MSM8976 interconnect providers support + adjusting the bandwidth requirements between the various NoC fabrics. allOf: - $ref: qcom,rpm-common.yaml# @@ -19,9 +19,15 @@ allOf: properties: compatible: enum: + - qcom,msm8937-bimc + - qcom,msm8937-pcnoc + - qcom,msm8937-snoc - qcom,msm8939-bimc - qcom,msm8939-pcnoc - qcom,msm8939-snoc + - qcom,msm8976-bimc + - qcom,msm8976-pcnoc + - qcom,msm8976-snoc reg: maxItems: 1 @@ -39,7 +45,10 @@ patternProperties: properties: compatible: - const: qcom,msm8939-snoc-mm + enum: + - qcom,msm8937-snoc-mm + - qcom,msm8939-snoc-mm + - qcom,msm8976-snoc-mm required: - compatible @@ -60,12 +69,6 @@ examples: compatible = "qcom,msm8939-snoc"; reg = <0x00580000 0x14000>; #interconnect-cells = <1>; - }; - - bimc: interconnect@400000 { - compatible = "qcom,msm8939-bimc"; - reg = <0x00400000 0x62000>; - #interconnect-cells = <1>; snoc_mm: interconnect-snoc { compatible = "qcom,msm8939-snoc-mm"; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8953.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8953.yaml index 732e9fa001a4..343ff62d7b65 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8953.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8953.yaml @@ -13,8 +13,7 @@ description: | The Qualcomm MSM8953 interconnect providers support adjusting the bandwidth requirements between the various NoC fabrics. - See also: - - dt-bindings/interconnect/qcom,msm8953.h + See also: include/dt-bindings/interconnect/qcom,msm8953.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 2cd1f5590fd9..189f5900ee50 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: - qcom,qcm2290-cpu-bwmon + - qcom,sa8775p-cpu-bwmon - qcom,sc7180-cpu-bwmon - qcom,sc7280-cpu-bwmon - qcom,sc8280xp-cpu-bwmon @@ -39,6 +40,7 @@ properties: - const: qcom,sdm845-bwmon # BWMON v4, unified register space - items: - enum: + - qcom,sa8775p-llcc-bwmon - qcom,sc7180-llcc-bwmon - qcom,sc8280xp-llcc-bwmon - qcom,sm6350-cpu-bwmon diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml index 9318b845ec35..1b9164dc162f 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml @@ -71,7 +71,7 @@ properties: - qcom,sdx65-system-noc - qcom,sm8150-aggre1-noc - qcom,sm8150-aggre2-noc - - qcom,sm8150-camnoc-noc + - qcom,sm8150-camnoc-virt - qcom,sm8150-compute-noc - qcom,sm8150-config-noc - qcom,sm8150-dc-noc @@ -113,6 +113,9 @@ allOf: properties: compatible: enum: + - qcom,sc8180x-camnoc-virt + - qcom,sc8180x-mc-virt + - qcom,sc8180x-qup-virt - qcom,sdx65-mc-virt - qcom,sm8250-qup-virt then: diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index c27a8f33d8d7..0840a3d92513 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -26,6 +26,7 @@ properties: - mdsp - sdsp - cdsp + - cdsp1 memory-region: maxItems: 1 @@ -81,7 +82,7 @@ patternProperties: iommus: minItems: 1 - maxItems: 3 + maxItems: 10 qcom,nsessions: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/net/ti,cc1352p7.yaml b/Documentation/devicetree/bindings/net/ti,cc1352p7.yaml index 3dde10de4630..4f4253441547 100644 --- a/Documentation/devicetree/bindings/net/ti,cc1352p7.yaml +++ b/Documentation/devicetree/bindings/net/ti,cc1352p7.yaml @@ -29,6 +29,12 @@ properties: reset-gpios: maxItems: 1 + bootloader-backdoor-gpios: + maxItems: 1 + description: | + gpios to enable bootloader backdoor in cc1352p7 bootloader to allow + flashing new firmware. + vdds-supply: true required: @@ -46,6 +52,7 @@ examples: clocks = <&sclk_hf 0>, <&sclk_lf 25>; clock-names = "sclk_hf", "sclk_lf"; reset-gpios = <&pio 35 GPIO_ACTIVE_LOW>; + bootloader-backdoor-gpios = <&pio 36 GPIO_ACTIVE_LOW>; vdds-supply = <&vdds>; }; }; diff --git a/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml b/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml index 70fb2ad25103..1b20b49eee79 100644 --- a/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml +++ b/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml @@ -15,6 +15,7 @@ description: | allOf: - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml index e21c06e9a741..b2cb76cf9053 100644 --- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml +++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml @@ -14,7 +14,7 @@ maintainers: description: | This binding represents the on-chip eFuse OTP controller found on i.MX6Q/D, i.MX6DL/S, i.MX6SL, i.MX6SX, i.MX6UL, i.MX6ULL/ULZ, i.MX6SLL, - i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM, i.MX8MN i.MX8MP and i.MX93 SoCs. + i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM, i.MX8MN i.MX8MP and i.MX93/5 SoCs. allOf: - $ref: nvmem.yaml# @@ -36,6 +36,7 @@ properties: - fsl,imx8mq-ocotp - fsl,imx8mm-ocotp - fsl,imx93-ocotp + - fsl,imx95-ocotp - const: syscon - items: - enum: diff --git a/Documentation/devicetree/bindings/nvmem/layouts/nvmem-layout.yaml b/Documentation/devicetree/bindings/nvmem/layouts/nvmem-layout.yaml index 3b40f7880774..382507060651 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/nvmem-layout.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/nvmem-layout.yaml @@ -21,6 +21,7 @@ oneOf: - $ref: fixed-layout.yaml - $ref: kontron,sl28-vpd.yaml - $ref: onie,tlv-layout.yaml + - $ref: u-boot,env.yaml properties: compatible: true diff --git a/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml b/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml index 9c36afc7084b..56a8f55d4a09 100644 --- a/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/nvmem/u-boot,env.yaml# +$id: http://devicetree.org/schemas/nvmem/layouts/u-boot,env.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: U-Boot environment variables +title: U-Boot environment variables layout description: | U-Boot uses environment variables to store device parameters and @@ -21,9 +21,6 @@ description: | This binding allows marking storage device (as containing env data) and specifying used format. - Right now only flash partition case is covered but it may be extended to e.g. - UBI volumes in the future. - Variables can be defined as NVMEM device subnodes. maintainers: @@ -42,6 +39,7 @@ properties: const: brcm,env reg: + description: Partition offset and size for env on top of MTD maxItems: 1 bootcmd: @@ -58,6 +56,17 @@ properties: description: The first argument is a MAC address offset. const: 1 +allOf: + - if: + properties: + $nodename: + not: + contains: + pattern: "^partition@[0-9a-f]+$" + then: + properties: + reg: false + additionalProperties: false examples: @@ -101,3 +110,23 @@ examples: }; }; }; + - | + partition@0 { + reg = <0x0 0x100000>; + label = "ubi"; + compatible = "linux,ubi"; + + volumes { + ubi-volume-u-boot-env { + volname = "env"; + + nvmem-layout { + compatible = "u-boot,env"; + + ethaddr { + #nvmem-cell-cells = <1>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml index 92bfe25f0571..3b2aa605a551 100644 --- a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml +++ b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml @@ -17,6 +17,7 @@ maintainers: allOf: - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml# properties: compatible: @@ -32,6 +33,8 @@ properties: patternProperties: "^.*@[0-9a-f]+$": type: object + $ref: layouts/fixed-cell.yaml + unevaluatedProperties: false properties: st,non-secure-otp: diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index adb15f4c8f6c..b320a39de7fe 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1541,6 +1541,8 @@ patternProperties: description: Turing Machines, Inc. "^tyan,.*": description: Tyan Computer Corporation + "^tyhx,.*": + description: NanjingTianyihexin Electronics Ltd. "^u-blox,.*": description: u-blox "^u-boot,.*": diff --git a/Documentation/iio/ad4000.rst b/Documentation/iio/ad4000.rst new file mode 100644 index 000000000000..de8fd3ae6e62 --- /dev/null +++ b/Documentation/iio/ad4000.rst @@ -0,0 +1,131 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD4000 driver +============= + +Device driver for Analog Devices Inc. AD4000 series of ADCs. + +Supported devices +================= + +* `AD4000 <https://www.analog.com/AD4000>`_ +* `AD4001 <https://www.analog.com/AD4001>`_ +* `AD4002 <https://www.analog.com/AD4002>`_ +* `AD4003 <https://www.analog.com/AD4003>`_ +* `AD4004 <https://www.analog.com/AD4004>`_ +* `AD4005 <https://www.analog.com/AD4005>`_ +* `AD4006 <https://www.analog.com/AD4006>`_ +* `AD4007 <https://www.analog.com/AD4007>`_ +* `AD4008 <https://www.analog.com/AD4008>`_ +* `AD4010 <https://www.analog.com/AD4010>`_ +* `AD4011 <https://www.analog.com/AD4011>`_ +* `AD4020 <https://www.analog.com/AD4020>`_ +* `AD4021 <https://www.analog.com/AD4021>`_ +* `AD4022 <https://www.analog.com/AD4022>`_ +* `ADAQ4001 <https://www.analog.com/ADAQ4001>`_ +* `ADAQ4003 <https://www.analog.com/ADAQ4003>`_ + +Wiring connections +------------------ + +Devices of the AD4000 series can be connected to the SPI host controller in a +few different modes. + +CS mode, 3-wire turbo mode +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Datasheet "3-wire" mode is what most resembles standard SPI connection which, +for these devices, comprises of connecting the controller CS line to device CNV +pin and other SPI lines as usual. This configuration is (misleadingly) called +"CS Mode, 3-Wire Turbo Mode" connection in datasheets. +NOTE: The datasheet definition of 3-wire mode for the AD4000 series is NOT the +same of standard spi-3wire mode. +This is the only connection mode that allows configuration register access but +it requires the SPI controller to support the ``SPI_MOSI_IDLE_HIGH`` feature. + +Omit the ``adi,sdi-pin`` property in device tree to select this mode. + +:: + + +-------------+ + + ----------------------------------| SDO | + | | | + | +-------------------| CS | + | v | | + | +--------------------+ | HOST | + | | CNV | | | + +--->| SDI AD4000 SDO |-------->| SDI | + | SCK | | | + +--------------------+ | | + ^ | | + +--------------------| SCLK | + +-------------+ + +CS mode, 3-wire, without busy indicator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Another wiring configuration supported as "3-wire" mode has the SDI pin +hard-wired to digital input/output interface supply (VIO). In this setup, the +controller is not required to support ``SPI_MOSI_IDLE_HIGH`` but register access +is not possible. This connection mode saves one wire and works with any SPI +controller. + +Set the ``adi,sdi-pin`` device tree property to ``"high"`` to select this mode. + +:: + + +-------------+ + +--------------------| CS | + v | | + VIO +--------------------+ | HOST | + | | CNV | | | + +--->| SDI AD4000 SDO |-------->| SDI | + | SCK | | | + +--------------------+ | | + ^ | | + +--------------------| SCLK | + +-------------+ + +Alternatively, a GPIO may be connected to the device CNV pin. This is similar to +the previous wiring configuration but saves the use of a CS line. + +:: + + +-------------+ + +--------------------| GPIO | + v | | + VIO +--------------------+ | HOST | + | | CNV | | | + +--->| SDI AD4000 SDO |-------->| SDI | + | SCK | | | + +--------------------+ | | + ^ | | + +--------------------| SCLK | + +-------------+ + +CS mode, 4-wire without busy indicator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In datasheet "4-wire" mode, the controller CS line is connected to the ADC SDI +pin and a GPIO is connected to the ADC CNV pin. This connection mode may better +suit scenarios where multiple ADCs can share one CNV trigger. + +Set ``adi,sdi-pin`` to ``"cs"`` to select this mode. + + +:: + + +-------------+ + + ----------------------------------| CS | + | | | + | +-------------------| GPIO | + | v | | + | +--------------------+ | HOST | + | | CNV | | | + +--->| SDI AD4000 SDO |-------->| SDI | + | SCK | | | + +--------------------+ | | + ^ | | + +--------------------| SCLK | + +-------------+ diff --git a/Documentation/iio/ad4695.rst b/Documentation/iio/ad4695.rst new file mode 100644 index 000000000000..33ed29b7c98a --- /dev/null +++ b/Documentation/iio/ad4695.rst @@ -0,0 +1,167 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD4695 driver +============= + +ADC driver for Analog Devices Inc. AD4695 and similar devices. The module name +is ``ad4695``. + + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD4695 <https://www.analog.com/AD4695>`_ +* `AD4696 <https://www.analog.com/AD4696>`_ +* `AD4697 <https://www.analog.com/AD4697>`_ +* `AD4698 <https://www.analog.com/AD4698>`_ + + +Supported features +================== + +SPI wiring modes +---------------- + +The driver currently supports the following SPI wiring configuration: + +4-wire mode +^^^^^^^^^^^ + +In this mode, CNV and CS are tied together and there is a single SDO line. + +.. code-block:: + + +-------------+ +-------------+ + | CS |<-+------| CS | + | CNV |<-+ | | + | ADC | | HOST | + | | | | + | SDI |<--------| SDO | + | SDO |-------->| SDI | + | SCLK |<--------| SCLK | + +-------------+ +-------------+ + +To use this mode, in the device tree, omit the ``cnv-gpios`` and +``spi-rx-bus-width`` properties. + +Channel configuration +--------------------- + +Since the chip supports multiple ways to configure each channel, this must be +described in the device tree based on what is actually wired up to the inputs. + +There are three typical configurations: + +An ``INx`` pin is used as the positive input with the ``REFGND``, ``COM`` or +the next ``INx`` pin as the negative input. + +Pairing with REFGND +^^^^^^^^^^^^^^^^^^^ + +Each ``INx`` pin can be used as a pseudo-differential input in conjunction with +the ``REFGND`` pin. The device tree will look like this: + +.. code-block:: + + channel@0 { + reg = <0>; /* IN0 */ + }; + +If no other channel properties are needed (e.g. ``adi,no-high-z``), the channel +node can be omitted entirely. + +This will appear on the IIO bus as the ``voltage0`` channel. The processed value +(*raw × scale*) will be the voltage present on the ``IN0`` pin relative to +``REFGND``. (Offset is always 0 when pairing with ``REFGND``.) + +Pairing with COM +^^^^^^^^^^^^^^^^ + +Each ``INx`` pin can be used as a pseudo-differential input in conjunction with +the ``COM`` pin. The device tree will look like this: + +.. code-block:: + + com-supply = <&vref_div_2>; + + channel@1 { + reg = <1>; /* IN1 */ + common-mode-channel = <AD4695_COMMON_MODE_COM>; + bipolar; + }; + +This will appear on the IIO bus as the ``voltage1`` channel. The processed value +(*(raw + offset) × scale*) will be the voltage measured on the ``IN1`` pin +relative to ``REFGND``. (The offset is determined by the ``com-supply`` voltage.) + +The macro comes from: + +.. code-block:: + + #include <dt-bindings/iio/adi,ad4695.h> + +Pairing two INx pins +^^^^^^^^^^^^^^^^^^^^ + +An even-numbered ``INx`` pin and the following odd-numbered ``INx`` pin can be +used as a pseudo-differential input. The device tree for using ``IN2`` as the +positive input and ``IN3`` as the negative input will look like this: + +.. code-block:: + + in3-supply = <&vref_div_2>; + + channel@2 { + reg = <2>; /* IN2 */ + common-mode-channel = <3>; /* IN3 */ + bipolar; + }; + +This will appear on the IIO bus as the ``voltage2`` channel. The processed value +(*(raw + offset) × scale*) will be the voltage measured on the ``IN1`` pin +relative to ``REFGND``. (Offset is determined by the ``in3-supply`` voltage.) + +VCC supply +---------- + +The chip supports being powered by an external LDO via the ``VCC`` input or an +internal LDO via the ``LDO_IN`` input. The driver looks at the device tree to +determine which is being used. If ``ldo-supply`` is present, then the internal +LDO is used. If ``vcc-supply`` is present, then the external LDO is used and +the internal LDO is disabled. + +Reference voltage +----------------- + +The chip supports an external reference voltage via the ``REF`` input or an +internal buffered reference voltage via the ``REFIN`` input. The driver looks +at the device tree to determine which is being used. If ``ref-supply`` is +present, then the external reference voltage is used and the internal buffer is +disabled. If ``refin-supply`` is present, then the internal buffered reference +voltage is used. + +Gain/offset calibration +----------------------- + +System calibration is supported using the channel gain and offset registers via +the ``calibscale`` and ``calibbias`` attributes respectively. + +Unimplemented features +---------------------- + +- Additional wiring modes +- Threshold events +- Oversampling +- GPIO support +- CRC support + +Device buffers +============== + +This driver supports hardware triggered buffers. This uses the "advanced +sequencer" feature of the chip to trigger a burst of conversions. + +Also see :doc:`iio_devbuf` for more general information. diff --git a/Documentation/iio/ad7380.rst b/Documentation/iio/ad7380.rst new file mode 100644 index 000000000000..9c784c1e652e --- /dev/null +++ b/Documentation/iio/ad7380.rst @@ -0,0 +1,130 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD7380 driver +============= + +ADC driver for Analog Devices Inc. AD7380 and similar devices. The module name +is ``ad7380``. + + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD7380 <https://www.analog.com/en/products/ad7380.html>`_ +* `AD7381 <https://www.analog.com/en/products/ad7381.html>`_ +* `AD7383 <https://www.analog.com/en/products/ad7383.html>`_ +* `AD7384 <https://www.analog.com/en/products/ad7384.html>`_ +* `AD7386 <https://www.analog.com/en/products/ad7386.html>`_ +* `AD7387 <https://www.analog.com/en/products/ad7387.html>`_ +* `AD7388 <https://www.analog.com/en/products/ad7388.html>`_ +* `AD7380-4 <https://www.analog.com/en/products/ad7380-4.html>`_ +* `AD7381-4 <https://www.analog.com/en/products/ad7381-4.html>`_ +* `AD7383-4 <https://www.analog.com/en/products/ad7383-4.html>`_ +* `AD7384-4 <https://www.analog.com/en/products/ad7384-4.html>`_ +* `AD7386-4 <https://www.analog.com/en/products/ad7386-4.html>`_ +* `AD7387-4 <https://www.analog.com/en/products/ad7387-4.html>`_ +* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_ + + +Supported features +================== + +SPI wiring modes +---------------- + +ad738x ADCs can output data on several SDO lines (1/2/4). The driver currently +supports only 1 SDO line. + +Reference voltage +----------------- + +2 possible reference voltage sources are supported: + +- Internal reference (2.5V) +- External reference (2.5V to 3.3V) + +The source is determined by the device tree. If ``refio-supply`` is present, +then the external reference is used, else the internal reference is used. + +Oversampling and resolution boost +--------------------------------- + +This family supports 2 types of oversampling: normal average and rolling +average. Only normal average is supported by the driver, as rolling average can +be achieved by processing a captured data buffer. The following ratios are +available: 1 (oversampling disabled)/2/4/8/16/32. + +When the on-chip oversampling function is enabled the performance of the ADC can +exceed the default resolution. To accommodate the performance boost achievable, +it is possible to enable an additional two bits of resolution. Because the +resolution boost feature can only be enabled when oversampling is enabled and +oversampling is not as useful without the resolution boost, the driver +automatically enables the resolution boost if and only if oversampling is +enabled. + +Since the resolution boost feature causes 16-bit chips to now have 18-bit data +which means the storagebits has to change from 16 to 32 bits, we use the new +ext_scan_type feature to allow changing the scan_type at runtime. Unfortunately +libiio does not support it. So when enabling or disabling oversampling, user +must restart iiod using the following command: + +.. code-block:: bash + + root:~# systemctl restart iiod + +Channel selection and sequencer (single-end chips only) +------------------------------------------------------- + +Single-ended chips of this family (ad7386/7/8(-4)) have a 2:1 multiplexer in +front of each ADC. They also include additional configuration registers that +allow for either manual selection or automatic switching (sequencer mode), of +the multiplexer inputs. + +From an IIO point of view, all inputs are exported, i.e ad7386/7/8 +export 4 channels and ad7386-4/7-4/8-4 export 8 channels. + +Inputs ``AinX0`` of multiplexers correspond to the first half of IIO channels (i.e +0-1 or 0-3) and inputs ``AinX1`` correspond to second half (i.e 2-3 or 4-7). +Example for AD7386/7/8 (2 channels parts): + +.. code-block:: + + IIO | AD7386/7/8 + | +---------------------------- + | | _____ ______ + | | | | | | + voltage0 | AinA0 --|--->| | | | + | | | mux |----->| ADCA |--- + voltage2 | AinA1 --|--->| | | | + | | |_____| |_____ | + | | _____ ______ + | | | | | | + voltage1 | AinB0 --|--->| | | | + | | | mux |----->| ADCB |--- + voltage3 | AinB1 --|--->| | | | + | | |_____| |______| + | | + | +---------------------------- + + +When enabling sequencer mode, the effective sampling rate is divided by two. + +Unimplemented features +---------------------- + +- 2/4 SDO lines +- Rolling average oversampling +- Power down mode +- CRC indication +- Alert + + +Device buffers +============== + +This driver supports IIO triggered buffers. + +See :doc:`iio_devbuf` for more information. diff --git a/Documentation/iio/adxl380.rst b/Documentation/iio/adxl380.rst new file mode 100644 index 000000000000..376dee5fe1dd --- /dev/null +++ b/Documentation/iio/adxl380.rst @@ -0,0 +1,233 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +ADXL380 driver +=============== + +This driver supports Analog Device's ADXL380/382 on SPI/I2C bus. + +1. Supported devices +==================== + +* `ADXL380 <https://www.analog.com/ADXL380>`_ +* `ADXL382 <https://www.analog.com/ADXL382>`_ + +The ADXL380/ADXL382 is a low noise density, low power, 3-axis accelerometer with +selectable measurement ranges. The ADXL380 supports the ±4 g, ±8 g, and ±16 g +ranges, and the ADXL382 supports ±15 g, ±30 g, and ±60 g ranges. + +2. Device attributes +==================== + +Accelerometer measurements are always provided. + +Temperature data are also provided. This data can be used to monitor the +internal system temperature or to improve the temperature stability of the +device via calibration. + +Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, +where X is the IIO index of the device. Under these folders reside a set of +device files, depending on the characteristics and features of the hardware +device in questions. These files are consistently generalized and documented in +the IIO ABI documentation. + +The following tables show the adxl380 related device files, found in the +specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. + ++---------------------------------------------------+----------------------------------------------------------+ +| 3-Axis Accelerometer related device files | Description | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_scale | Scale for the accelerometer channels. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_filter_high_pass_3db_frequency | Low pass filter bandwidth. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_filter_high_pass_3db_frequency_available | Available low pass filter bandwidth configurations. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_filter_low_pass_3db_frequency | High pass filter bandwidth. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_filter_low_pass_3db_frequency_available | Available high pass filter bandwidth configurations. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_raw | Raw X-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_calibbias | y-axis acceleration offset correction | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_raw | Raw Y-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_raw | Raw Z-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ + ++----------------------------------+--------------------------------------------+ +| Temperature sensor related files | Description | ++----------------------------------+--------------------------------------------+ +| in_temp_raw | Raw temperature channel value. | ++----------------------------------+--------------------------------------------+ +| in_temp_offset | Offset for the temperature sensor channel. | ++----------------------------------+--------------------------------------------+ +| in_temp_scale | Scale for the temperature sensor channel. | ++----------------------------------+--------------------------------------------+ + ++------------------------------+----------------------------------------------+ +| Miscellaneous device files | Description | ++------------------------------+----------------------------------------------+ +| name | Name of the IIO device. | ++------------------------------+----------------------------------------------+ +| sampling_frequency | Currently selected sample rate. | ++------------------------------+----------------------------------------------+ +| sampling_frequency_available | Available sampling frequency configurations. | ++------------------------------+----------------------------------------------+ + +Channels processed values +------------------------- + +A channel value can be read from its _raw attribute. The value returned is the +raw value as reported by the devices. To get the processed value of the channel, +apply the following formula: + +.. code-block:: bash + + processed value = (_raw + _offset) * _scale + +Where _offset and _scale are device attributes. If no _offset attribute is +present, simply assume its value is 0. + +The adis16475 driver offers data for 2 types of channels, the table below shows +the measurement units for the processed value, which are defined by the IIO +framework: + ++-------------------------------------+---------------------------+ +| Channel type | Measurement unit | ++-------------------------------------+---------------------------+ +| Acceleration on X, Y, and Z axis | Meters per Second squared | ++-------------------------------------+---------------------------+ +| Temperature | Millidegrees Celsius | ++-------------------------------------+---------------------------+ + +Usage examples +-------------- + +Show device name: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat name + adxl382 + +Show accelerometer channels value: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw + -1771 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw + 282 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw + -1523 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale + 0.004903325 + +- X-axis acceleration = in_accel_x_raw * in_accel_scale = −8.683788575 m/s^2 +- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 1.38273765 m/s^2 +- Z-axis acceleration = in_accel_z_raw * in_accel_scale = -7.467763975 m/s^2 + +Set calibration offset for accelerometer channels: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 0 + + root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 50 + +Set sampling frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency + 16000 + root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency_available + 16000 32000 64000 + + root:/sys/bus/iio/devices/iio:device0> echo 32000 > sampling_frequency + root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency + 32000 + +Set low pass filter bandwidth for accelerometer channels: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency + 32000 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency_available + 32000 8000 4000 2000 + + root:/sys/bus/iio/devices/iio:device0> echo 2000 > in_accel_filter_low_pass_3db_frequency + root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency + 2000 + +3. Device buffers +================= + +This driver supports IIO buffers. + +All devices support retrieving the raw acceleration and temperature measurements +using buffers. + +Usage examples +-------------- + +Select channels for buffer read: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp_en + +Set the number of samples to be stored in the buffer: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length + +Enable buffer readings: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable + +Obtain buffered data: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0 + ... + 002bc300 f7 e7 00 a8 fb c5 24 80 f7 e7 01 04 fb d6 24 80 |......$.......$.| + 002bc310 f7 f9 00 ab fb dc 24 80 f7 c3 00 b8 fb e2 24 80 |......$.......$.| + 002bc320 f7 fb 00 bb fb d1 24 80 f7 b1 00 5f fb d1 24 80 |......$...._..$.| + 002bc330 f7 c4 00 c6 fb a6 24 80 f7 a6 00 68 fb f1 24 80 |......$....h..$.| + 002bc340 f7 b8 00 a3 fb e7 24 80 f7 9a 00 b1 fb af 24 80 |......$.......$.| + 002bc350 f7 b1 00 67 fb ee 24 80 f7 96 00 be fb 92 24 80 |...g..$.......$.| + 002bc360 f7 ab 00 7a fc 1b 24 80 f7 b6 00 ae fb 76 24 80 |...z..$......v$.| + 002bc370 f7 ce 00 a3 fc 02 24 80 f7 c0 00 be fb 8b 24 80 |......$.......$.| + 002bc380 f7 c3 00 93 fb d0 24 80 f7 ce 00 d8 fb c8 24 80 |......$.......$.| + 002bc390 f7 bd 00 c0 fb 82 24 80 f8 00 00 e8 fb db 24 80 |......$.......$.| + 002bc3a0 f7 d8 00 d3 fb b4 24 80 f8 0b 00 e5 fb c3 24 80 |......$.......$.| + 002bc3b0 f7 eb 00 c8 fb 92 24 80 f7 e7 00 ea fb cb 24 80 |......$.......$.| + 002bc3c0 f7 fd 00 cb fb 94 24 80 f7 e3 00 f2 fb b8 24 80 |......$.......$.| + ... + +See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered +data is structured. + +4. IIO Interfacing Tools +======================== + +See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO +interfacing tools. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 9cb4c50cb20d..dfcf9618568a 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -18,8 +18,12 @@ Industrial I/O Kernel Drivers .. toctree:: :maxdepth: 1 + ad4000 + ad4695 + ad7380 ad7944 adis16475 adis16480 + adxl380 bno055 ep93xx_adc diff --git a/MAINTAINERS b/MAINTAINERS index 9b899a1327a0..8157243a84d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -449,6 +449,7 @@ S: Supported W: https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad738x W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +F: Documentation/iio/ad7380.rst F: drivers/iio/adc/ad7380.c AD7877 TOUCHSCREEN DRIVER @@ -631,6 +632,17 @@ F: drivers/iio/accel/adxl372.c F: drivers/iio/accel/adxl372_i2c.c F: drivers/iio/accel/adxl372_spi.c +ADXL380 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Ramona Gradinariu <ramona.gradinariu@analog.com> +M: Antoniu Miclaus <antoniu.miclaus@analog.com> +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/accel/adi,adxl380.yaml +F: drivers/iio/accel/adxl380.c +F: drivers/iio/accel/adxl380.h +F: drivers/iio/accel/adxl380_i2c.c +F: drivers/iio/accel/adxl380_spi.c + AF8133J THREE-AXIS MAGNETOMETER DRIVER M: Ondřej Jirman <megi@xff.cz> S: Maintained @@ -1242,6 +1254,8 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml +F: Documentation/iio/ad4000.rst +F: drivers/iio/adc/ad4000.c ANALOG DEVICES INC AD4130 DRIVER M: Cosmin Tanislav <cosmin.tanislav@analog.com> @@ -1252,6 +1266,18 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c +ANALOG DEVICES INC AD4695 DRIVER +M: Michael Hennerich <michael.hennerich@analog.com> +M: Nuno Sá <nuno.sa@analog.com> +R: David Lechner <dlechner@baylibre.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml +F: Documentation/iio/ad4695.rst +F: drivers/iio/adc/ad4695.c +F: include/dt-bindings/iio/adi,ad4695.h + ANALOG DEVICES INC AD7091R DRIVER M: Marcelo Schmitt <marcelo.schmitt@analog.com> L: linux-iio@vger.kernel.org @@ -1318,6 +1344,16 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c +ANALOG DEVICES INC AD9467 DRIVER +M: Michael Hennerich <Michael.Hennerich@analog.com> +M: Nuno Sa <nuno.sa@analog.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/ABI/testing/debugfs-iio-ad9467 +F: Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml +F: drivers/iio/adc/ad9467.c + ANALOG DEVICES INC AD9739a DRIVER M: Nuno Sa <nuno.sa@analog.com> M: Dragos Bogdan <dragos.bogdan@analog.com> @@ -10977,6 +11013,7 @@ M: Nuno Sa <nuno.sa@analog.com> R: Olivier Moysan <olivier.moysan@foss.st.com> L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/debugfs-iio-backend F: drivers/iio/industrialio-backend.c F: include/linux/iio/backend.h @@ -13418,6 +13455,16 @@ S: Maintained F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml F: drivers/iio/dac/ltc1660.c +LTC2664 IIO DAC DRIVER +M: Michael Hennerich <michael.hennerich@analog.com> +M: Kim Seer Paller <kimseer.paller@analog.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ltc2664.yaml +F: Documentation/devicetree/bindings/iio/dac/adi,ltc2672.yaml +F: drivers/iio/dac/ltc2664.c + LTC2688 IIO DAC DRIVER M: Nuno Sá <nuno.sa@analog.com> L: linux-iio@vger.kernel.org @@ -15186,6 +15233,13 @@ F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml F: drivers/nvmem/microchip-otpc.c F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h +MICROCHIP PAC1921 POWER/CURRENT MONITOR DRIVER +M: Matteo Martelli <matteomartelli3@gmail.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/microchip,pac1921.yaml +F: drivers/iio/adc/pac1921.c + MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER M: Marius Cristea <marius.cristea@microchip.com> L: linux-iio@vger.kernel.org @@ -19990,6 +20044,12 @@ S: Supported F: drivers/power/supply/bd99954-charger.c F: drivers/power/supply/bd99954-charger.h +ROHM BH1745 COLOUR SENSOR +M: Mudit Sharma <muditsharma.info@gmail.com> +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/light/bh1745.c + ROHM BH1750 AMBIENT LIGHT SENSOR DRIVER M: Tomasz Duszynski <tduszyns@gmail.com> S: Maintained @@ -20843,6 +20903,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml F: drivers/iio/chemical/scd4x.c +SENSIRION SDP500 DIFFERENTIAL PRESSURE SENSOR DRIVER +M: Petar Stoykov <petar.stoykov@prodrive-technologies.com> +S: Maintained +F: Documentation/devicetree/bindings/iio/pressure/sensirion,sdp500.yaml +F: drivers/iio/pressure/sdp500.c + SENSIRION SGP40 GAS SENSOR DRIVER M: Andreas Klinger <ak@it-klinger.de> S: Maintained @@ -23605,7 +23671,8 @@ F: drivers/media/pci/tw686x/ U-BOOT ENVIRONMENT VARIABLES M: Rafał Miłecki <rafal@milecki.pl> S: Maintained -F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml +F: Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml +F: drivers/nvmem/layouts/u-boot-env.c F: drivers/nvmem/u-boot-env.c UACCE ACCELERATOR FRAMEWORK diff --git a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts index 70de288d728e..a1cd47d7f5e3 100644 --- a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts +++ b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts @@ -888,7 +888,8 @@ mcu { compatible = "ti,cc1352p7"; - reset-gpios = <&main_gpio0 72 GPIO_ACTIVE_LOW>; + bootloader-backdoor-gpios = <&main_gpio0 13 GPIO_ACTIVE_HIGH>; + reset-gpios = <&main_gpio0 14 GPIO_ACTIVE_HIGH>; vdds-supply = <&vdd_3v3>; }; }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e8643c69d426..978740537a1a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -277,7 +277,7 @@ _binder_proc_lock(struct binder_proc *proc, int line) } /** - * binder_proc_unlock() - Release spinlock for given binder_proc + * binder_proc_unlock() - Release outer lock for given binder_proc * @proc: struct binder_proc to acquire * * Release lock acquired via binder_proc_lock() @@ -1352,6 +1352,7 @@ static void binder_free_ref(struct binder_ref *ref) if (ref->node) binder_free_node(ref->node); kfree(ref->death); + kfree(ref->freeze); kfree(ref); } @@ -1546,7 +1547,7 @@ static void binder_thread_dec_tmpref(struct binder_thread *thread) * by threads that are being released. When done with the binder_proc, * this function is called to decrement the counter and free the * proc if appropriate (proc has been released, all threads have - * been released and not currenly in-use to process a transaction). + * been released and not currently in-use to process a transaction). */ static void binder_proc_dec_tmpref(struct binder_proc *proc) { @@ -3842,6 +3843,155 @@ err_invalid_target_handle: } } +static int +binder_request_freeze_notification(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_handle_cookie *handle_cookie) +{ + struct binder_ref_freeze *freeze; + struct binder_ref *ref; + bool is_frozen; + + freeze = kzalloc(sizeof(*freeze), GFP_KERNEL); + if (!freeze) + return -ENOMEM; + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, handle_cookie->handle, false); + if (!ref) { + binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n", + proc->pid, thread->pid, handle_cookie->handle); + binder_proc_unlock(proc); + kfree(freeze); + return -EINVAL; + } + + binder_node_lock(ref->node); + + if (ref->freeze || !ref->node->proc) { + binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n", + proc->pid, thread->pid, + ref->freeze ? "already set" : "dead node"); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + kfree(freeze); + return -EINVAL; + } + binder_inner_proc_lock(ref->node->proc); + is_frozen = ref->node->proc->is_frozen; + binder_inner_proc_unlock(ref->node->proc); + + binder_stats_created(BINDER_STAT_FREEZE); + INIT_LIST_HEAD(&freeze->work.entry); + freeze->cookie = handle_cookie->cookie; + freeze->work.type = BINDER_WORK_FROZEN_BINDER; + freeze->is_frozen = is_frozen; + + ref->freeze = freeze; + + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + binder_inner_proc_unlock(proc); + + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return 0; +} + +static int +binder_clear_freeze_notification(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_handle_cookie *handle_cookie) +{ + struct binder_ref_freeze *freeze; + struct binder_ref *ref; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, handle_cookie->handle, false); + if (!ref) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n", + proc->pid, thread->pid, handle_cookie->handle); + binder_proc_unlock(proc); + return -EINVAL; + } + + binder_node_lock(ref->node); + + if (!ref->freeze) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n", + proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return -EINVAL; + } + freeze = ref->freeze; + binder_inner_proc_lock(proc); + if (freeze->cookie != handle_cookie->cookie) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n", + proc->pid, thread->pid, (u64)freeze->cookie, + (u64)handle_cookie->cookie); + binder_inner_proc_unlock(proc); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return -EINVAL; + } + ref->freeze = NULL; + /* + * Take the existing freeze object and overwrite its work type. There are three cases here: + * 1. No pending notification. In this case just add the work to the queue. + * 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we + * should resend with the new work type. + * 3. A notification is pending to be sent. Since the work is already in the queue, nothing + * needs to be done here. + */ + freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION; + if (list_empty(&freeze->work.entry)) { + binder_enqueue_work_ilocked(&freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + } else if (freeze->sent) { + freeze->resend = true; + } + binder_inner_proc_unlock(proc); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return 0; +} + +static int +binder_freeze_notification_done(struct binder_proc *proc, + struct binder_thread *thread, + binder_uintptr_t cookie) +{ + struct binder_ref_freeze *freeze = NULL; + struct binder_work *w; + + binder_inner_proc_lock(proc); + list_for_each_entry(w, &proc->delivered_freeze, entry) { + struct binder_ref_freeze *tmp_freeze = + container_of(w, struct binder_ref_freeze, work); + + if (tmp_freeze->cookie == cookie) { + freeze = tmp_freeze; + break; + } + } + if (!freeze) { + binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n", + proc->pid, thread->pid, (u64)cookie); + binder_inner_proc_unlock(proc); + return -EINVAL; + } + binder_dequeue_work_ilocked(&freeze->work); + freeze->sent = false; + if (freeze->resend) { + freeze->resend = false; + binder_enqueue_work_ilocked(&freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + } + binder_inner_proc_unlock(proc); + return 0; +} + /** * binder_free_buf() - free the specified buffer * @proc: binder proc that owns buffer @@ -4325,6 +4475,44 @@ static int binder_thread_write(struct binder_proc *proc, binder_inner_proc_unlock(proc); } break; + case BC_REQUEST_FREEZE_NOTIFICATION: { + struct binder_handle_cookie handle_cookie; + int error; + + if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie))) + return -EFAULT; + ptr += sizeof(handle_cookie); + error = binder_request_freeze_notification(proc, thread, + &handle_cookie); + if (error) + return error; + } break; + + case BC_CLEAR_FREEZE_NOTIFICATION: { + struct binder_handle_cookie handle_cookie; + int error; + + if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie))) + return -EFAULT; + ptr += sizeof(handle_cookie); + error = binder_clear_freeze_notification(proc, thread, &handle_cookie); + if (error) + return error; + } break; + + case BC_FREEZE_NOTIFICATION_DONE: { + binder_uintptr_t cookie; + int error; + + if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + + ptr += sizeof(cookie); + error = binder_freeze_notification_done(proc, thread, cookie); + if (error) + return error; + } break; + default: pr_err("%d:%d unknown command %u\n", proc->pid, thread->pid, cmd); @@ -4714,6 +4902,46 @@ retry: if (cmd == BR_DEAD_BINDER) goto done; /* DEAD_BINDER notifications can cause transactions */ } break; + + case BINDER_WORK_FROZEN_BINDER: { + struct binder_ref_freeze *freeze; + struct binder_frozen_state_info info; + + memset(&info, 0, sizeof(info)); + freeze = container_of(w, struct binder_ref_freeze, work); + info.is_frozen = freeze->is_frozen; + info.cookie = freeze->cookie; + freeze->sent = true; + binder_enqueue_work_ilocked(w, &proc->delivered_freeze); + binder_inner_proc_unlock(proc); + + if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (copy_to_user(ptr, &info, sizeof(info))) + return -EFAULT; + ptr += sizeof(info); + binder_stat_br(proc, thread, BR_FROZEN_BINDER); + goto done; /* BR_FROZEN_BINDER notifications can cause transactions */ + } break; + + case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: { + struct binder_ref_freeze *freeze = + container_of(w, struct binder_ref_freeze, work); + binder_uintptr_t cookie = freeze->cookie; + + binder_inner_proc_unlock(proc); + kfree(freeze); + binder_stats_deleted(BINDER_STAT_FREEZE); + if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE); + } break; + default: binder_inner_proc_unlock(proc); pr_err("%d:%d: bad work type %d\n", @@ -5322,6 +5550,48 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc) return false; } +static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen) +{ + struct rb_node *n; + struct binder_ref *ref; + + binder_inner_proc_lock(proc); + for (n = rb_first(&proc->nodes); n; n = rb_next(n)) { + struct binder_node *node; + + node = rb_entry(n, struct binder_node, rb_node); + binder_inner_proc_unlock(proc); + binder_node_lock(node); + hlist_for_each_entry(ref, &node->refs, node_entry) { + /* + * Need the node lock to synchronize + * with new notification requests and the + * inner lock to synchronize with queued + * freeze notifications. + */ + binder_inner_proc_lock(ref->proc); + if (!ref->freeze) { + binder_inner_proc_unlock(ref->proc); + continue; + } + ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER; + if (list_empty(&ref->freeze->work.entry)) { + ref->freeze->is_frozen = is_frozen; + binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo); + binder_wakeup_proc_ilocked(ref->proc); + } else { + if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen) + ref->freeze->resend = true; + ref->freeze->is_frozen = is_frozen; + } + binder_inner_proc_unlock(ref->proc); + } + binder_node_unlock(node); + binder_inner_proc_lock(proc); + } + binder_inner_proc_unlock(proc); +} + static int binder_ioctl_freeze(struct binder_freeze_info *info, struct binder_proc *target_proc) { @@ -5333,6 +5603,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, target_proc->async_recv = false; target_proc->is_frozen = false; binder_inner_proc_unlock(target_proc); + binder_add_freeze_work(target_proc, false); return 0; } @@ -5365,6 +5636,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, binder_inner_proc_lock(target_proc); target_proc->is_frozen = false; binder_inner_proc_unlock(target_proc); + } else { + binder_add_freeze_work(target_proc, true); } return ret; @@ -5740,6 +6013,7 @@ static int binder_open(struct inode *nodp, struct file *filp) binder_stats_created(BINDER_STAT_PROC); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); + INIT_LIST_HEAD(&proc->delivered_freeze); INIT_LIST_HEAD(&proc->waiting_threads); filp->private_data = proc; @@ -6291,7 +6565,9 @@ static const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", - "BR_TRANSACTION_PENDING_FROZEN" + "BR_TRANSACTION_PENDING_FROZEN", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_command_strings[] = { @@ -6314,6 +6590,9 @@ static const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_objstat_strings[] = { @@ -6323,7 +6602,8 @@ static const char * const binder_objstat_strings[] = { "ref", "death", "transaction", - "transaction_complete" + "transaction_complete", + "freeze", }; static void print_binder_stats(struct seq_file *m, const char *prefix, diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 7d4fc53f7a73..f8d6be682f23 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -130,12 +130,13 @@ enum binder_stat_types { BINDER_STAT_DEATH, BINDER_STAT_TRANSACTION, BINDER_STAT_TRANSACTION_COMPLETE, + BINDER_STAT_FREEZE, BINDER_STAT_COUNT }; struct binder_stats { - atomic_t br[_IOC_NR(BR_TRANSACTION_PENDING_FROZEN) + 1]; - atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; + atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1]; + atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; }; @@ -160,6 +161,8 @@ struct binder_work { BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, + BINDER_WORK_FROZEN_BINDER, + BINDER_WORK_CLEAR_FREEZE_NOTIFICATION, } type; }; @@ -276,6 +279,14 @@ struct binder_ref_death { binder_uintptr_t cookie; }; +struct binder_ref_freeze { + struct binder_work work; + binder_uintptr_t cookie; + bool is_frozen:1; + bool sent:1; + bool resend:1; +}; + /** * struct binder_ref_data - binder_ref counts and id * @debug_id: unique ID for the ref @@ -308,6 +319,8 @@ struct binder_ref_data { * @node indicates the node must be freed * @death: pointer to death notification (ref_death) if requested * (protected by @node->lock) + * @freeze: pointer to freeze notification (ref_freeze) if requested + * (protected by @node->lock) * * Structure to track references from procA to target node (on procB). This * structure is unsafe to access without holding @proc->outer_lock. @@ -324,6 +337,7 @@ struct binder_ref { struct binder_proc *proc; struct binder_node *node; struct binder_ref_death *death; + struct binder_ref_freeze *freeze; }; /** @@ -377,6 +391,8 @@ struct binder_ref { * (atomics, no lock needed) * @delivered_death: list of delivered death notification * (protected by @inner_lock) + * @delivered_freeze: list of delivered freeze notification + * (protected by @inner_lock) * @max_threads: cap on number of binder threads * (protected by @inner_lock) * @requested_threads: number of binder threads requested but not @@ -424,6 +440,7 @@ struct binder_proc { struct list_head todo; struct binder_stats stats; struct list_head delivered_death; + struct list_head delivered_freeze; u32 max_threads; int requested_threads; int requested_threads_started; diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 3001d754ac36..ad1fa7abc323 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -58,6 +58,7 @@ enum binderfs_stats_mode { struct binder_features { bool oneway_spam_detection; bool extended_error; + bool freeze_notification; }; static const struct constant_table binderfs_param_stats[] = { @@ -74,6 +75,7 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = { static struct binder_features binder_features = { .oneway_spam_detection = true, .extended_error = true, + .freeze_notification = true, }; static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb) @@ -608,6 +610,12 @@ static int init_binder_features(struct super_block *sb) if (IS_ERR(dentry)) return PTR_ERR(dentry); + dentry = binderfs_create_file(dir, "freeze_notification", + &binder_features_fops, + &binder_features.freeze_notification); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + return 0; } diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 23b8cba4a2a3..7a7609298e18 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -202,29 +202,24 @@ static void cache_of_set_props(struct cacheinfo *this_leaf, static int cache_setup_of_node(unsigned int cpu) { - struct device_node *np, *prev; struct cacheinfo *this_leaf; unsigned int index = 0; - np = of_cpu_device_node_get(cpu); + struct device_node *np __free(device_node) = of_cpu_device_node_get(cpu); if (!np) { pr_err("Failed to find cpu%d device node\n", cpu); return -ENOENT; } if (!of_check_cache_nodes(np)) { - of_node_put(np); return -ENOENT; } - prev = np; - while (index < cache_leaves(cpu)) { this_leaf = per_cpu_cacheinfo_idx(cpu, index); if (this_leaf->level != 1) { + struct device_node *prev __free(device_node) = np; np = of_find_next_cache_node(np); - of_node_put(prev); - prev = np; if (!np) break; } @@ -233,8 +228,6 @@ static int cache_setup_of_node(unsigned int cpu) index++; } - of_node_put(np); - if (index != cache_leaves(cpu)) /* not all OF nodes populated */ return -ENOENT; @@ -243,17 +236,14 @@ static int cache_setup_of_node(unsigned int cpu) static bool of_check_cache_nodes(struct device_node *np) { - struct device_node *next; - if (of_property_present(np, "cache-size") || of_property_present(np, "i-cache-size") || of_property_present(np, "d-cache-size") || of_property_present(np, "cache-unified")) return true; - next = of_find_next_cache_node(np); + struct device_node *next __free(device_node) = of_find_next_cache_node(np); if (next) { - of_node_put(next); return true; } @@ -287,12 +277,10 @@ static int of_count_cache_leaves(struct device_node *np) int init_of_cache_level(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); - struct device_node *np = of_cpu_device_node_get(cpu); - struct device_node *prev = NULL; + struct device_node *np __free(device_node) = of_cpu_device_node_get(cpu); unsigned int levels = 0, leaves, level; if (!of_check_cache_nodes(np)) { - of_node_put(np); return -ENOENT; } @@ -300,30 +288,27 @@ int init_of_cache_level(unsigned int cpu) if (leaves > 0) levels = 1; - prev = np; - while ((np = of_find_next_cache_node(np))) { - of_node_put(prev); - prev = np; + while (1) { + struct device_node *prev __free(device_node) = np; + np = of_find_next_cache_node(np); + if (!np) + break; + if (!of_device_is_compatible(np, "cache")) - goto err_out; + return -EINVAL; if (of_property_read_u32(np, "cache-level", &level)) - goto err_out; + return -EINVAL; if (level <= levels) - goto err_out; + return -EINVAL; leaves += of_count_cache_leaves(np); levels = level; } - of_node_put(np); this_cpu_ci->num_levels = levels; this_cpu_ci->num_leaves = leaves; return 0; - -err_out: - of_node_put(np); - return -EINVAL; } #else diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index ce7d2e62c2f1..a9b1f8beee7b 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1464,7 +1464,7 @@ static int mhi_match(struct device *dev, const struct device_driver *drv) return 0; }; -struct bus_type mhi_bus_type = { +const struct bus_type mhi_bus_type = { .name = "mhi", .dev_name = "mhi", .match = mhi_match, diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index aaad40a07f69..d057e877932e 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -9,7 +9,7 @@ #include "../common.h" -extern struct bus_type mhi_bus_type; +extern const struct bus_type mhi_bus_type; /* Host request register */ #define MHI_SOC_RESET_REQ_OFFSET 0xb0 diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 14a11880bcea..9938bb034c1c 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -26,6 +26,7 @@ /* PCI VID definitions */ #define PCI_VENDOR_ID_THALES 0x1269 #define PCI_VENDOR_ID_QUECTEL 0x1eac +#define PCI_VENDOR_ID_NETPRISMA 0x203e #define MHI_EDL_DB 91 #define MHI_EDL_COOKIE 0xEDEDEDED @@ -433,8 +434,8 @@ static const struct mhi_controller_config modem_foxconn_sdx72_config = { static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = { .name = "foxconn-sdx55", - .fw = "qcom/sdx55m/sbl1.mbn", - .edl = "qcom/sdx55m/edl.mbn", + .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -444,8 +445,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = { static const struct mhi_pci_dev_info mhi_foxconn_t99w175_info = { .name = "foxconn-t99w175", - .fw = "qcom/sdx55m/sbl1.mbn", - .edl = "qcom/sdx55m/edl.mbn", + .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -455,8 +456,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_t99w175_info = { static const struct mhi_pci_dev_info mhi_foxconn_dw5930e_info = { .name = "foxconn-dw5930e", - .fw = "qcom/sdx55m/sbl1.mbn", - .edl = "qcom/sdx55m/edl.mbn", + .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -466,6 +467,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5930e_info = { static const struct mhi_pci_dev_info mhi_foxconn_t99w368_info = { .name = "foxconn-t99w368", + .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -475,6 +478,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_t99w368_info = { static const struct mhi_pci_dev_info mhi_foxconn_t99w373_info = { .name = "foxconn-t99w373", + .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -484,6 +489,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_t99w373_info = { static const struct mhi_pci_dev_info mhi_foxconn_t99w510_info = { .name = "foxconn-t99w510", + .edl = "qcom/sdx24m/foxconn/prog_firehose_sdx24.mbn", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -493,6 +500,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_t99w510_info = { static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = { .name = "foxconn-dw5932e", + .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf", + .edl_trigger = true, .config = &modem_foxconn_sdx55_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -502,7 +511,7 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = { static const struct mhi_pci_dev_info mhi_foxconn_t99w515_info = { .name = "foxconn-t99w515", - .edl = "fox/sdx72m/edl.mbn", + .edl = "qcom/sdx72m/foxconn/edl.mbn", .edl_trigger = true, .config = &modem_foxconn_sdx72_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, @@ -513,7 +522,7 @@ static const struct mhi_pci_dev_info mhi_foxconn_t99w515_info = { static const struct mhi_pci_dev_info mhi_foxconn_dw5934e_info = { .name = "foxconn-dw5934e", - .edl = "fox/sdx72m/edl.mbn", + .edl = "qcom/sdx72m/foxconn/edl.mbn", .edl_trigger = true, .config = &modem_foxconn_sdx72_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, @@ -680,6 +689,35 @@ static const struct mhi_pci_dev_info mhi_telit_fn990_info = { .mru_default = 32768, }; +static const struct mhi_pci_dev_info mhi_telit_fe990a_info = { + .name = "telit-fe990a", + .config = &modem_telit_fn990_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, + .mru_default = 32768, +}; + +static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = { + .name = "netprisma-lcur57", + .edl = "qcom/prog_firehose_sdx24.mbn", + .config = &modem_quectel_em1xx_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = true, +}; + +static const struct mhi_pci_dev_info mhi_netprisma_fcun69_info = { + .name = "netprisma-fcun69", + .edl = "qcom/prog_firehose_sdx6x.elf", + .config = &modem_quectel_em1xx_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = true, +}; + /* Keep the list sorted based on the PID. New VID should be added as the last entry */ static const struct pci_device_id mhi_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304), @@ -697,9 +735,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FN990 */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2010), .driver_data = (kernel_ulong_t) &mhi_telit_fn990_info }, - /* Telit FE990 */ + /* Telit FE990A */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015), - .driver_data = (kernel_ulong_t) &mhi_telit_fn990_info }, + .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309), @@ -778,6 +816,12 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* T99W175 (sdx55), HP variant */ { PCI_DEVICE(0x03f0, 0x0a6c), .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w175_info }, + /* NETPRISMA LCUR57 (SDX24) */ + { PCI_DEVICE(PCI_VENDOR_ID_NETPRISMA, 0x1000), + .driver_data = (kernel_ulong_t) &mhi_netprisma_lcur57_info }, + /* NETPRISMA FCUN69 (SDX6X) */ + { PCI_DEVICE(PCI_VENDOR_ID_NETPRISMA, 0x1001), + .driver_data = (kernel_ulong_t) &mhi_netprisma_fcun69_info }, { } }; MODULE_DEVICE_TABLE(pci, mhi_pci_id_table); diff --git a/drivers/cdx/controller/mcdi.c b/drivers/cdx/controller/mcdi.c index 1eedc5eeb315..e760f8d347cc 100644 --- a/drivers/cdx/controller/mcdi.c +++ b/drivers/cdx/controller/mcdi.c @@ -27,10 +27,6 @@ #include "bitfield.h" #include "mcdi.h" -struct cdx_mcdi_copy_buffer { - struct cdx_dword buffer[DIV_ROUND_UP(MCDI_CTL_SDU_LEN_MAX, 4)]; -}; - static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd); static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx); static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx, diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index da32e8ed0830..3dadc4accee3 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -808,7 +808,7 @@ int hpet_alloc(struct hpet_data *hdp) struct hpets *hpetp; struct hpet __iomem *hpet; static struct hpets *last; - unsigned long period; + u32 period; unsigned long long temp; u32 remainder; @@ -865,11 +865,11 @@ int hpet_alloc(struct hpet_data *hdp) do_div(temp, period); hpetp->hp_tick_freq = temp; /* ticks per second */ - printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s", + printk(KERN_INFO "hpet%u: at MMIO 0x%lx, IRQ%s", hpetp->hp_which, hdp->hd_phys_address, hpetp->hp_ntimer > 1 ? "s" : ""); for (i = 0; i < hpetp->hp_ntimer; i++) - printk(KERN_CONT "%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); + printk(KERN_CONT "%s %u", i > 0 ? "," : "", hdp->hd_irq[i]); printk(KERN_CONT "\n"); temp = hpetp->hp_tick_freq; diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index 8876a1d24c56..330ae1c58800 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -79,6 +79,15 @@ #include "ni_stc.h" +static const struct comedi_lrange range_ni_E_ao_ext = { + 4, { + BIP_RANGE(10), + UNI_RANGE(10), + RANGE_ext(-1, 1), + RANGE_ext(0, 1) + } +}; + /* AT specific setup */ static const struct ni_board_struct ni_boards[] = { { diff --git a/drivers/comedi/drivers/ni_mio_common.c b/drivers/comedi/drivers/ni_mio_common.c index 980f309d6de7..3acb449d293c 100644 --- a/drivers/comedi/drivers/ni_mio_common.c +++ b/drivers/comedi/drivers/ni_mio_common.c @@ -166,15 +166,6 @@ static const struct comedi_lrange range_ni_M_ai_628x = { } }; -static const struct comedi_lrange range_ni_E_ao_ext = { - 4, { - BIP_RANGE(10), - UNI_RANGE(10), - RANGE_ext(-1, 1), - RANGE_ext(0, 1) - } -}; - static const struct comedi_lrange *const ni_range_lkup[] = { [ai_gain_16] = &range_ni_E_ai, [ai_gain_8] = &range_ni_E_ai_limited, diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index 0b055321023d..f63c390314e1 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -102,6 +102,15 @@ #define PCIDMA +static const struct comedi_lrange range_ni_E_ao_ext = { + 4, { + BIP_RANGE(10), + UNI_RANGE(10), + RANGE_ext(-1, 1), + RANGE_ext(0, 1) + } +}; + /* * These are not all the possible ao ranges for 628x boards. * They can do OFFSET +- REFERENCE where OFFSET can be diff --git a/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c b/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c index d55521b5bdcb..892a66b2cea6 100644 --- a/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c +++ b/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c @@ -140,6 +140,11 @@ int main(void) { FILE *fp = fopen("ni_values.py", "w"); + if (fp == NULL) { + fprintf(stderr, "Could not open file!"); + return -1; + } + /* write route register values */ fprintf(fp, "ni_route_values = {\n"); for (int i = 0; ni_all_route_values[i]; ++i) diff --git a/drivers/comedi/drivers/ni_stc.h b/drivers/comedi/drivers/ni_stc.h index fbc0b753a0f5..7837e4683c6d 100644 --- a/drivers/comedi/drivers/ni_stc.h +++ b/drivers/comedi/drivers/ni_stc.h @@ -1137,6 +1137,4 @@ struct ni_private { u8 rgout0_usage; }; -static const struct comedi_lrange range_ni_E_ao_ext; - #endif /* _COMEDI_NI_STC_H */ diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 3da94b382292..a6f6d467aacf 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -75,6 +75,17 @@ config EXTCON_INTEL_MRFLD Say Y here to enable extcon support for charger detection / control on the Intel Merrifield Basin Cove PMIC. +config EXTCON_LC824206XA + tristate "LC824206XA extcon Support" + depends on I2C + depends on POWER_SUPPLY + help + Say Y here to enable support for the ON Semiconductor LC824206XA + microUSB switch and accessory detector chip. The LC824206XA is a USB + port accessory detector and switch. The LC824206XA is fully controlled + using I2C and enables USB data, stereo and mono audio, video, + microphone and UART data to use a common connector port. + config EXTCON_MAX14577 tristate "Maxim MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index f779adb5e4c7..0d6d23faf748 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o obj-$(CONFIG_EXTCON_INTEL_MRFLD) += extcon-intel-mrfld.o +obj-$(CONFIG_EXTCON_LC824206XA) += extcon-lc824206xa.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o diff --git a/drivers/extcon/extcon-lc824206xa.c b/drivers/extcon/extcon-lc824206xa.c new file mode 100644 index 000000000000..56938748aea8 --- /dev/null +++ b/drivers/extcon/extcon-lc824206xa.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ON Semiconductor LC824206XA Micro USB Switch driver + * + * Copyright (c) 2024 Hans de Goede <hansg@kernel.org> + * + * ON Semiconductor has an "Advance Information" datasheet available + * (ENA2222-D.PDF), but no full datasheet. So there is no documentation + * available for the registers. + * + * This driver is based on the register info from the extcon-fsa9285.c driver, + * from the Lollipop Android sources for the Lenovo Yoga Tablet 2 (Pro) + * 830 / 1050 / 1380 models. Note despite the name this is actually a driver + * for the LC824206XA not the FSA9285. The Android sources can be downloaded + * from Lenovo's support page for these tablets, filename: + * yoga_tab_2_osc_android_to_lollipop_201505.rar. + */ + +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/extcon-provider.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> + +/* + * Register defines as mentioned above there is no datasheet with register + * info, so this may not be 100% accurate. + */ +#define REG00 0x00 +#define REG00_INIT_VALUE 0x01 + +#define REG_STATUS 0x01 +#define STATUS_OVP BIT(0) +#define STATUS_DATA_SHORT BIT(1) +#define STATUS_VBUS_PRESENT BIT(2) +#define STATUS_USB_ID GENMASK(7, 3) +#define STATUS_USB_ID_GND 0x80 +#define STATUS_USB_ID_ACA 0xf0 +#define STATUS_USB_ID_FLOAT 0xf8 + +/* + * This controls the DP/DM muxes + other switches, + * meaning of individual bits is unknown. + */ +#define REG_SWITCH_CONTROL 0x02 +#define SWITCH_STEREO_MIC 0xc8 +#define SWITCH_USB_HOST 0xec +#define SWITCH_DISCONNECTED 0xf8 +#define SWITCH_USB_DEVICE 0xfc + +/* 5 bits? ADC 0x10 GND, 0x1a-0x1f ACA, 0x1f float */ +#define REG_ID_PIN_ADC_VALUE 0x03 + +/* Masks for all 3 interrupt registers */ +#define INTR_ID_PIN_CHANGE BIT(0) +#define INTR_VBUS_CHANGE BIT(1) +/* Both of these get set after a continuous mode ADC conversion */ +#define INTR_ID_PIN_ADC_INT1 BIT(2) +#define INTR_ID_PIN_ADC_INT2 BIT(3) +/* Charger type available in reg 0x09 */ +#define INTR_CHARGER_DET_DONE BIT(4) +#define INTR_OVP BIT(5) + +/* There are 7 interrupt sources, bit 6 use is unknown (OCP?) */ +#define INTR_ALL GENMASK(6, 0) + +/* Unmask interrupts this driver cares about */ +#define INTR_MASK \ + (INTR_ALL & ~(INTR_ID_PIN_CHANGE | INTR_VBUS_CHANGE | INTR_CHARGER_DET_DONE)) + +/* Active (event happened and not cleared yet) interrupts */ +#define REG_INTR_STATUS 0x04 + +/* + * Writing a 1 to a bit here clears it in INTR_STATUS. These bits do NOT + * auto-reset to 0, so these must be set to 0 manually after clearing. + */ +#define REG_INTR_CLEAR 0x05 + +/* Interrupts which bit is set to 1 here will not raise the HW IRQ */ +#define REG_INTR_MASK 0x06 + +/* ID pin ADC control, meaning of individual bits is unknown */ +#define REG_ID_PIN_ADC_CTRL 0x07 +#define ID_PIN_ADC_AUTO 0x40 +#define ID_PIN_ADC_CONTINUOUS 0x44 + +#define REG_CHARGER_DET 0x08 +#define CHARGER_DET_ON BIT(0) +#define CHARGER_DET_CDP_ON BIT(1) +#define CHARGER_DET_CDP_VAL BIT(2) + +#define REG_CHARGER_TYPE 0x09 +#define CHARGER_TYPE_UNKNOWN 0x00 +#define CHARGER_TYPE_DCP 0x01 +#define CHARGER_TYPE_SDP_OR_CDP 0x04 +#define CHARGER_TYPE_QC 0x06 + +#define REG10 0x10 +#define REG10_INIT_VALUE 0x00 + +struct lc824206xa_data { + struct work_struct work; + struct i2c_client *client; + struct extcon_dev *edev; + struct power_supply *psy; + struct regulator *vbus_boost; + unsigned int usb_type; + unsigned int cable; + unsigned int previous_cable; + u8 switch_control; + u8 previous_switch_control; + bool vbus_ok; + bool vbus_boost_enabled; + bool fastcharge_over_miclr; +}; + +static const unsigned int lc824206xa_cables[] = { + EXTCON_USB_HOST, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_ACA, + EXTCON_CHG_USB_FAST, + EXTCON_NONE, +}; + +/* read/write reg helpers to add error logging to smbus byte functions */ +static int lc824206xa_read_reg(struct lc824206xa_data *data, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, reg); + if (ret < 0) + dev_err(&data->client->dev, "Error %d reading reg 0x%02x\n", ret, reg); + + return ret; +} + +static int lc824206xa_write_reg(struct lc824206xa_data *data, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, reg, val); + if (ret < 0) + dev_err(&data->client->dev, "Error %d writing reg 0x%02x\n", ret, reg); + + return ret; +} + +static int lc824206xa_get_id(struct lc824206xa_data *data) +{ + int ret; + + ret = lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_CONTINUOUS); + if (ret) + return ret; + + ret = lc824206xa_read_reg(data, REG_ID_PIN_ADC_VALUE); + + lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_AUTO); + + return ret; +} + +static void lc824206xa_set_vbus_boost(struct lc824206xa_data *data, bool enable) +{ + int ret; + + if (data->vbus_boost_enabled == enable) + return; + + if (enable) + ret = regulator_enable(data->vbus_boost); + else + ret = regulator_disable(data->vbus_boost); + + if (ret == 0) + data->vbus_boost_enabled = enable; + else + dev_err(&data->client->dev, "Error updating Vbus boost regulator: %d\n", ret); +} + +static void lc824206xa_charger_detect(struct lc824206xa_data *data) +{ + int charger_type, ret; + + charger_type = lc824206xa_read_reg(data, REG_CHARGER_TYPE); + if (charger_type < 0) + return; + + dev_dbg(&data->client->dev, "charger type 0x%02x\n", charger_type); + + switch (charger_type) { + case CHARGER_TYPE_UNKNOWN: + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + /* Treat as SDP */ + data->cable = EXTCON_CHG_USB_SDP; + data->switch_control = SWITCH_USB_DEVICE; + break; + case CHARGER_TYPE_SDP_OR_CDP: + data->usb_type = POWER_SUPPLY_USB_TYPE_SDP; + data->cable = EXTCON_CHG_USB_SDP; + data->switch_control = SWITCH_USB_DEVICE; + + ret = lc824206xa_write_reg(data, REG_CHARGER_DET, + CHARGER_DET_CDP_ON | CHARGER_DET_ON); + if (ret < 0) + break; + + msleep(100); + ret = lc824206xa_read_reg(data, REG_CHARGER_DET); + if (ret >= 0 && (ret & CHARGER_DET_CDP_VAL)) { + data->usb_type = POWER_SUPPLY_USB_TYPE_CDP; + data->cable = EXTCON_CHG_USB_CDP; + } + + lc824206xa_write_reg(data, REG_CHARGER_DET, CHARGER_DET_ON); + break; + case CHARGER_TYPE_DCP: + data->usb_type = POWER_SUPPLY_USB_TYPE_DCP; + data->cable = EXTCON_CHG_USB_DCP; + if (data->fastcharge_over_miclr) + data->switch_control = SWITCH_STEREO_MIC; + else + data->switch_control = SWITCH_DISCONNECTED; + break; + case CHARGER_TYPE_QC: + data->usb_type = POWER_SUPPLY_USB_TYPE_DCP; + data->cable = EXTCON_CHG_USB_DCP; + data->switch_control = SWITCH_DISCONNECTED; + break; + default: + dev_warn(&data->client->dev, "Unknown charger type: 0x%02x\n", charger_type); + break; + } +} + +static void lc824206xa_work(struct work_struct *work) +{ + struct lc824206xa_data *data = container_of(work, struct lc824206xa_data, work); + bool vbus_boost_enable = false; + int status, id; + + status = lc824206xa_read_reg(data, REG_STATUS); + if (status < 0) + return; + + dev_dbg(&data->client->dev, "status 0x%02x\n", status); + + data->vbus_ok = (status & (STATUS_VBUS_PRESENT | STATUS_OVP)) == STATUS_VBUS_PRESENT; + + /* Read id pin ADC if necessary */ + switch (status & STATUS_USB_ID) { + case STATUS_USB_ID_GND: + case STATUS_USB_ID_FLOAT: + break; + default: + /* Happens when the connector is inserted slowly, log at dbg level */ + dev_dbg(&data->client->dev, "Unknown status 0x%02x\n", status); + fallthrough; + case STATUS_USB_ID_ACA: + id = lc824206xa_get_id(data); + dev_dbg(&data->client->dev, "RID 0x%02x\n", id); + switch (id) { + case 0x10: + status = STATUS_USB_ID_GND; + break; + case 0x18 ... 0x1e: + status = STATUS_USB_ID_ACA; + break; + case 0x1f: + status = STATUS_USB_ID_FLOAT; + break; + default: + dev_warn(&data->client->dev, "Unknown RID 0x%02x\n", id); + return; + } + } + + /* Check for out of spec OTG charging hubs, treat as ACA */ + if ((status & STATUS_USB_ID) == STATUS_USB_ID_GND && + data->vbus_ok && !data->vbus_boost_enabled) { + dev_info(&data->client->dev, "Out of spec USB host adapter with Vbus present, not enabling 5V output\n"); + status = STATUS_USB_ID_ACA; + } + + switch (status & STATUS_USB_ID) { + case STATUS_USB_ID_ACA: + data->usb_type = POWER_SUPPLY_USB_TYPE_ACA; + data->cable = EXTCON_CHG_USB_ACA; + data->switch_control = SWITCH_USB_HOST; + break; + case STATUS_USB_ID_GND: + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + data->cable = EXTCON_USB_HOST; + data->switch_control = SWITCH_USB_HOST; + vbus_boost_enable = true; + break; + case STATUS_USB_ID_FLOAT: + /* When fast charging with Vbus > 5V, OVP will be set */ + if (data->fastcharge_over_miclr && + data->switch_control == SWITCH_STEREO_MIC && + (status & STATUS_OVP)) { + data->cable = EXTCON_CHG_USB_FAST; + break; + } + + if (data->vbus_ok) { + lc824206xa_charger_detect(data); + } else { + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + data->cable = EXTCON_NONE; + data->switch_control = SWITCH_DISCONNECTED; + } + break; + } + + lc824206xa_set_vbus_boost(data, vbus_boost_enable); + + if (data->switch_control != data->previous_switch_control) { + lc824206xa_write_reg(data, REG_SWITCH_CONTROL, data->switch_control); + data->previous_switch_control = data->switch_control; + } + + if (data->cable != data->previous_cable) { + extcon_set_state_sync(data->edev, data->previous_cable, false); + extcon_set_state_sync(data->edev, data->cable, true); + data->previous_cable = data->cable; + } + + power_supply_changed(data->psy); +} + +static irqreturn_t lc824206xa_irq(int irq, void *_data) +{ + struct lc824206xa_data *data = _data; + int intr_status; + + intr_status = lc824206xa_read_reg(data, REG_INTR_STATUS); + if (intr_status < 0) + intr_status = INTR_ALL; /* Should never happen, clear all */ + + dev_dbg(&data->client->dev, "interrupt 0x%02x\n", intr_status); + + lc824206xa_write_reg(data, REG_INTR_CLEAR, intr_status); + lc824206xa_write_reg(data, REG_INTR_CLEAR, 0); + + schedule_work(&data->work); + return IRQ_HANDLED; +} + +/* + * Newer charger (power_supply) drivers expect the max input current to be + * provided by a parent power_supply device for the charger chip. + */ +static int lc824206xa_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct lc824206xa_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = data->vbus_ok && !data->vbus_boost_enabled; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = data->usb_type; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + switch (data->usb_type) { + case POWER_SUPPLY_USB_TYPE_DCP: + case POWER_SUPPLY_USB_TYPE_ACA: + val->intval = 2000000; + break; + case POWER_SUPPLY_USB_TYPE_CDP: + val->intval = 1500000; + break; + default: + val->intval = 500000; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static const enum power_supply_property lc824206xa_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static const struct power_supply_desc lc824206xa_psy_desc = { + .name = "lc824206xa-charger-detect", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | + BIT(POWER_SUPPLY_USB_TYPE_CDP) | + BIT(POWER_SUPPLY_USB_TYPE_DCP) | + BIT(POWER_SUPPLY_USB_TYPE_ACA) | + BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), + .properties = lc824206xa_psy_props, + .num_properties = ARRAY_SIZE(lc824206xa_psy_props), + .get_property = lc824206xa_psy_get_prop, +}; + +static int lc824206xa_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = { }; + struct device *dev = &client->dev; + struct lc824206xa_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + INIT_WORK(&data->work, lc824206xa_work); + data->cable = EXTCON_NONE; + data->previous_cable = EXTCON_NONE; + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + /* Some designs use a custom fast-charge protocol over the mic L/R inputs */ + data->fastcharge_over_miclr = + device_property_read_bool(dev, "onnn,enable-miclr-for-dcp"); + + data->vbus_boost = devm_regulator_get(dev, "vbus"); + if (IS_ERR(data->vbus_boost)) + return dev_err_probe(dev, PTR_ERR(data->vbus_boost), + "getting regulator\n"); + + /* Init */ + ret = lc824206xa_write_reg(data, REG00, REG00_INIT_VALUE); + ret |= lc824206xa_write_reg(data, REG10, REG10_INIT_VALUE); + msleep(100); + ret |= lc824206xa_write_reg(data, REG_INTR_CLEAR, INTR_ALL); + ret |= lc824206xa_write_reg(data, REG_INTR_CLEAR, 0); + ret |= lc824206xa_write_reg(data, REG_INTR_MASK, INTR_MASK); + ret |= lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_AUTO); + ret |= lc824206xa_write_reg(data, REG_CHARGER_DET, CHARGER_DET_ON); + if (ret) + return -EIO; + + /* Initialize extcon device */ + data->edev = devm_extcon_dev_allocate(dev, lc824206xa_cables); + if (IS_ERR(data->edev)) + return PTR_ERR(data->edev); + + ret = devm_extcon_dev_register(dev, data->edev); + if (ret) + return dev_err_probe(dev, ret, "registering extcon device\n"); + + psy_cfg.drv_data = data; + data->psy = devm_power_supply_register(dev, &lc824206xa_psy_desc, &psy_cfg); + if (IS_ERR(data->psy)) + return dev_err_probe(dev, PTR_ERR(data->psy), "registering power supply\n"); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, lc824206xa_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + KBUILD_MODNAME, data); + if (ret) + return dev_err_probe(dev, ret, "requesting IRQ\n"); + + /* Sync initial state */ + schedule_work(&data->work); + return 0; +} + +static const struct i2c_device_id lc824206xa_i2c_ids[] = { + { "lc824206xa" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lc824206xa_i2c_ids); + +static struct i2c_driver lc824206xa_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = lc824206xa_probe, + .id_table = lc824206xa_i2c_ids, +}; + +module_i2c_driver(lc824206xa_driver); + +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_DESCRIPTION("LC824206XA Micro USB Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c index 723ea0ad3f09..b08b4bb8f650 100644 --- a/drivers/fpga/socfpga.c +++ b/drivers/fpga/socfpga.c @@ -301,16 +301,17 @@ static irqreturn_t socfpga_fpga_isr(int irq, void *dev_id) static int socfpga_fpga_wait_for_config_done(struct socfpga_fpga_priv *priv) { - int timeout, ret = 0; + int ret = 0; + long time_left; socfpga_fpga_disable_irqs(priv); init_completion(&priv->status_complete); socfpga_fpga_enable_irqs(priv, SOCFPGA_FPGMGR_MON_CONF_DONE); - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &priv->status_complete, msecs_to_jiffies(10)); - if (timeout == 0) + if (time_left == 0) ret = -ETIMEDOUT; socfpga_fpga_disable_irqs(priv); diff --git a/drivers/fpga/tests/fpga-bridge-test.c b/drivers/fpga/tests/fpga-bridge-test.c index 2f7a24f23808..b9ab29809e96 100644 --- a/drivers/fpga/tests/fpga-bridge-test.c +++ b/drivers/fpga/tests/fpga-bridge-test.c @@ -23,6 +23,13 @@ struct bridge_ctx { struct bridge_stats stats; }; +/* + * Wrapper to avoid a cast warning when passing the action function directly + * to kunit_add_action(). + */ +KUNIT_DEFINE_ACTION_WRAPPER(fpga_bridge_unregister_wrapper, fpga_bridge_unregister, + struct fpga_bridge *); + static int op_enable_set(struct fpga_bridge *bridge, bool enable) { struct bridge_stats *stats = bridge->priv; @@ -50,6 +57,7 @@ static const struct fpga_bridge_ops fake_bridge_ops = { static struct bridge_ctx *register_test_bridge(struct kunit *test, const char *dev_name) { struct bridge_ctx *ctx; + int ret; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); @@ -61,13 +69,10 @@ static struct bridge_ctx *register_test_bridge(struct kunit *test, const char *d &ctx->stats); KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge)); - return ctx; -} + ret = kunit_add_action_or_reset(test, fpga_bridge_unregister_wrapper, ctx->bridge); + KUNIT_ASSERT_EQ(test, ret, 0); -static void unregister_test_bridge(struct kunit *test, struct bridge_ctx *ctx) -{ - fpga_bridge_unregister(ctx->bridge); - kunit_device_unregister(test, ctx->dev); + return ctx; } static void fpga_bridge_test_get(struct kunit *test) @@ -141,8 +146,6 @@ static void fpga_bridge_test_get_put_list(struct kunit *test) fpga_bridges_put(&bridge_list); KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list)); - - unregister_test_bridge(test, ctx_1); } static int fpga_bridge_test_init(struct kunit *test) @@ -152,11 +155,6 @@ static int fpga_bridge_test_init(struct kunit *test) return 0; } -static void fpga_bridge_test_exit(struct kunit *test) -{ - unregister_test_bridge(test, test->priv); -} - static struct kunit_case fpga_bridge_test_cases[] = { KUNIT_CASE(fpga_bridge_test_get), KUNIT_CASE(fpga_bridge_test_toggle), @@ -167,7 +165,6 @@ static struct kunit_case fpga_bridge_test_cases[] = { static struct kunit_suite fpga_bridge_suite = { .name = "fpga_bridge", .init = fpga_bridge_test_init, - .exit = fpga_bridge_test_exit, .test_cases = fpga_bridge_test_cases, }; diff --git a/drivers/fpga/tests/fpga-mgr-test.c b/drivers/fpga/tests/fpga-mgr-test.c index 125b3a4d43c6..9cb37aefbac4 100644 --- a/drivers/fpga/tests/fpga-mgr-test.c +++ b/drivers/fpga/tests/fpga-mgr-test.c @@ -44,6 +44,16 @@ struct mgr_ctx { struct mgr_stats stats; }; +/* + * Wrappers to avoid cast warnings when passing action functions directly + * to kunit_add_action(). + */ +KUNIT_DEFINE_ACTION_WRAPPER(sg_free_table_wrapper, sg_free_table, + struct sg_table *); + +KUNIT_DEFINE_ACTION_WRAPPER(fpga_image_info_free_wrapper, fpga_image_info_free, + struct fpga_image_info *); + /** * init_test_buffer() - Allocate and initialize a test image in a buffer. * @test: KUnit test context object. @@ -257,6 +267,9 @@ static void fpga_mgr_test_img_load_sgt(struct kunit *test) KUNIT_ASSERT_EQ(test, ret, 0); sg_init_one(sgt->sgl, img_buf, IMAGE_SIZE); + ret = kunit_add_action_or_reset(test, sg_free_table_wrapper, sgt); + KUNIT_ASSERT_EQ(test, ret, 0); + ctx->img_info->sgt = sgt; ret = fpga_mgr_load(ctx->mgr, ctx->img_info); @@ -273,13 +286,12 @@ static void fpga_mgr_test_img_load_sgt(struct kunit *test) KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1); KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_seq, ctx->stats.op_parse_header_seq + 2); KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3); - - sg_free_table(ctx->img_info->sgt); } static int fpga_mgr_test_init(struct kunit *test) { struct mgr_ctx *ctx; + int ret; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); @@ -294,19 +306,14 @@ static int fpga_mgr_test_init(struct kunit *test) ctx->img_info = fpga_image_info_alloc(ctx->dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info); + ret = kunit_add_action_or_reset(test, fpga_image_info_free_wrapper, ctx->img_info); + KUNIT_ASSERT_EQ(test, ret, 0); + test->priv = ctx; return 0; } -static void fpga_mgr_test_exit(struct kunit *test) -{ - struct mgr_ctx *ctx = test->priv; - - fpga_image_info_free(ctx->img_info); - kunit_device_unregister(test, ctx->dev); -} - static struct kunit_case fpga_mgr_test_cases[] = { KUNIT_CASE(fpga_mgr_test_get), KUNIT_CASE(fpga_mgr_test_lock), @@ -318,7 +325,6 @@ static struct kunit_case fpga_mgr_test_cases[] = { static struct kunit_suite fpga_mgr_suite = { .name = "fpga_mgr", .init = fpga_mgr_test_init, - .exit = fpga_mgr_test_exit, .test_cases = fpga_mgr_test_cases, }; diff --git a/drivers/fpga/tests/fpga-region-test.c b/drivers/fpga/tests/fpga-region-test.c index bcf0651df261..6a108cafded8 100644 --- a/drivers/fpga/tests/fpga-region-test.c +++ b/drivers/fpga/tests/fpga-region-test.c @@ -35,6 +35,19 @@ struct test_ctx { struct mgr_stats mgr_stats; }; +/* + * Wrappers to avoid cast warnings when passing action functions directly + * to kunit_add_action(). + */ +KUNIT_DEFINE_ACTION_WRAPPER(fpga_image_info_free_wrapper, fpga_image_info_free, + struct fpga_image_info *); + +KUNIT_DEFINE_ACTION_WRAPPER(fpga_bridge_unregister_wrapper, fpga_bridge_unregister, + struct fpga_bridge *); + +KUNIT_DEFINE_ACTION_WRAPPER(fpga_region_unregister_wrapper, fpga_region_unregister, + struct fpga_region *); + static int op_write(struct fpga_manager *mgr, const char *buf, size_t count) { struct mgr_stats *stats = mgr->priv; @@ -111,6 +124,9 @@ static void fpga_region_test_program_fpga(struct kunit *test) img_info = fpga_image_info_alloc(ctx->mgr_dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); + ret = kunit_add_action_or_reset(test, fpga_image_info_free_wrapper, img_info); + KUNIT_ASSERT_EQ(test, ret, 0); + img_info->buf = img_buf; img_info->count = sizeof(img_buf); @@ -130,8 +146,6 @@ static void fpga_region_test_program_fpga(struct kunit *test) KUNIT_EXPECT_EQ(test, 2, ctx->bridge_stats.cycles_count); fpga_bridges_put(&ctx->region->bridge_list); - - fpga_image_info_free(img_info); } /* @@ -144,6 +158,7 @@ static int fpga_region_test_init(struct kunit *test) { struct test_ctx *ctx; struct fpga_region_info region_info = { 0 }; + int ret; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); @@ -164,6 +179,9 @@ static int fpga_region_test_init(struct kunit *test) ctx->bridge_stats.enable = true; + ret = kunit_add_action_or_reset(test, fpga_bridge_unregister_wrapper, ctx->bridge); + KUNIT_ASSERT_EQ(test, ret, 0); + ctx->region_dev = kunit_device_register(test, "fpga-region-test-dev"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_dev); @@ -174,24 +192,14 @@ static int fpga_region_test_init(struct kunit *test) ctx->region = fpga_region_register_full(ctx->region_dev, ®ion_info); KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region)); + ret = kunit_add_action_or_reset(test, fpga_region_unregister_wrapper, ctx->region); + KUNIT_ASSERT_EQ(test, ret, 0); + test->priv = ctx; return 0; } -static void fpga_region_test_exit(struct kunit *test) -{ - struct test_ctx *ctx = test->priv; - - fpga_region_unregister(ctx->region); - kunit_device_unregister(test, ctx->region_dev); - - fpga_bridge_unregister(ctx->bridge); - kunit_device_unregister(test, ctx->bridge_dev); - - kunit_device_unregister(test, ctx->mgr_dev); -} - static struct kunit_case fpga_region_test_cases[] = { KUNIT_CASE(fpga_region_test_class_find), KUNIT_CASE(fpga_region_test_program_fpga), @@ -199,9 +207,8 @@ static struct kunit_case fpga_region_test_cases[] = { }; static struct kunit_suite fpga_region_suite = { - .name = "fpga_mgr", + .name = "fpga_region", .init = fpga_region_test_init, - .exit = fpga_region_test_exit, .test_cases = fpga_region_test_cases, }; diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 0ac93183d201..4db3d80e10b0 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -387,7 +387,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) const char *why; int err; u32 intr_status; - unsigned long timeout; + unsigned long time_left; unsigned long flags; struct scatterlist *sg; int i; @@ -427,8 +427,8 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) zynq_step_dma(priv); spin_unlock_irqrestore(&priv->dma_lock, flags); - timeout = wait_for_completion_timeout(&priv->dma_done, - msecs_to_jiffies(DMA_TIMEOUT_MS)); + time_left = wait_for_completion_timeout(&priv->dma_done, + msecs_to_jiffies(DMA_TIMEOUT_MS)); spin_lock_irqsave(&priv->dma_lock, flags); zynq_fpga_set_irq(priv, 0); @@ -452,7 +452,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) if (priv->cur_sg || !((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) { - if (timeout == 0) + if (time_left == 0) why = "DMA timed out"; else why = "DMA did not complete"; diff --git a/drivers/greybus/Kconfig b/drivers/greybus/Kconfig index ab81ceceb337..c3f056d28b01 100644 --- a/drivers/greybus/Kconfig +++ b/drivers/greybus/Kconfig @@ -21,6 +21,8 @@ config GREYBUS_BEAGLEPLAY tristate "Greybus BeaglePlay driver" depends on SERIAL_DEV_BUS select CRC_CCITT + select FW_LOADER + select FW_UPLOAD help Select this option if you have a BeaglePlay where CC1352 co-processor acts as Greybus SVC. diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 33f8fad70260..3a1ade84737c 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -6,21 +6,19 @@ * Copyright (c) 2023 BeagleBoard.org Foundation */ -#include <linux/gfp.h> +#include <asm-generic/unaligned.h> +#include <linux/crc32.h> +#include <linux/gpio/consumer.h> +#include <linux/firmware.h> #include <linux/greybus.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/printk.h> #include <linux/serdev.h> -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/greybus/hd.h> -#include <linux/init.h> -#include <linux/device.h> #include <linux/crc-ccitt.h> #include <linux/circ_buf.h> -#include <linux/types.h> -#include <linux/workqueue.h> + +#define CC1352_FIRMWARE_SIZE (704 * 1024) +#define CC1352_BOOTLOADER_TIMEOUT 2000 +#define CC1352_BOOTLOADER_ACK 0xcc +#define CC1352_BOOTLOADER_NACK 0x33 #define RX_HDLC_PAYLOAD 256 #define CRC_LEN 2 @@ -57,6 +55,17 @@ * @rx_buffer_len: length of receive buffer filled. * @rx_buffer: hdlc frame receive buffer * @rx_in_esc: hdlc rx flag to indicate ESC frame + * + * @fwl: underlying firmware upload device + * @bootloader_backdoor_gpio: cc1352p7 boot gpio + * @rst_gpio: cc1352p7 reset gpio + * @flashing_mode: flag to indicate that flashing is currently in progress + * @fwl_ack_com: completion to signal an Ack/Nack + * @fwl_ack: Ack/Nack byte received + * @fwl_cmd_response_com: completion to signal a bootloader command response + * @fwl_cmd_response: bootloader command response data + * @fwl_crc32: crc32 of firmware to flash + * @fwl_reset_addr: flag to indicate if we need to send COMMAND_DOWNLOAD again */ struct gb_beagleplay { struct serdev_device *sd; @@ -72,6 +81,17 @@ struct gb_beagleplay { u16 rx_buffer_len; bool rx_in_esc; u8 rx_buffer[MAX_RX_HDLC]; + + struct fw_upload *fwl; + struct gpio_desc *bootloader_backdoor_gpio; + struct gpio_desc *rst_gpio; + bool flashing_mode; + struct completion fwl_ack_com; + u8 fwl_ack; + struct completion fwl_cmd_response_com; + u32 fwl_cmd_response; + u32 fwl_crc32; + bool fwl_reset_addr; }; /** @@ -100,6 +120,87 @@ struct hdlc_greybus_frame { u8 payload[]; } __packed; +/** + * enum cc1352_bootloader_cmd: CC1352 Bootloader Commands + * + * @COMMAND_DOWNLOAD: Prepares flash programming + * @COMMAND_GET_STATUS: Returns the status of the last command that was issued + * @COMMAND_SEND_DATA: Transfers data and programs flash + * @COMMAND_RESET: Performs a system reset + * @COMMAND_CRC32: Calculates CRC32 over a specified memory area + * @COMMAND_BANK_ERASE: Performs an erase of all of the customer-accessible + * flash sectors not protected by FCFG1 and CCFG + * writeprotect bits. + * + * CC1352 Bootloader serial bus commands + */ +enum cc1352_bootloader_cmd { + COMMAND_DOWNLOAD = 0x21, + COMMAND_GET_STATUS = 0x23, + COMMAND_SEND_DATA = 0x24, + COMMAND_RESET = 0x25, + COMMAND_CRC32 = 0x27, + COMMAND_BANK_ERASE = 0x2c, +}; + +/** + * enum cc1352_bootloader_status: CC1352 Bootloader COMMAND_GET_STATUS response + * + * @COMMAND_RET_SUCCESS: Status for successful command + * @COMMAND_RET_UNKNOWN_CMD: Status for unknown command + * @COMMAND_RET_INVALID_CMD: Status for invalid command (in other words, + * incorrect packet size) + * @COMMAND_RET_INVALID_ADR: Status for invalid input address + * @COMMAND_RET_FLASH_FAIL: Status for failing flash erase or program operation + */ +enum cc1352_bootloader_status { + COMMAND_RET_SUCCESS = 0x40, + COMMAND_RET_UNKNOWN_CMD = 0x41, + COMMAND_RET_INVALID_CMD = 0x42, + COMMAND_RET_INVALID_ADR = 0x43, + COMMAND_RET_FLASH_FAIL = 0x44, +}; + +/** + * struct cc1352_bootloader_packet: CC1352 Bootloader Request Packet + * + * @len: length of packet + optional request data + * @checksum: 8-bit checksum excluding len + * @cmd: bootloader command + */ +struct cc1352_bootloader_packet { + u8 len; + u8 checksum; + u8 cmd; +} __packed; + +#define CC1352_BOOTLOADER_PKT_MAX_SIZE \ + (U8_MAX - sizeof(struct cc1352_bootloader_packet)) + +/** + * struct cc1352_bootloader_download_cmd_data: CC1352 Bootloader COMMAND_DOWNLOAD request data + * + * @addr: address to start programming data into + * @size: size of data that will be sent + */ +struct cc1352_bootloader_download_cmd_data { + __be32 addr; + __be32 size; +} __packed; + +/** + * struct cc1352_bootloader_crc32_cmd_data: CC1352 Bootloader COMMAND_CRC32 request data + * + * @addr: address where crc32 calculation starts + * @size: number of bytes comprised by crc32 calculation + * @read_repeat: number of read repeats for each data location + */ +struct cc1352_bootloader_crc32_cmd_data { + __be32 addr; + __be32 size; + __be32 read_repeat; +} __packed; + static void hdlc_rx_greybus_frame(struct gb_beagleplay *bg, u8 *buf, u16 len) { struct hdlc_greybus_frame *gb_frame = (struct hdlc_greybus_frame *)buf; @@ -331,11 +432,135 @@ static void hdlc_deinit(struct gb_beagleplay *bg) flush_work(&bg->tx_work); } +/** + * csum8: Calculate 8-bit checksum on data + * + * @data: bytes to calculate 8-bit checksum of + * @size: number of bytes + * @base: starting value for checksum + */ +static u8 csum8(const u8 *data, size_t size, u8 base) +{ + size_t i; + u8 sum = base; + + for (i = 0; i < size; ++i) + sum += data[i]; + + return sum; +} + +static void cc1352_bootloader_send_ack(struct gb_beagleplay *bg) +{ + static const u8 ack[] = { 0x00, CC1352_BOOTLOADER_ACK }; + + serdev_device_write_buf(bg->sd, ack, sizeof(ack)); +} + +static void cc1352_bootloader_send_nack(struct gb_beagleplay *bg) +{ + static const u8 nack[] = { 0x00, CC1352_BOOTLOADER_NACK }; + + serdev_device_write_buf(bg->sd, nack, sizeof(nack)); +} + +/** + * cc1352_bootloader_pkt_rx: Process a CC1352 Bootloader Packet + * + * @bg: beagleplay greybus driver + * @data: packet buffer + * @count: packet buffer size + * + * @return: number of bytes processed + * + * Here are the steps to successfully receive a packet from cc1352 bootloader + * according to the docs: + * 1. Wait for nonzero data to be returned from the device. This is important + * as the device may send zero bytes between a sent and a received data + * packet. The first nonzero byte received is the size of the packet that is + * being received. + * 2. Read the next byte, which is the checksum for the packet. + * 3. Read the data bytes from the device. During the data phase, packet size + * minus 2 bytes is sent. + * 4. Calculate the checksum of the data bytes and verify it matches the + * checksum received in the packet. + * 5. Send an acknowledge byte or a not-acknowledge byte to the device to + * indicate the successful or unsuccessful reception of the packet. + */ +static int cc1352_bootloader_pkt_rx(struct gb_beagleplay *bg, const u8 *data, + size_t count) +{ + bool is_valid = false; + + switch (data[0]) { + /* Skip 0x00 bytes. */ + case 0x00: + return 1; + case CC1352_BOOTLOADER_ACK: + case CC1352_BOOTLOADER_NACK: + WRITE_ONCE(bg->fwl_ack, data[0]); + complete(&bg->fwl_ack_com); + return 1; + case 3: + if (count < 3) + return 0; + is_valid = data[1] == data[2]; + WRITE_ONCE(bg->fwl_cmd_response, (u32)data[2]); + break; + case 6: + if (count < 6) + return 0; + is_valid = csum8(&data[2], sizeof(__be32), 0) == data[1]; + WRITE_ONCE(bg->fwl_cmd_response, get_unaligned_be32(&data[2])); + break; + default: + return -EINVAL; + } + + if (is_valid) { + cc1352_bootloader_send_ack(bg); + complete(&bg->fwl_cmd_response_com); + } else { + dev_warn(&bg->sd->dev, + "Dropping bootloader packet with invalid checksum"); + cc1352_bootloader_send_nack(bg); + } + + return data[0]; +} + +static size_t cc1352_bootloader_rx(struct gb_beagleplay *bg, const u8 *data, + size_t count) +{ + int ret; + size_t off = 0; + + memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count); + bg->rx_buffer_len += count; + + do { + ret = cc1352_bootloader_pkt_rx(bg, bg->rx_buffer + off, + bg->rx_buffer_len - off); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, ret, + "Invalid Packet"); + off += ret; + } while (ret > 0 && off < count); + + bg->rx_buffer_len -= off; + memmove(bg->rx_buffer, bg->rx_buffer + off, bg->rx_buffer_len); + + return count; +} + static size_t gb_tty_receive(struct serdev_device *sd, const u8 *data, size_t count) { struct gb_beagleplay *bg = serdev_device_get_drvdata(sd); + if (READ_ONCE(bg->flashing_mode)) + return cc1352_bootloader_rx(bg, data, count); + return hdlc_rx(bg, data, count); } @@ -343,7 +568,8 @@ static void gb_tty_wakeup(struct serdev_device *serdev) { struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev); - schedule_work(&bg->tx_work); + if (!READ_ONCE(bg->flashing_mode)) + schedule_work(&bg->tx_work); } static struct serdev_device_ops gb_beagleplay_ops = { @@ -412,6 +638,195 @@ static void gb_beagleplay_stop_svc(struct gb_beagleplay *bg) hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); } +static int cc1352_bootloader_wait_for_ack(struct gb_beagleplay *bg) +{ + int ret; + + ret = wait_for_completion_timeout( + &bg->fwl_ack_com, msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT)); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, ret, + "Failed to acquire ack semaphore"); + + switch (READ_ONCE(bg->fwl_ack)) { + case CC1352_BOOTLOADER_ACK: + return 0; + case CC1352_BOOTLOADER_NACK: + return -EAGAIN; + default: + return -EINVAL; + } +} + +static int cc1352_bootloader_sync(struct gb_beagleplay *bg) +{ + static const u8 sync_bytes[] = { 0x55, 0x55 }; + + serdev_device_write_buf(bg->sd, sync_bytes, sizeof(sync_bytes)); + return cc1352_bootloader_wait_for_ack(bg); +} + +static int cc1352_bootloader_get_status(struct gb_beagleplay *bg) +{ + int ret; + static const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt), + .checksum = COMMAND_GET_STATUS, + .cmd = COMMAND_GET_STATUS + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + ret = cc1352_bootloader_wait_for_ack(bg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout( + &bg->fwl_cmd_response_com, + msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT)); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, ret, + "Failed to acquire last status semaphore"); + + switch (READ_ONCE(bg->fwl_cmd_response)) { + case COMMAND_RET_SUCCESS: + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int cc1352_bootloader_erase(struct gb_beagleplay *bg) +{ + int ret; + static const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt), + .checksum = COMMAND_BANK_ERASE, + .cmd = COMMAND_BANK_ERASE + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + + ret = cc1352_bootloader_wait_for_ack(bg); + if (ret < 0) + return ret; + + return cc1352_bootloader_get_status(bg); +} + +static int cc1352_bootloader_reset(struct gb_beagleplay *bg) +{ + static const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt), + .checksum = COMMAND_RESET, + .cmd = COMMAND_RESET + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + + return cc1352_bootloader_wait_for_ack(bg); +} + +/** + * cc1352_bootloader_empty_pkt: Calculate the number of empty bytes in the current packet + * + * @data: packet bytes array to check + * @size: number of bytes in array + */ +static size_t cc1352_bootloader_empty_pkt(const u8 *data, size_t size) +{ + size_t i; + + for (i = 0; i < size && data[i] == 0xff; ++i) + continue; + + return i; +} + +static int cc1352_bootloader_crc32(struct gb_beagleplay *bg, u32 *crc32) +{ + int ret; + static const struct cc1352_bootloader_crc32_cmd_data cmd_data = { + .addr = 0, .size = cpu_to_be32(704 * 1024), .read_repeat = 0 + }; + const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt) + sizeof(cmd_data), + .checksum = csum8((const void *)&cmd_data, sizeof(cmd_data), + COMMAND_CRC32), + .cmd = COMMAND_CRC32 + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + serdev_device_write_buf(bg->sd, (const u8 *)&cmd_data, + sizeof(cmd_data)); + + ret = cc1352_bootloader_wait_for_ack(bg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout( + &bg->fwl_cmd_response_com, + msecs_to_jiffies(CC1352_BOOTLOADER_TIMEOUT)); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, ret, + "Failed to acquire last status semaphore"); + + *crc32 = READ_ONCE(bg->fwl_cmd_response); + + return 0; +} + +static int cc1352_bootloader_download(struct gb_beagleplay *bg, u32 size, + u32 addr) +{ + int ret; + const struct cc1352_bootloader_download_cmd_data cmd_data = { + .addr = cpu_to_be32(addr), + .size = cpu_to_be32(size), + }; + const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt) + sizeof(cmd_data), + .checksum = csum8((const void *)&cmd_data, sizeof(cmd_data), + COMMAND_DOWNLOAD), + .cmd = COMMAND_DOWNLOAD + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + serdev_device_write_buf(bg->sd, (const u8 *)&cmd_data, + sizeof(cmd_data)); + + ret = cc1352_bootloader_wait_for_ack(bg); + if (ret < 0) + return ret; + + return cc1352_bootloader_get_status(bg); +} + +static int cc1352_bootloader_send_data(struct gb_beagleplay *bg, const u8 *data, + size_t size) +{ + int ret, rem = min(size, CC1352_BOOTLOADER_PKT_MAX_SIZE); + const struct cc1352_bootloader_packet pkt = { + .len = sizeof(pkt) + rem, + .checksum = csum8(data, rem, COMMAND_SEND_DATA), + .cmd = COMMAND_SEND_DATA + }; + + serdev_device_write_buf(bg->sd, (const u8 *)&pkt, sizeof(pkt)); + serdev_device_write_buf(bg->sd, data, rem); + + ret = cc1352_bootloader_wait_for_ack(bg); + if (ret < 0) + return ret; + + ret = cc1352_bootloader_get_status(bg); + if (ret < 0) + return ret; + + return rem; +} + static void gb_greybus_deinit(struct gb_beagleplay *bg) { gb_hd_del(bg->gb_hd); @@ -442,6 +857,157 @@ free_gb_hd: return ret; } +static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload, + const u8 *data, u32 size) +{ + int ret; + u32 curr_crc32; + struct gb_beagleplay *bg = fw_upload->dd_handle; + + dev_info(&bg->sd->dev, "CC1352 Start Flashing..."); + + if (size != CC1352_FIRMWARE_SIZE) + return FW_UPLOAD_ERR_INVALID_SIZE; + + /* Might involve network calls */ + gb_greybus_deinit(bg); + msleep(5 * MSEC_PER_SEC); + + gb_beagleplay_stop_svc(bg); + msleep(200); + flush_work(&bg->tx_work); + + serdev_device_wait_until_sent(bg->sd, CC1352_BOOTLOADER_TIMEOUT); + + WRITE_ONCE(bg->flashing_mode, true); + + gpiod_direction_output(bg->bootloader_backdoor_gpio, 0); + gpiod_direction_output(bg->rst_gpio, 0); + msleep(200); + + gpiod_set_value(bg->rst_gpio, 1); + msleep(200); + + gpiod_set_value(bg->bootloader_backdoor_gpio, 1); + msleep(200); + + gpiod_direction_input(bg->bootloader_backdoor_gpio); + gpiod_direction_input(bg->rst_gpio); + + ret = cc1352_bootloader_sync(bg); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to sync"); + + ret = cc1352_bootloader_crc32(bg, &curr_crc32); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to fetch crc32"); + + bg->fwl_crc32 = crc32(0xffffffff, data, size) ^ 0xffffffff; + + /* Check if attempting to reflash same firmware */ + if (bg->fwl_crc32 == curr_crc32) { + dev_warn(&bg->sd->dev, "Skipping reflashing same image"); + cc1352_bootloader_reset(bg); + WRITE_ONCE(bg->flashing_mode, false); + msleep(200); + gb_greybus_init(bg); + gb_beagleplay_start_svc(bg); + return FW_UPLOAD_ERR_FW_INVALID; + } + + ret = cc1352_bootloader_erase(bg); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to erase"); + + bg->fwl_reset_addr = true; + + return FW_UPLOAD_ERR_NONE; +} + +static void cc1352_cleanup(struct fw_upload *fw_upload) +{ + struct gb_beagleplay *bg = fw_upload->dd_handle; + + WRITE_ONCE(bg->flashing_mode, false); +} + +static enum fw_upload_err cc1352_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, u32 size, + u32 *written) +{ + int ret; + size_t empty_bytes; + struct gb_beagleplay *bg = fw_upload->dd_handle; + + /* Skip 0xff packets. Significant performance improvement */ + empty_bytes = cc1352_bootloader_empty_pkt(data + offset, size); + if (empty_bytes >= CC1352_BOOTLOADER_PKT_MAX_SIZE) { + bg->fwl_reset_addr = true; + *written = empty_bytes; + return FW_UPLOAD_ERR_NONE; + } + + if (bg->fwl_reset_addr) { + ret = cc1352_bootloader_download(bg, size, offset); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, + FW_UPLOAD_ERR_HW_ERROR, + "Failed to send download cmd"); + + bg->fwl_reset_addr = false; + } + + ret = cc1352_bootloader_send_data(bg, data + offset, size); + if (ret < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to flash firmware"); + *written = ret; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err cc1352_poll_complete(struct fw_upload *fw_upload) +{ + u32 curr_crc32; + struct gb_beagleplay *bg = fw_upload->dd_handle; + + if (cc1352_bootloader_crc32(bg, &curr_crc32) < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to fetch crc32"); + + if (bg->fwl_crc32 != curr_crc32) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_FW_INVALID, + "Invalid CRC32"); + + if (cc1352_bootloader_reset(bg) < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_HW_ERROR, + "Failed to reset"); + + dev_info(&bg->sd->dev, "CC1352 Flashing Successful"); + WRITE_ONCE(bg->flashing_mode, false); + msleep(200); + + if (gb_greybus_init(bg) < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, + "Failed to initialize greybus"); + + gb_beagleplay_start_svc(bg); + + return FW_UPLOAD_ERR_NONE; +} + +static void cc1352_cancel(struct fw_upload *fw_upload) +{ + struct gb_beagleplay *bg = fw_upload->dd_handle; + + dev_info(&bg->sd->dev, "CC1352 Bootloader Cancel"); + + cc1352_bootloader_reset(bg); +} + static void gb_serdev_deinit(struct gb_beagleplay *bg) { serdev_device_close(bg->sd); @@ -463,6 +1029,65 @@ static int gb_serdev_init(struct gb_beagleplay *bg) return 0; } +static const struct fw_upload_ops cc1352_bootloader_ops = { + .prepare = cc1352_prepare, + .write = cc1352_write, + .poll_complete = cc1352_poll_complete, + .cancel = cc1352_cancel, + .cleanup = cc1352_cleanup +}; + +static int gb_fw_init(struct gb_beagleplay *bg) +{ + int ret; + struct fw_upload *fwl; + struct gpio_desc *desc; + + bg->fwl = NULL; + bg->bootloader_backdoor_gpio = NULL; + bg->rst_gpio = NULL; + bg->flashing_mode = false; + bg->fwl_cmd_response = 0; + bg->fwl_ack = 0; + init_completion(&bg->fwl_ack_com); + init_completion(&bg->fwl_cmd_response_com); + + desc = devm_gpiod_get(&bg->sd->dev, "bootloader-backdoor", GPIOD_IN); + if (IS_ERR(desc)) + return PTR_ERR(desc); + bg->bootloader_backdoor_gpio = desc; + + desc = devm_gpiod_get(&bg->sd->dev, "reset", GPIOD_IN); + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto free_boot; + } + bg->rst_gpio = desc; + + fwl = firmware_upload_register(THIS_MODULE, &bg->sd->dev, "cc1352p7", + &cc1352_bootloader_ops, bg); + if (IS_ERR(fwl)) { + ret = PTR_ERR(fwl); + goto free_reset; + } + bg->fwl = fwl; + + return 0; + +free_reset: + devm_gpiod_put(&bg->sd->dev, bg->rst_gpio); + bg->rst_gpio = NULL; +free_boot: + devm_gpiod_put(&bg->sd->dev, bg->bootloader_backdoor_gpio); + bg->bootloader_backdoor_gpio = NULL; + return ret; +} + +static void gb_fw_deinit(struct gb_beagleplay *bg) +{ + firmware_upload_unregister(bg->fwl); +} + static int gb_beagleplay_probe(struct serdev_device *serdev) { int ret = 0; @@ -481,14 +1106,20 @@ static int gb_beagleplay_probe(struct serdev_device *serdev) if (ret) goto free_serdev; - ret = gb_greybus_init(bg); + ret = gb_fw_init(bg); if (ret) goto free_hdlc; + ret = gb_greybus_init(bg); + if (ret) + goto free_fw; + gb_beagleplay_start_svc(bg); return 0; +free_fw: + gb_fw_deinit(bg); free_hdlc: hdlc_deinit(bg); free_serdev: @@ -500,6 +1131,7 @@ static void gb_beagleplay_remove(struct serdev_device *serdev) { struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev); + gb_fw_deinit(bg); gb_greybus_deinit(bg); gb_beagleplay_stop_svc(bg); hdlc_deinit(bg); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 9fc6f6b863e0..ea38ecf26fcb 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -487,23 +487,25 @@ struct coresight_device *coresight_get_sink(struct list_head *path) return csdev; } +u32 coresight_get_sink_id(struct coresight_device *csdev) +{ + if (!csdev->ea) + return 0; + + /* + * See function etm_perf_add_symlink_sink() to know where + * this comes from. + */ + return (u32) (unsigned long) csdev->ea->var; +} + static int coresight_sink_by_id(struct device *dev, const void *data) { struct coresight_device *csdev = to_coresight_device(dev); - unsigned long hash; if (csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { - - if (!csdev->ea) - return 0; - /* - * See function etm_perf_add_symlink_sink() to know where - * this comes from. - */ - hash = (unsigned long)csdev->ea->var; - - if ((u32)hash == *(u32 *)data) + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { + if (coresight_get_sink_id(csdev) == *(u32 *)data) return 1; } @@ -902,6 +904,7 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev); fwnode_handle_put(csdev->dev.fwnode); + free_percpu(csdev->perf_sink_id_map.cpu_map); kfree(csdev); } @@ -1159,6 +1162,16 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); dev_set_name(&csdev->dev, "%s", desc->name); + if (csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { + spin_lock_init(&csdev->perf_sink_id_map.lock); + csdev->perf_sink_id_map.cpu_map = alloc_percpu(atomic_t); + if (!csdev->perf_sink_id_map.cpu_map) { + kfree(csdev); + ret = -ENOMEM; + goto err_out; + } + } /* * Make sure the device registration and the connection fixup * are synchronised, so that we don't see uninitialised devices diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index ccef04f27f12..d0ae10bf6128 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -416,20 +416,16 @@ static int cti_plat_create_impdef_connections(struct device *dev, struct cti_drvdata *drvdata) { int rc = 0; - struct fwnode_handle *fwnode = dev_fwnode(dev); - struct fwnode_handle *child = NULL; - if (IS_ERR_OR_NULL(fwnode)) + if (IS_ERR_OR_NULL(dev_fwnode(dev))) return -EINVAL; - fwnode_for_each_child_node(fwnode, child) { + device_for_each_child_node_scoped(dev, child) { if (cti_plat_node_name_eq(child, CTI_DT_CONNS)) - rc = cti_plat_create_connection(dev, drvdata, - child); + rc = cti_plat_create_connection(dev, drvdata, child); if (rc != 0) break; } - fwnode_handle_put(child); return rc; } diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index ac70c0b491be..bb85fa663ffc 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -21,8 +21,12 @@ DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source"); DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink"); static int dummy_source_enable(struct coresight_device *csdev, - struct perf_event *event, enum cs_mode mode) + struct perf_event *event, enum cs_mode mode, + __maybe_unused struct coresight_trace_id_map *id_map) { + if (!coresight_take_mode(csdev, mode)) + return -EBUSY; + dev_dbg(csdev->dev.parent, "Dummy source enabled\n"); return 0; @@ -31,6 +35,7 @@ static int dummy_source_enable(struct coresight_device *csdev, static void dummy_source_disable(struct coresight_device *csdev, struct perf_event *event) { + coresight_set_mode(csdev, CS_MODE_DISABLED); dev_dbg(csdev->dev.parent, "Dummy source disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index c0c60e6a1703..ad6a8f4b70b6 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -229,15 +229,24 @@ static void free_event_data(struct work_struct *work) struct list_head **ppath; ppath = etm_event_cpu_path_ptr(event_data, cpu); - if (!(IS_ERR_OR_NULL(*ppath))) + if (!(IS_ERR_OR_NULL(*ppath))) { + struct coresight_device *sink = coresight_get_sink(*ppath); + + /* + * Mark perf event as done for trace id allocator, but don't call + * coresight_trace_id_put_cpu_id_map() on individual IDs. Perf sessions + * never free trace IDs to ensure that the ID associated with a CPU + * cannot change during their and other's concurrent sessions. Instead, + * a refcount is used so that the last event to call + * coresight_trace_id_perf_stop() frees all IDs. + */ + coresight_trace_id_perf_stop(&sink->perf_sink_id_map); + coresight_release_path(*ppath); + } *ppath = NULL; - coresight_trace_id_put_cpu_id(cpu); } - /* mark perf event as done for trace id allocator */ - coresight_trace_id_perf_stop(); - free_percpu(event_data->path); kfree(event_data); } @@ -325,9 +334,6 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, sink = user_sink = coresight_get_sink_by_id(id); } - /* tell the trace ID allocator that a perf event is starting up */ - coresight_trace_id_perf_start(); - /* check if user wants a coresight configuration selected */ cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32); if (cfg_hash) { @@ -401,13 +407,14 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, } /* ensure we can allocate a trace ID for this CPU */ - trace_id = coresight_trace_id_get_cpu_id(cpu); + trace_id = coresight_trace_id_get_cpu_id_map(cpu, &sink->perf_sink_id_map); if (!IS_VALID_CS_TRACE_ID(trace_id)) { cpumask_clear_cpu(cpu, mask); coresight_release_path(path); continue; } + coresight_trace_id_perf_start(&sink->perf_sink_id_map); *etm_event_cpu_path_ptr(event_data, cpu) = path; } @@ -453,6 +460,7 @@ static void etm_event_start(struct perf_event *event, int flags) struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); struct list_head *path; u64 hw_id; + u8 trace_id; if (!csdev) goto fail; @@ -495,7 +503,8 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop; /* Finally enable the tracer */ - if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF)) + if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, + &sink->perf_sink_id_map)) goto fail_disable_path; /* @@ -504,10 +513,16 @@ static void etm_event_start(struct perf_event *event, int flags) */ if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) { cpumask_set_cpu(cpu, &event_data->aux_hwid_done); - hw_id = FIELD_PREP(CS_AUX_HW_ID_VERSION_MASK, - CS_AUX_HW_ID_CURR_VERSION); - hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK, - coresight_trace_id_read_cpu_id(cpu)); + + trace_id = coresight_trace_id_read_cpu_id_map(cpu, &sink->perf_sink_id_map); + + hw_id = FIELD_PREP(CS_AUX_HW_ID_MAJOR_VERSION_MASK, + CS_AUX_HW_ID_MAJOR_VERSION); + hw_id |= FIELD_PREP(CS_AUX_HW_ID_MINOR_VERSION_MASK, + CS_AUX_HW_ID_MINOR_VERSION); + hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK, trace_id); + hw_id |= FIELD_PREP(CS_AUX_HW_ID_SINK_ID_MASK, coresight_get_sink_id(sink)); + perf_report_aux_output_id(event, hw_id); } diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h index bebbadee2ceb..744531158d6b 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -62,7 +62,6 @@ struct etm_event_data { struct list_head * __percpu *path; }; -#if IS_ENABLED(CONFIG_CORESIGHT) int etm_perf_symlink(struct coresight_device *csdev, bool link); int etm_perf_add_symlink_sink(struct coresight_device *csdev); void etm_perf_del_symlink_sink(struct coresight_device *csdev); @@ -77,23 +76,6 @@ static inline void *etm_perf_sink_config(struct perf_output_handle *handle) int etm_perf_add_symlink_cscfg(struct device *dev, struct cscfg_config_desc *config_desc); void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc); -#else -static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) -{ return -EINVAL; } -int etm_perf_add_symlink_sink(struct coresight_device *csdev) -{ return -EINVAL; } -void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} -static inline void *etm_perf_sink_config(struct perf_output_handle *handle) -{ - return NULL; -} -int etm_perf_add_symlink_cscfg(struct device *dev, - struct cscfg_config_desc *config_desc) -{ return -EINVAL; } -void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc) {} - -#endif /* CONFIG_CORESIGHT */ - int __init etm_perf_init(void); void etm_perf_exit(void); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index 8b362605d242..c103f4c70f5d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -481,7 +481,8 @@ void etm_release_trace_id(struct etm_drvdata *drvdata) } static int etm_enable_perf(struct coresight_device *csdev, - struct perf_event *event) + struct perf_event *event, + struct coresight_trace_id_map *id_map) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int trace_id; @@ -500,7 +501,7 @@ static int etm_enable_perf(struct coresight_device *csdev, * with perf locks - we know the ID cannot change until perf shuts down * the session */ - trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu); + trace_id = coresight_trace_id_read_cpu_id_map(drvdata->cpu, id_map); if (!IS_VALID_CS_TRACE_ID(trace_id)) { dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n", dev_name(&drvdata->csdev->dev), drvdata->cpu); @@ -553,7 +554,7 @@ unlock_enable_sysfs: } static int etm_enable(struct coresight_device *csdev, struct perf_event *event, - enum cs_mode mode) + enum cs_mode mode, struct coresight_trace_id_map *id_map) { int ret; struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -568,7 +569,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, ret = etm_enable_sysfs(csdev); break; case CS_MODE_PERF: - ret = etm_enable_perf(csdev, event); + ret = etm_enable_perf(csdev, event, id_map); break; default: ret = -EINVAL; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index bf01f01964cf..66d44a404ad0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -752,7 +752,8 @@ out: } static int etm4_enable_perf(struct coresight_device *csdev, - struct perf_event *event) + struct perf_event *event, + struct coresight_trace_id_map *id_map) { int ret = 0, trace_id; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -775,7 +776,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, * with perf locks - we know the ID cannot change until perf shuts down * the session */ - trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu); + trace_id = coresight_trace_id_read_cpu_id_map(drvdata->cpu, id_map); if (!IS_VALID_CS_TRACE_ID(trace_id)) { dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n", dev_name(&drvdata->csdev->dev), drvdata->cpu); @@ -837,7 +838,7 @@ unlock_sysfs_enable: } static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, - enum cs_mode mode) + enum cs_mode mode, struct coresight_trace_id_map *id_map) { int ret; @@ -851,7 +852,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, ret = etm4_enable_sysfs(csdev); break; case CS_MODE_PERF: - ret = etm4_enable_perf(csdev, event); + ret = etm4_enable_perf(csdev, event, id_map); break; default: ret = -EINVAL; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 61a46d3bdcc8..05f891ca6b5c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -148,6 +148,7 @@ int coresight_make_links(struct coresight_device *orig, struct coresight_device *target); void coresight_remove_links(struct coresight_device *orig, struct coresight_connection *conn); +u32 coresight_get_sink_id(struct coresight_device *csdev); #if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X) extern int etm_readl_cp14(u32 off, unsigned int *val); diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 117dbb484543..cb3e04755c99 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -194,7 +194,8 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) } static int stm_enable(struct coresight_device *csdev, struct perf_event *event, - enum cs_mode mode) + enum cs_mode mode, + __maybe_unused struct coresight_trace_id_map *trace_id) { struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 1e67cc7758d7..a01c9e54e2ed 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include "coresight-priv.h" +#include "coresight-trace-id.h" /* * Use IDR to map the hash of the source's device name @@ -63,7 +64,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, */ lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) { - ret = source_ops(csdev)->enable(csdev, data, mode); + ret = source_ops(csdev)->enable(csdev, data, mode, NULL); if (ret) return ret; } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index e75428fa1592..a48bb85d0e7f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -36,7 +36,8 @@ struct etr_buf_hw { * etr_perf_buffer - Perf buffer used for ETR * @drvdata - The ETR drvdaga this buffer has been allocated for. * @etr_buf - Actual buffer used by the ETR - * @pid - The PID this etr_perf_buffer belongs to. + * @pid - The PID of the session owner that etr_perf_buffer + * belongs to. * @snaphost - Perf session mode * @nr_pages - Number of pages in the ring buffer. * @pages - Array of Pages in the ring buffer. @@ -261,6 +262,7 @@ void tmc_free_sg_table(struct tmc_sg_table *sg_table) { tmc_free_table_pages(sg_table); tmc_free_data_pages(sg_table); + kfree(sg_table); } EXPORT_SYMBOL_GPL(tmc_free_sg_table); @@ -342,7 +344,6 @@ struct tmc_sg_table *tmc_alloc_sg_table(struct device *dev, rc = tmc_alloc_table_pages(sg_table); if (rc) { tmc_free_sg_table(sg_table); - kfree(sg_table); return ERR_PTR(rc); } @@ -1662,7 +1663,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) goto unlock_out; } - /* Get a handle on the pid of the process to monitor */ + /* Get a handle on the pid of the session owner */ pid = etr_perf->pid; /* Do not proceed if this device is associated with another session */ diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index c77763b49de0..2671926be62a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -171,8 +171,9 @@ struct etr_buf { * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.tmc" entry. * @spinlock: only one at a time pls. - * @pid: Process ID of the process being monitored by the session - * that is using this component. + * @pid: Process ID of the process that owns the session that is using + * this component. For example this would be the pid of the Perf + * process. * @buf: Snapshot of the trace data for ETF/ETB. * @etr_buf: details of buffer used in TMC-ETR * @len: size of the available trace for ETF/ETB. diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 0726f8842552..b7d99e91ab84 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -439,7 +439,8 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata) } static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, - enum cs_mode mode) + enum cs_mode mode, + __maybe_unused struct coresight_trace_id_map *id_map) { struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -449,6 +450,11 @@ static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, return -EBUSY; } + if (!coresight_take_mode(csdev, mode)) { + spin_unlock(&drvdata->spinlock); + return -EBUSY; + } + __tpdm_enable(drvdata); drvdata->enable = true; spin_unlock(&drvdata->spinlock); @@ -506,6 +512,7 @@ static void tpdm_disable(struct coresight_device *csdev, } __tpdm_disable(drvdata); + coresight_set_mode(csdev, CS_MODE_DISABLED); drvdata->enable = false; spin_unlock(&drvdata->spinlock); diff --git a/drivers/hwtracing/coresight/coresight-trace-id.c b/drivers/hwtracing/coresight/coresight-trace-id.c index af5b4ef59cea..d98e12cb30ec 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.c +++ b/drivers/hwtracing/coresight/coresight-trace-id.c @@ -3,6 +3,7 @@ * Copyright (c) 2022, Linaro Limited, All rights reserved. * Author: Mike Leach <mike.leach@linaro.org> */ +#include <linux/coresight.h> #include <linux/coresight-pmu.h> #include <linux/cpumask.h> #include <linux/kernel.h> @@ -11,18 +12,12 @@ #include "coresight-trace-id.h" -/* Default trace ID map. Used on systems that don't require per sink mappings */ -static struct coresight_trace_id_map id_map_default; - -/* maintain a record of the mapping of IDs and pending releases per cpu */ -static DEFINE_PER_CPU(atomic_t, cpu_id) = ATOMIC_INIT(0); -static cpumask_t cpu_id_release_pending; - -/* perf session active counter */ -static atomic_t perf_cs_etm_session_active = ATOMIC_INIT(0); - -/* lock to protect id_map and cpu data */ -static DEFINE_SPINLOCK(id_map_lock); +/* Default trace ID map. Used in sysfs mode and for system sources */ +static DEFINE_PER_CPU(atomic_t, id_map_default_cpu_ids) = ATOMIC_INIT(0); +static struct coresight_trace_id_map id_map_default = { + .cpu_map = &id_map_default_cpu_ids, + .lock = __SPIN_LOCK_UNLOCKED(id_map_default.lock) +}; /* #define TRACE_ID_DEBUG 1 */ #if defined(TRACE_ID_DEBUG) || defined(CONFIG_COMPILE_TEST) @@ -32,7 +27,6 @@ static void coresight_trace_id_dump_table(struct coresight_trace_id_map *id_map, { pr_debug("%s id_map::\n", func_name); pr_debug("Used = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->used_ids); - pr_debug("Pend = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->pend_rel_ids); } #define DUMP_ID_MAP(map) coresight_trace_id_dump_table(map, __func__) #define DUMP_ID_CPU(cpu, id) pr_debug("%s called; cpu=%d, id=%d\n", __func__, cpu, id) @@ -46,9 +40,9 @@ static void coresight_trace_id_dump_table(struct coresight_trace_id_map *id_map, #endif /* unlocked read of current trace ID value for given CPU */ -static int _coresight_trace_id_read_cpu_id(int cpu) +static int _coresight_trace_id_read_cpu_id(int cpu, struct coresight_trace_id_map *id_map) { - return atomic_read(&per_cpu(cpu_id, cpu)); + return atomic_read(per_cpu_ptr(id_map->cpu_map, cpu)); } /* look for next available odd ID, return 0 if none found */ @@ -119,49 +113,33 @@ static void coresight_trace_id_free(int id, struct coresight_trace_id_map *id_ma clear_bit(id, id_map->used_ids); } -static void coresight_trace_id_set_pend_rel(int id, struct coresight_trace_id_map *id_map) -{ - if (WARN(!IS_VALID_CS_TRACE_ID(id), "Invalid Trace ID %d\n", id)) - return; - set_bit(id, id_map->pend_rel_ids); -} - /* - * release all pending IDs for all current maps & clear CPU associations - * - * This currently operates on the default id map, but may be extended to - * operate on all registered id maps if per sink id maps are used. + * Release all IDs and clear CPU associations. */ -static void coresight_trace_id_release_all_pending(void) +static void coresight_trace_id_release_all(struct coresight_trace_id_map *id_map) { - struct coresight_trace_id_map *id_map = &id_map_default; unsigned long flags; - int cpu, bit; + int cpu; - spin_lock_irqsave(&id_map_lock, flags); - for_each_set_bit(bit, id_map->pend_rel_ids, CORESIGHT_TRACE_ID_RES_TOP) { - clear_bit(bit, id_map->used_ids); - clear_bit(bit, id_map->pend_rel_ids); - } - for_each_cpu(cpu, &cpu_id_release_pending) { - atomic_set(&per_cpu(cpu_id, cpu), 0); - cpumask_clear_cpu(cpu, &cpu_id_release_pending); - } - spin_unlock_irqrestore(&id_map_lock, flags); + spin_lock_irqsave(&id_map->lock, flags); + bitmap_zero(id_map->used_ids, CORESIGHT_TRACE_IDS_MAX); + for_each_possible_cpu(cpu) + atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), 0); + spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID_MAP(id_map); } -static int coresight_trace_id_map_get_cpu_id(int cpu, struct coresight_trace_id_map *id_map) +static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map *id_map) { unsigned long flags; int id; - spin_lock_irqsave(&id_map_lock, flags); + spin_lock_irqsave(&id_map->lock, flags); /* check for existing allocation for this CPU */ - id = _coresight_trace_id_read_cpu_id(cpu); + id = _coresight_trace_id_read_cpu_id(cpu, id_map); if (id) - goto get_cpu_id_clr_pend; + goto get_cpu_id_out_unlock; /* * Find a new ID. @@ -180,44 +158,32 @@ static int coresight_trace_id_map_get_cpu_id(int cpu, struct coresight_trace_id_ goto get_cpu_id_out_unlock; /* allocate the new id to the cpu */ - atomic_set(&per_cpu(cpu_id, cpu), id); - -get_cpu_id_clr_pend: - /* we are (re)using this ID - so ensure it is not marked for release */ - cpumask_clear_cpu(cpu, &cpu_id_release_pending); - clear_bit(id, id_map->pend_rel_ids); + atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), id); get_cpu_id_out_unlock: - spin_unlock_irqrestore(&id_map_lock, flags); + spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID_CPU(cpu, id); DUMP_ID_MAP(id_map); return id; } -static void coresight_trace_id_map_put_cpu_id(int cpu, struct coresight_trace_id_map *id_map) +static void _coresight_trace_id_put_cpu_id(int cpu, struct coresight_trace_id_map *id_map) { unsigned long flags; int id; /* check for existing allocation for this CPU */ - id = _coresight_trace_id_read_cpu_id(cpu); + id = _coresight_trace_id_read_cpu_id(cpu, id_map); if (!id) return; - spin_lock_irqsave(&id_map_lock, flags); + spin_lock_irqsave(&id_map->lock, flags); - if (atomic_read(&perf_cs_etm_session_active)) { - /* set release at pending if perf still active */ - coresight_trace_id_set_pend_rel(id, id_map); - cpumask_set_cpu(cpu, &cpu_id_release_pending); - } else { - /* otherwise clear id */ - coresight_trace_id_free(id, id_map); - atomic_set(&per_cpu(cpu_id, cpu), 0); - } + coresight_trace_id_free(id, id_map); + atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), 0); - spin_unlock_irqrestore(&id_map_lock, flags); + spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID_CPU(cpu, id); DUMP_ID_MAP(id_map); } @@ -227,10 +193,10 @@ static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *i unsigned long flags; int id; - spin_lock_irqsave(&id_map_lock, flags); + spin_lock_irqsave(&id_map->lock, flags); /* prefer odd IDs for system components to avoid legacy CPU IDS */ id = coresight_trace_id_alloc_new_id(id_map, 0, true); - spin_unlock_irqrestore(&id_map_lock, flags); + spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID(id); DUMP_ID_MAP(id_map); @@ -241,9 +207,9 @@ static void coresight_trace_id_map_put_system_id(struct coresight_trace_id_map * { unsigned long flags; - spin_lock_irqsave(&id_map_lock, flags); + spin_lock_irqsave(&id_map->lock, flags); coresight_trace_id_free(id, id_map); - spin_unlock_irqrestore(&id_map_lock, flags); + spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID(id); DUMP_ID_MAP(id_map); @@ -253,22 +219,40 @@ static void coresight_trace_id_map_put_system_id(struct coresight_trace_id_map * int coresight_trace_id_get_cpu_id(int cpu) { - return coresight_trace_id_map_get_cpu_id(cpu, &id_map_default); + return _coresight_trace_id_get_cpu_id(cpu, &id_map_default); } EXPORT_SYMBOL_GPL(coresight_trace_id_get_cpu_id); +int coresight_trace_id_get_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map) +{ + return _coresight_trace_id_get_cpu_id(cpu, id_map); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_get_cpu_id_map); + void coresight_trace_id_put_cpu_id(int cpu) { - coresight_trace_id_map_put_cpu_id(cpu, &id_map_default); + _coresight_trace_id_put_cpu_id(cpu, &id_map_default); } EXPORT_SYMBOL_GPL(coresight_trace_id_put_cpu_id); +void coresight_trace_id_put_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map) +{ + _coresight_trace_id_put_cpu_id(cpu, id_map); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_put_cpu_id_map); + int coresight_trace_id_read_cpu_id(int cpu) { - return _coresight_trace_id_read_cpu_id(cpu); + return _coresight_trace_id_read_cpu_id(cpu, &id_map_default); } EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id); +int coresight_trace_id_read_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map) +{ + return _coresight_trace_id_read_cpu_id(cpu, id_map); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id_map); + int coresight_trace_id_get_system_id(void) { return coresight_trace_id_map_get_system_id(&id_map_default); @@ -281,17 +265,17 @@ void coresight_trace_id_put_system_id(int id) } EXPORT_SYMBOL_GPL(coresight_trace_id_put_system_id); -void coresight_trace_id_perf_start(void) +void coresight_trace_id_perf_start(struct coresight_trace_id_map *id_map) { - atomic_inc(&perf_cs_etm_session_active); - PERF_SESSION(atomic_read(&perf_cs_etm_session_active)); + atomic_inc(&id_map->perf_cs_etm_session_active); + PERF_SESSION(atomic_read(&id_map->perf_cs_etm_session_active)); } EXPORT_SYMBOL_GPL(coresight_trace_id_perf_start); -void coresight_trace_id_perf_stop(void) +void coresight_trace_id_perf_stop(struct coresight_trace_id_map *id_map) { - if (!atomic_dec_return(&perf_cs_etm_session_active)) - coresight_trace_id_release_all_pending(); - PERF_SESSION(atomic_read(&perf_cs_etm_session_active)); + if (!atomic_dec_return(&id_map->perf_cs_etm_session_active)) + coresight_trace_id_release_all(id_map); + PERF_SESSION(atomic_read(&id_map->perf_cs_etm_session_active)); } EXPORT_SYMBOL_GPL(coresight_trace_id_perf_stop); diff --git a/drivers/hwtracing/coresight/coresight-trace-id.h b/drivers/hwtracing/coresight/coresight-trace-id.h index 3797777d367e..9aae50a553ca 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.h +++ b/drivers/hwtracing/coresight/coresight-trace-id.h @@ -17,9 +17,10 @@ * released when done. * * In order to ensure that a consistent cpu / ID matching is maintained - * throughout a perf cs_etm event session - a session in progress flag will - * be maintained, and released IDs not cleared until the perf session is - * complete. This allows the same CPU to be re-allocated its prior ID. + * throughout a perf cs_etm event session - a session in progress flag will be + * maintained for each sink, and IDs are cleared when all the perf sessions + * complete. This allows the same CPU to be re-allocated its prior ID when + * events are scheduled in and out. * * * Trace ID maps will be created and initialised to prevent architecturally @@ -32,10 +33,6 @@ #include <linux/bitops.h> #include <linux/types.h> - -/* architecturally we have 128 IDs some of which are reserved */ -#define CORESIGHT_TRACE_IDS_MAX 128 - /* ID 0 is reserved */ #define CORESIGHT_TRACE_ID_RES_0 0 @@ -47,23 +44,6 @@ ((id > CORESIGHT_TRACE_ID_RES_0) && (id < CORESIGHT_TRACE_ID_RES_TOP)) /** - * Trace ID map. - * - * @used_ids: Bitmap to register available (bit = 0) and in use (bit = 1) IDs. - * Initialised so that the reserved IDs are permanently marked as - * in use. - * @pend_rel_ids: CPU IDs that have been released by the trace source but not - * yet marked as available, to allow re-allocation to the same - * CPU during a perf session. - */ -struct coresight_trace_id_map { - DECLARE_BITMAP(used_ids, CORESIGHT_TRACE_IDS_MAX); - DECLARE_BITMAP(pend_rel_ids, CORESIGHT_TRACE_IDS_MAX); -}; - -/* Allocate and release IDs for a single default trace ID map */ - -/** * Read and optionally allocate a CoreSight trace ID and associate with a CPU. * * Function will read the current trace ID for the associated CPU, @@ -79,19 +59,27 @@ struct coresight_trace_id_map { int coresight_trace_id_get_cpu_id(int cpu); /** + * Version of coresight_trace_id_get_cpu_id() that allows the ID map to operate + * on to be provided. + */ +int coresight_trace_id_get_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map); + +/** * Release an allocated trace ID associated with the CPU. * - * This will release the CoreSight trace ID associated with the CPU, - * unless a perf session is in operation. - * - * If a perf session is in operation then the ID will be marked as pending - * release. + * This will release the CoreSight trace ID associated with the CPU. * * @cpu: The CPU index to release the associated trace ID. */ void coresight_trace_id_put_cpu_id(int cpu); /** + * Version of coresight_trace_id_put_cpu_id() that allows the ID map to operate + * on to be provided. + */ +void coresight_trace_id_put_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map); + +/** * Read the current allocated CoreSight Trace ID value for the CPU. * * Fast read of the current value that does not allocate if no ID allocated @@ -112,6 +100,12 @@ void coresight_trace_id_put_cpu_id(int cpu); int coresight_trace_id_read_cpu_id(int cpu); /** + * Version of coresight_trace_id_read_cpu_id() that allows the ID map to operate + * on to be provided. + */ +int coresight_trace_id_read_cpu_id_map(int cpu, struct coresight_trace_id_map *id_map); + +/** * Allocate a CoreSight trace ID for a system component. * * Unconditionally allocates a Trace ID, without associating the ID with a CPU. @@ -136,21 +130,21 @@ void coresight_trace_id_put_system_id(int id); /** * Notify the Trace ID allocator that a perf session is starting. * - * Increase the perf session reference count - called by perf when setting up - * a trace event. + * Increase the perf session reference count - called by perf when setting up a + * trace event. * - * This reference count is used by the ID allocator to ensure that trace IDs - * associated with a CPU cannot change or be released during a perf session. + * Perf sessions never free trace IDs to ensure that the ID associated with a + * CPU cannot change during their and other's concurrent sessions. Instead, + * this refcount is used so that the last event to finish always frees all IDs. */ -void coresight_trace_id_perf_start(void); +void coresight_trace_id_perf_start(struct coresight_trace_id_map *id_map); /** * Notify the ID allocator that a perf session is stopping. * - * Decrease the perf session reference count. - * if this causes the count to go to zero, then all Trace IDs marked as pending - * release, will be released. + * Decrease the perf session reference count. If this causes the count to go to + * zero, then all Trace IDs will be released. */ -void coresight_trace_id_perf_stop(void); +void coresight_trace_id_perf_stop(struct coresight_trace_id_map *id_map); #endif /* _CORESIGHT_TRACE_ID_H */ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 80b57d3ee3a7..516c1a8e4d56 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -177,6 +177,33 @@ config ADXL372_I2C To compile this driver as a module, choose M here: the module will be called adxl372_i2c. +config ADXL380 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config ADXL380_SPI + tristate "Analog Devices ADXL380 3-Axis Accelerometer SPI Driver" + depends on SPI + select ADXL380 + select REGMAP_SPI + help + Say yes here to add support for the Analog Devices ADXL380 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl380_spi. + +config ADXL380_I2C + tristate "Analog Devices ADXL380 3-Axis Accelerometer I2C Driver" + depends on I2C + select ADXL380 + select REGMAP_I2C + help + Say yes here to add support for the Analog Devices ADXL380 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl380_i2c. + config BMA180 tristate "Bosch BMA023/BMA1x0/BMA250 3-Axis Accelerometer Driver" depends on I2C && INPUT_BMA150=n diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index db90532ba24a..ca8569e25aba 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -21,6 +21,9 @@ obj-$(CONFIG_ADXL367_SPI) += adxl367_spi.o obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o +obj-$(CONFIG_ADXL380) += adxl380.o +obj-$(CONFIG_ADXL380_I2C) += adxl380_i2c.o +obj-$(CONFIG_ADXL380_SPI) += adxl380_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMA400) += bma400_core.o diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 5cf4828a5eb5..1c046e96aef9 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1220,7 +1220,7 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev, return ret; st->fifo_set_size = bitmap_weight(active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); return 0; } diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c index 118c894015a5..b70117265791 100644 --- a/drivers/iio/accel/adxl367_spi.c +++ b/drivers/iio/accel/adxl367_spi.c @@ -72,7 +72,7 @@ static int adxl367_write(void *context, const void *val_buf, size_t val_size) return spi_sync(st->spi, &st->reg_write_msg); } -static struct regmap_bus adxl367_spi_regmap_bus = { +static const struct regmap_bus adxl367_spi_regmap_bus = { .read = adxl367_read, .write = adxl367_write, }; diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index c4193286eb05..ef8dd557877b 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1050,7 +1050,7 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits; st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); /* Configure the FIFO to store sets of impact event peak. */ if (st->peak_fifo_mode_en) { diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c new file mode 100644 index 000000000000..98863e22bb6b --- /dev/null +++ b/drivers/iio/accel/adxl380.c @@ -0,0 +1,1905 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL380 3-Axis Digital Accelerometer core driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> + +#include <linux/regulator/consumer.h> + +#include "adxl380.h" + +#define ADXL380_ID_VAL 380 +#define ADXL382_ID_VAL 382 + +#define ADXL380_DEVID_AD_REG 0x00 +#define ADLX380_PART_ID_REG 0x02 + +#define ADXL380_X_DATA_H_REG 0x15 +#define ADXL380_Y_DATA_H_REG 0x17 +#define ADXL380_Z_DATA_H_REG 0x19 +#define ADXL380_T_DATA_H_REG 0x1B + +#define ADXL380_MISC_0_REG 0x20 +#define ADXL380_XL382_MSK BIT(7) + +#define ADXL380_MISC_1_REG 0x21 + +#define ADXL380_X_DSM_OFFSET_REG 0x4D + +#define ADXL380_ACT_INACT_CTL_REG 0x37 +#define ADXL380_INACT_EN_MSK BIT(2) +#define ADXL380_ACT_EN_MSK BIT(0) + +#define ADXL380_SNSR_AXIS_EN_REG 0x38 +#define ADXL380_ACT_INACT_AXIS_EN_MSK GENMASK(2, 0) + +#define ADXL380_THRESH_ACT_H_REG 0x39 +#define ADXL380_TIME_ACT_H_REG 0x3B +#define ADXL380_THRESH_INACT_H_REG 0x3E +#define ADXL380_TIME_INACT_H_REG 0x40 +#define ADXL380_THRESH_MAX GENMASK(12, 0) +#define ADXL380_TIME_MAX GENMASK(24, 0) + +#define ADXL380_FIFO_CONFIG_0_REG 0x30 +#define ADXL380_FIFO_SAMPLES_8_MSK BIT(0) +#define ADXL380_FIFO_MODE_MSK GENMASK(5, 4) + +#define ADXL380_FIFO_DISABLED 0 +#define ADXL380_FIFO_NORMAL 1 +#define ADXL380_FIFO_STREAMED 2 +#define ADXL380_FIFO_TRIGGERED 3 + +#define ADXL380_FIFO_CONFIG_1_REG 0x31 +#define ADXL380_FIFO_STATUS_0_REG 0x1E + +#define ADXL380_TAP_THRESH_REG 0x43 +#define ADXL380_TAP_DUR_REG 0x44 +#define ADXL380_TAP_LATENT_REG 0x45 +#define ADXL380_TAP_WINDOW_REG 0x46 +#define ADXL380_TAP_TIME_MAX GENMASK(7, 0) + +#define ADXL380_TAP_CFG_REG 0x47 +#define ADXL380_TAP_AXIS_MSK GENMASK(1, 0) + +#define ADXL380_TRIG_CFG_REG 0x49 +#define ADXL380_TRIG_CFG_DEC_2X_MSK BIT(7) +#define ADXL380_TRIG_CFG_SINC_RATE_MSK BIT(6) + +#define ADXL380_FILTER_REG 0x50 +#define ADXL380_FILTER_EQ_FILT_MSK BIT(6) +#define ADXL380_FILTER_LPF_MODE_MSK GENMASK(5, 4) +#define ADXL380_FILTER_HPF_PATH_MSK BIT(3) +#define ADXL380_FILTER_HPF_CORNER_MSK GENMASK(2, 0) + +#define ADXL380_OP_MODE_REG 0x26 +#define ADXL380_OP_MODE_RANGE_MSK GENMASK(7, 6) +#define ADXL380_OP_MODE_MSK GENMASK(3, 0) +#define ADXL380_OP_MODE_STANDBY 0 +#define ADXL380_OP_MODE_HEART_SOUND 1 +#define ADXL380_OP_MODE_ULP 2 +#define ADXL380_OP_MODE_VLP 3 +#define ADXL380_OP_MODE_LP 4 +#define ADXL380_OP_MODE_LP_ULP 6 +#define ADXL380_OP_MODE_LP_VLP 7 +#define ADXL380_OP_MODE_RBW 8 +#define ADXL380_OP_MODE_RBW_ULP 10 +#define ADXL380_OP_MODE_RBW_VLP 11 +#define ADXL380_OP_MODE_HP 12 +#define ADXL380_OP_MODE_HP_ULP 14 +#define ADXL380_OP_MODE_HP_VLP 15 + +#define ADXL380_OP_MODE_4G_RANGE 0 +#define ADXL382_OP_MODE_15G_RANGE 0 +#define ADXL380_OP_MODE_8G_RANGE 1 +#define ADXL382_OP_MODE_30G_RANGE 1 +#define ADXL380_OP_MODE_16G_RANGE 2 +#define ADXL382_OP_MODE_60G_RANGE 2 + +#define ADXL380_DIG_EN_REG 0x27 +#define ADXL380_CHAN_EN_MSK(chan) BIT(4 + (chan)) +#define ADXL380_FIFO_EN_MSK BIT(3) + +#define ADXL380_INT0_MAP0_REG 0x2B +#define ADXL380_INT1_MAP0_REG 0x2D +#define ADXL380_INT_MAP0_INACT_INT0_MSK BIT(6) +#define ADXL380_INT_MAP0_ACT_INT0_MSK BIT(5) +#define ADXL380_INT_MAP0_FIFO_WM_INT0_MSK BIT(3) + +#define ADXL380_INT0_MAP1_REG 0x2C +#define ADXL380_INT1_MAP1_REG 0x2E +#define ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK BIT(1) +#define ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK BIT(0) + +#define ADXL380_INT0_REG 0x5D +#define ADXL380_INT0_POL_MSK BIT(7) + +#define ADXL380_RESET_REG 0x2A +#define ADXL380_FIFO_DATA 0x1D + +#define ADXL380_DEVID_AD_VAL 0xAD +#define ADXL380_RESET_CODE 0x52 + +#define ADXL380_STATUS_0_REG 0x11 +#define ADXL380_STATUS_0_FIFO_FULL_MSK BIT(1) +#define ADXL380_STATUS_0_FIFO_WM_MSK BIT(3) + +#define ADXL380_STATUS_1_INACT_MSK BIT(6) +#define ADXL380_STATUS_1_ACT_MSK BIT(5) +#define ADXL380_STATUS_1_DOUBLE_TAP_MSK BIT(1) +#define ADXL380_STATUS_1_SINGLE_TAP_MSK BIT(0) + +#define ADXL380_FIFO_SAMPLES 315UL + +enum adxl380_channels { + ADXL380_ACCEL_X, + ADXL380_ACCEL_Y, + ADXL380_ACCEL_Z, + ADXL380_TEMP, + ADXL380_CH_NUM +}; + +enum adxl380_axis { + ADXL380_X_AXIS, + ADXL380_Y_AXIS, + ADXL380_Z_AXIS, +}; + +enum adxl380_activity_type { + ADXL380_ACTIVITY, + ADXL380_INACTIVITY, +}; + +enum adxl380_tap_type { + ADXL380_SINGLE_TAP, + ADXL380_DOUBLE_TAP, +}; + +enum adxl380_tap_time_type { + ADXL380_TAP_TIME_LATENT, + ADXL380_TAP_TIME_WINDOW, +}; + +static const int adxl380_range_scale_factor_tbl[] = { 1, 2, 4 }; + +const struct adxl380_chip_info adxl380_chip_info = { + .name = "adxl380", + .chip_id = ADXL380_ID_VAL, + .scale_tbl = { + [ADXL380_OP_MODE_4G_RANGE] = { 0, 1307226 }, + [ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 }, + [ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 }, + }, + .samp_freq_tbl = { 8000, 16000, 32000 }, + /* + * The datasheet defines an intercept of 470 LSB at 25 degC + * and a sensitivity of 10.2 LSB/C. + */ + .temp_offset = 25 * 102 / 10 - 470, + +}; +EXPORT_SYMBOL_NS_GPL(adxl380_chip_info, IIO_ADXL380); + +const struct adxl380_chip_info adxl382_chip_info = { + .name = "adxl382", + .chip_id = ADXL382_ID_VAL, + .scale_tbl = { + [ADXL382_OP_MODE_15G_RANGE] = { 0, 4903325 }, + [ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 }, + [ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 }, + }, + .samp_freq_tbl = { 16000, 32000, 64000 }, + /* + * The datasheet defines an intercept of 570 LSB at 25 degC + * and a sensitivity of 10.2 LSB/C. + */ + .temp_offset = 25 * 102 / 10 - 570, +}; +EXPORT_SYMBOL_NS_GPL(adxl382_chip_info, IIO_ADXL380); + +static const unsigned int adxl380_th_reg_high_addr[2] = { + [ADXL380_ACTIVITY] = ADXL380_THRESH_ACT_H_REG, + [ADXL380_INACTIVITY] = ADXL380_THRESH_INACT_H_REG, +}; + +static const unsigned int adxl380_time_reg_high_addr[2] = { + [ADXL380_ACTIVITY] = ADXL380_TIME_ACT_H_REG, + [ADXL380_INACTIVITY] = ADXL380_TIME_INACT_H_REG, +}; + +static const unsigned int adxl380_tap_time_reg[2] = { + [ADXL380_TAP_TIME_LATENT] = ADXL380_TAP_LATENT_REG, + [ADXL380_TAP_TIME_WINDOW] = ADXL380_TAP_WINDOW_REG, +}; + +struct adxl380_state { + struct regmap *regmap; + struct device *dev; + const struct adxl380_chip_info *chip_info; + /* + * Synchronize access to members of driver state, and ensure atomicity + * of consecutive regmap operations. + */ + struct mutex lock; + enum adxl380_axis tap_axis_en; + u8 range; + u8 odr; + u8 fifo_set_size; + u8 transf_buf[3]; + u16 watermark; + u32 act_time_ms; + u32 act_threshold; + u32 inact_time_ms; + u32 inact_threshold; + u32 tap_latent_us; + u32 tap_window_us; + u32 tap_duration_us; + u32 tap_threshold; + int irq; + int int_map[2]; + int lpf_tbl[4]; + int hpf_tbl[7][2]; + + __be16 fifo_buf[ADXL380_FIFO_SAMPLES] __aligned(IIO_DMA_MINALIGN); +}; + +bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return reg == ADXL380_FIFO_DATA; +} +EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, IIO_ADXL380); + +static int adxl380_set_measure_en(struct adxl380_state *st, bool en) +{ + int ret; + unsigned int act_inact_ctl; + u8 op_mode = ADXL380_OP_MODE_STANDBY; + + if (en) { + ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl); + if (ret) + return ret; + + /* Activity/ Inactivity detection available only in VLP/ULP mode */ + if (FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) || + FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl)) + op_mode = ADXL380_OP_MODE_VLP; + else + op_mode = ADXL380_OP_MODE_HP; + } + + return regmap_update_bits(st->regmap, ADXL380_OP_MODE_REG, + ADXL380_OP_MODE_MSK, + FIELD_PREP(ADXL380_OP_MODE_MSK, op_mode)); +} + +static void adxl380_scale_act_inact_thresholds(struct adxl380_state *st, + u8 old_range, + u8 new_range) +{ + st->act_threshold = mult_frac(st->act_threshold, + adxl380_range_scale_factor_tbl[old_range], + adxl380_range_scale_factor_tbl[new_range]); + st->inact_threshold = mult_frac(st->inact_threshold, + adxl380_range_scale_factor_tbl[old_range], + adxl380_range_scale_factor_tbl[new_range]); +} + +static int adxl380_write_act_inact_threshold(struct adxl380_state *st, + enum adxl380_activity_type act, + unsigned int th) +{ + int ret; + u8 reg = adxl380_th_reg_high_addr[act]; + + if (th > ADXL380_THRESH_MAX) + return -EINVAL; + + ret = regmap_write(st->regmap, reg + 1, th & GENMASK(7, 0)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, reg, GENMASK(2, 0), th >> 8); + if (ret) + return ret; + + if (act == ADXL380_ACTIVITY) + st->act_threshold = th; + else + st->inact_threshold = th; + + return 0; +} + +static int adxl380_set_act_inact_threshold(struct iio_dev *indio_dev, + enum adxl380_activity_type act, + u16 th) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = adxl380_write_act_inact_threshold(st, act, th); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_set_tap_threshold_value(struct iio_dev *indio_dev, u8 th) +{ + int ret; + struct adxl380_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADXL380_TAP_THRESH_REG, th); + if (ret) + return ret; + + st->tap_threshold = th; + + return adxl380_set_measure_en(st, true); +} + +static int _adxl380_write_tap_time_us(struct adxl380_state *st, + enum adxl380_tap_time_type tap_time_type, + u32 us) +{ + u8 reg = adxl380_tap_time_reg[tap_time_type]; + unsigned int reg_val; + int ret; + + /* scale factor for tap window is 1250us / LSB */ + reg_val = DIV_ROUND_CLOSEST(us, 1250); + if (reg_val > ADXL380_TAP_TIME_MAX) + reg_val = ADXL380_TAP_TIME_MAX; + + ret = regmap_write(st->regmap, reg, reg_val); + if (ret) + return ret; + + if (tap_time_type == ADXL380_TAP_TIME_WINDOW) + st->tap_window_us = us; + else + st->tap_latent_us = us; + + return 0; +} + +static int adxl380_write_tap_time_us(struct adxl380_state *st, + enum adxl380_tap_time_type tap_time_type, u32 us) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = _adxl380_write_tap_time_us(st, tap_time_type, us); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_write_tap_dur_us(struct iio_dev *indio_dev, u32 us) +{ + int ret; + unsigned int reg_val; + struct adxl380_state *st = iio_priv(indio_dev); + + /* 625us per code is the scale factor of TAP_DUR register */ + reg_val = DIV_ROUND_CLOSEST(us, 625); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADXL380_TAP_DUR_REG, reg_val); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_read_chn(struct adxl380_state *st, u8 addr) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_bulk_read(st->regmap, addr, &st->transf_buf, 2); + if (ret) + return ret; + + return get_unaligned_be16(st->transf_buf); +} + +static int adxl380_get_odr(struct adxl380_state *st, int *odr) +{ + int ret; + unsigned int trig_cfg, odr_idx; + + ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg); + if (ret) + return ret; + + odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) | + (FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1); + + *odr = st->chip_info->samp_freq_tbl[odr_idx]; + + return 0; +} + +static const int adxl380_lpf_div[] = { + 1, 4, 8, 16, +}; + +static int adxl380_fill_lpf_tbl(struct adxl380_state *st) +{ + int ret, i; + int odr; + + ret = adxl380_get_odr(st, &odr); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(st->lpf_tbl); i++) + st->lpf_tbl[i] = DIV_ROUND_CLOSEST(odr, adxl380_lpf_div[i]); + + return 0; +} + +static const int adxl380_hpf_mul[] = { + 0, 247000, 62084, 15545, 3862, 954, 238, +}; + +static int adxl380_fill_hpf_tbl(struct adxl380_state *st) +{ + int i, ret, odr_hz; + u32 multiplier; + u64 div, rem, odr; + + ret = adxl380_get_odr(st, &odr_hz); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(adxl380_hpf_mul); i++) { + odr = mul_u64_u32_shr(odr_hz, MEGA, 0); + multiplier = adxl380_hpf_mul[i]; + div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0), + TERA * 100, &rem); + + st->hpf_tbl[i][0] = div; + st->hpf_tbl[i][1] = div_u64(rem, MEGA * 100); + } + + return 0; +} + +static int adxl380_set_odr(struct adxl380_state *st, u8 odr) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, + ADXL380_TRIG_CFG_DEC_2X_MSK, + FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, + ADXL380_TRIG_CFG_SINC_RATE_MSK, + FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1)); + if (ret) + return ret; + + ret = adxl380_set_measure_en(st, true); + if (ret) + return ret; + + ret = adxl380_fill_lpf_tbl(st); + if (ret) + return ret; + + return adxl380_fill_hpf_tbl(st); +} + +static int adxl380_find_match_1d_tbl(const int *array, unsigned int size, + int val) +{ + int i; + + for (i = 0; i < size; i++) { + if (val == array[i]) + return i; + } + + return size - 1; +} + +static int adxl380_find_match_2d_tbl(const int (*freq_tbl)[2], int n, int val, int val2) +{ + int i; + + for (i = 0; i < n; i++) { + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) + return i; + } + + return -EINVAL; +} + +static int adxl380_get_lpf(struct adxl380_state *st, int *lpf) +{ + int ret; + unsigned int trig_cfg, lpf_idx; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, ADXL380_FILTER_REG, &trig_cfg); + if (ret) + return ret; + + lpf_idx = FIELD_GET(ADXL380_FILTER_LPF_MODE_MSK, trig_cfg); + + *lpf = st->lpf_tbl[lpf_idx]; + + return 0; +} + +static int adxl380_set_lpf(struct adxl380_state *st, u8 lpf) +{ + int ret; + u8 eq_bypass = 0; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + if (lpf) + eq_bypass = 1; + + ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG, + ADXL380_FILTER_EQ_FILT_MSK, + FIELD_PREP(ADXL380_FILTER_EQ_FILT_MSK, eq_bypass)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG, + ADXL380_FILTER_LPF_MODE_MSK, + FIELD_PREP(ADXL380_FILTER_LPF_MODE_MSK, lpf)); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_get_hpf(struct adxl380_state *st, int *hpf_int, int *hpf_frac) +{ + int ret; + unsigned int trig_cfg, hpf_idx; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, ADXL380_FILTER_REG, &trig_cfg); + if (ret) + return ret; + + hpf_idx = FIELD_GET(ADXL380_FILTER_HPF_CORNER_MSK, trig_cfg); + + *hpf_int = st->hpf_tbl[hpf_idx][0]; + *hpf_frac = st->hpf_tbl[hpf_idx][1]; + + return 0; +} + +static int adxl380_set_hpf(struct adxl380_state *st, u8 hpf) +{ + int ret; + u8 hpf_path = 0; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + if (hpf) + hpf_path = 1; + + ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG, + ADXL380_FILTER_HPF_PATH_MSK, + FIELD_PREP(ADXL380_FILTER_HPF_PATH_MSK, hpf_path)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG, + ADXL380_FILTER_HPF_CORNER_MSK, + FIELD_PREP(ADXL380_FILTER_HPF_CORNER_MSK, hpf)); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int _adxl380_set_act_inact_time_ms(struct adxl380_state *st, + enum adxl380_activity_type act, + u32 ms) +{ + u8 reg = adxl380_time_reg_high_addr[act]; + unsigned int reg_val; + int ret; + + /* 500us per code is the scale factor of TIME_ACT / TIME_INACT registers */ + reg_val = min(DIV_ROUND_CLOSEST(ms * 1000, 500), ADXL380_TIME_MAX); + + put_unaligned_be24(reg_val, &st->transf_buf[0]); + + ret = regmap_bulk_write(st->regmap, reg, st->transf_buf, sizeof(st->transf_buf)); + if (ret) + return ret; + + if (act == ADXL380_ACTIVITY) + st->act_time_ms = ms; + else + st->inact_time_ms = ms; + + return 0; +} + +static int adxl380_set_act_inact_time_ms(struct adxl380_state *st, + enum adxl380_activity_type act, + u32 ms) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = _adxl380_set_act_inact_time_ms(st, act, ms); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_set_range(struct adxl380_state *st, u8 range) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_OP_MODE_REG, + ADXL380_OP_MODE_RANGE_MSK, + FIELD_PREP(ADXL380_OP_MODE_RANGE_MSK, range)); + + if (ret) + return ret; + + adxl380_scale_act_inact_thresholds(st, st->range, range); + + /* Activity thresholds depend on range */ + ret = adxl380_write_act_inact_threshold(st, ADXL380_ACTIVITY, + st->act_threshold); + if (ret) + return ret; + + ret = adxl380_write_act_inact_threshold(st, ADXL380_INACTIVITY, + st->inact_threshold); + if (ret) + return ret; + + st->range = range; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_write_act_inact_en(struct adxl380_state *st, + enum adxl380_activity_type type, + bool en) +{ + if (type == ADXL380_ACTIVITY) + return regmap_update_bits(st->regmap, ADXL380_ACT_INACT_CTL_REG, + ADXL380_ACT_EN_MSK, + FIELD_PREP(ADXL380_ACT_EN_MSK, en)); + + return regmap_update_bits(st->regmap, ADXL380_ACT_INACT_CTL_REG, + ADXL380_INACT_EN_MSK, + FIELD_PREP(ADXL380_INACT_EN_MSK, en)); +} + +static int adxl380_read_act_inact_int(struct adxl380_state *st, + enum adxl380_activity_type type, + bool *en) +{ + int ret; + unsigned int reg_val; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, st->int_map[0], ®_val); + if (ret) + return ret; + + if (type == ADXL380_ACTIVITY) + *en = FIELD_GET(ADXL380_INT_MAP0_ACT_INT0_MSK, reg_val); + else + *en = FIELD_GET(ADXL380_INT_MAP0_INACT_INT0_MSK, reg_val); + + return 0; +} + +static int adxl380_write_act_inact_int(struct adxl380_state *st, + enum adxl380_activity_type act, + bool en) +{ + if (act == ADXL380_ACTIVITY) + return regmap_update_bits(st->regmap, st->int_map[0], + ADXL380_INT_MAP0_ACT_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP0_ACT_INT0_MSK, en)); + + return regmap_update_bits(st->regmap, st->int_map[0], + ADXL380_INT_MAP0_INACT_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP0_INACT_INT0_MSK, en)); +} + +static int adxl380_act_inact_config(struct adxl380_state *st, + enum adxl380_activity_type type, + bool en) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = adxl380_write_act_inact_en(st, type, en); + if (ret) + return ret; + + ret = adxl380_write_act_inact_int(st, type, en); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_write_tap_axis(struct adxl380_state *st, + enum adxl380_axis axis) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL380_TAP_CFG_REG, + ADXL380_TAP_AXIS_MSK, + FIELD_PREP(ADXL380_TAP_AXIS_MSK, axis)); + + if (ret) + return ret; + + st->tap_axis_en = axis; + + return 0; +} + +static int adxl380_read_tap_int(struct adxl380_state *st, enum adxl380_tap_type type, bool *en) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(st->regmap, st->int_map[1], ®_val); + if (ret) + return ret; + + if (type == ADXL380_SINGLE_TAP) + *en = FIELD_GET(ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK, reg_val); + else + *en = FIELD_GET(ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK, reg_val); + + return 0; +} + +static int adxl380_write_tap_int(struct adxl380_state *st, enum adxl380_tap_type type, bool en) +{ + if (type == ADXL380_SINGLE_TAP) + return regmap_update_bits(st->regmap, st->int_map[1], + ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK, en)); + + return regmap_update_bits(st->regmap, st->int_map[1], + ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK, en)); +} + +static int adxl380_tap_config(struct adxl380_state *st, + enum adxl380_axis axis, + enum adxl380_tap_type type, + bool en) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = adxl380_write_tap_axis(st, axis); + if (ret) + return ret; + + ret = adxl380_write_tap_int(st, type, en); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_set_fifo_samples(struct adxl380_state *st) +{ + int ret; + u16 fifo_samples = st->watermark * st->fifo_set_size; + + ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG, + ADXL380_FIFO_SAMPLES_8_MSK, + FIELD_PREP(ADXL380_FIFO_SAMPLES_8_MSK, + (fifo_samples & BIT(8)))); + if (ret) + return ret; + + return regmap_write(st->regmap, ADXL380_FIFO_CONFIG_1_REG, + fifo_samples & 0xFF); +} + +static int adxl380_get_status(struct adxl380_state *st, u8 *status0, u8 *status1) +{ + int ret; + + /* STATUS0, STATUS1 are adjacent regs */ + ret = regmap_bulk_read(st->regmap, ADXL380_STATUS_0_REG, + &st->transf_buf, 2); + if (ret) + return ret; + + *status0 = st->transf_buf[0]; + *status1 = st->transf_buf[1]; + + return 0; +} + +static int adxl380_get_fifo_entries(struct adxl380_state *st, u16 *fifo_entries) +{ + int ret; + + ret = regmap_bulk_read(st->regmap, ADXL380_FIFO_STATUS_0_REG, + &st->transf_buf, 2); + if (ret) + return ret; + + *fifo_entries = st->transf_buf[0] | ((BIT(0) & st->transf_buf[1]) << 8); + + return 0; +} + +static void adxl380_push_event(struct iio_dev *indio_dev, s64 timestamp, + u8 status1) +{ + if (FIELD_GET(ADXL380_STATUS_1_ACT_MSK, status1)) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + + if (FIELD_GET(ADXL380_STATUS_1_INACT_MSK, status1)) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + timestamp); + if (FIELD_GET(ADXL380_STATUS_1_SINGLE_TAP_MSK, status1)) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, IIO_EV_DIR_SINGLETAP), + timestamp); + + if (FIELD_GET(ADXL380_STATUS_1_DOUBLE_TAP_MSK, status1)) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, IIO_EV_DIR_DOUBLETAP), + timestamp); +} + +static irqreturn_t adxl380_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adxl380_state *st = iio_priv(indio_dev); + u8 status0, status1; + u16 fifo_entries; + int i; + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_get_status(st, &status0, &status1); + if (ret) + return IRQ_HANDLED; + + adxl380_push_event(indio_dev, iio_get_time_ns(indio_dev), status1); + + if (!FIELD_GET(ADXL380_STATUS_0_FIFO_WM_MSK, status0)) + return IRQ_HANDLED; + + ret = adxl380_get_fifo_entries(st, &fifo_entries); + if (ret) + return IRQ_HANDLED; + + for (i = 0; i < fifo_entries; i += st->fifo_set_size) { + ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, + &st->fifo_buf[i], + 2 * st->fifo_set_size); + if (ret) + return IRQ_HANDLED; + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + } + + return IRQ_HANDLED; +} + +static int adxl380_write_calibbias_value(struct adxl380_state *st, + unsigned long chan_addr, + s8 calibbias) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADXL380_X_DSM_OFFSET_REG + chan_addr, calibbias); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_read_calibbias_value(struct adxl380_state *st, + unsigned long chan_addr, + int *calibbias) +{ + int ret; + unsigned int reg_val; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, ADXL380_X_DSM_OFFSET_REG + chan_addr, ®_val); + if (ret) + return ret; + + *calibbias = sign_extend32(reg_val, 7); + + return 0; +} + +static ssize_t hwfifo_watermark_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "1\n"); +} + +static ssize_t hwfifo_watermark_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%lu\n", ADXL380_FIFO_SAMPLES); +} + +static ssize_t adxl380_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl380_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", st->watermark); +} + +static ssize_t adxl380_get_fifo_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl380_state *st = iio_priv(indio_dev); + int ret; + unsigned int reg_val; + + ret = regmap_read(st->regmap, ADXL380_DIG_EN_REG, ®_val); + if (ret) + return ret; + + return sysfs_emit(buf, "%lu\n", + FIELD_GET(ADXL380_FIFO_EN_MSK, reg_val)); +} + +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + adxl380_get_fifo_watermark, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + adxl380_get_fifo_enabled, NULL, 0); + +static const struct iio_dev_attr *adxl380_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + +static int adxl380_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int i; + int ret; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, + st->int_map[0], + ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, 1)); + if (ret) + return ret; + + for_each_clear_bit(i, indio_dev->active_scan_mask, ADXL380_CH_NUM) { + ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, + ADXL380_CHAN_EN_MSK(i), + 0 << (4 + i)); + if (ret) + return ret; + } + + st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + + if ((st->watermark * st->fifo_set_size) > ADXL380_FIFO_SAMPLES) + st->watermark = (ADXL380_FIFO_SAMPLES / st->fifo_set_size); + + ret = adxl380_set_fifo_samples(st); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, ADXL380_FIFO_EN_MSK, + FIELD_PREP(ADXL380_FIFO_EN_MSK, 1)); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static int adxl380_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int ret, i; + + guard(mutex)(&st->lock); + + ret = adxl380_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, + st->int_map[0], + ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, + FIELD_PREP(ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, 0)); + if (ret) + return ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, + ADXL380_CHAN_EN_MSK(i), + 1 << (4 + i)); + if (ret) + return ret; + } + + ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, ADXL380_FIFO_EN_MSK, + FIELD_PREP(ADXL380_FIFO_EN_MSK, 0)); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +static const struct iio_buffer_setup_ops adxl380_buffer_ops = { + .postenable = adxl380_buffer_postenable, + .predisable = adxl380_buffer_predisable, +}; + +static int adxl380_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = adxl380_read_chn(st, chan->address); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + scoped_guard(mutex, &st->lock) { + *val = st->chip_info->scale_tbl[st->range][0]; + *val2 = st->chip_info->scale_tbl[st->range][1]; + } + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + /* 10.2 LSB / Degree Celsius */ + *val = 10000; + *val2 = 102; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = st->chip_info->temp_offset; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + ret = adxl380_read_calibbias_value(st, chan->scan_index, val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + ret = adxl380_get_odr(st, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = adxl380_get_lpf(st, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + ret = adxl380_get_hpf(st, val, val2); + if (ret) + return ret; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int adxl380_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl380_state *st = iio_priv(indio_dev); + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->chip_info->scale_tbl; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)st->chip_info->samp_freq_tbl; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->chip_info->samp_freq_tbl); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)st->lpf_tbl; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->lpf_tbl); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)st->hpf_tbl; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->hpf_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl380_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int odr_index, lpf_index, hpf_index, range_index; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl, + ARRAY_SIZE(st->chip_info->samp_freq_tbl), + val); + return adxl380_set_odr(st, odr_index); + case IIO_CHAN_INFO_CALIBBIAS: + return adxl380_write_calibbias_value(st, chan->scan_index, val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + lpf_index = adxl380_find_match_1d_tbl(st->lpf_tbl, + ARRAY_SIZE(st->lpf_tbl), + val); + return adxl380_set_lpf(st, lpf_index); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + hpf_index = adxl380_find_match_2d_tbl(st->hpf_tbl, + ARRAY_SIZE(st->hpf_tbl), + val, val2); + if (hpf_index < 0) + return hpf_index; + return adxl380_set_hpf(st, hpf_index); + case IIO_CHAN_INFO_SCALE: + range_index = adxl380_find_match_2d_tbl(st->chip_info->scale_tbl, + ARRAY_SIZE(st->chip_info->scale_tbl), + val, val2); + if (range_index < 0) + return range_index; + return adxl380_set_range(st, range_index); + default: + return -EINVAL; + } +} + +static int adxl380_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int adxl380_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct adxl380_state *st = iio_priv(indio_dev); + int ret; + bool int_en; + bool tap_axis_en = false; + + switch (chan->channel2) { + case IIO_MOD_X: + tap_axis_en = st->tap_axis_en == ADXL380_X_AXIS; + break; + case IIO_MOD_Y: + tap_axis_en = st->tap_axis_en == ADXL380_Y_AXIS; + break; + case IIO_MOD_Z: + tap_axis_en = st->tap_axis_en == ADXL380_Z_AXIS; + break; + default: + return -EINVAL; + } + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = adxl380_read_act_inact_int(st, ADXL380_ACTIVITY, &int_en); + if (ret) + return ret; + return int_en; + case IIO_EV_DIR_FALLING: + ret = adxl380_read_act_inact_int(st, ADXL380_INACTIVITY, &int_en); + if (ret) + return ret; + return int_en; + case IIO_EV_DIR_SINGLETAP: + ret = adxl380_read_tap_int(st, ADXL380_SINGLE_TAP, &int_en); + if (ret) + return ret; + return int_en && tap_axis_en; + case IIO_EV_DIR_DOUBLETAP: + ret = adxl380_read_tap_int(st, ADXL380_DOUBLE_TAP, &int_en); + if (ret) + return ret; + return int_en && tap_axis_en; + default: + return -EINVAL; + } +} + +static int adxl380_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct adxl380_state *st = iio_priv(indio_dev); + enum adxl380_axis axis; + + switch (chan->channel2) { + case IIO_MOD_X: + axis = ADXL380_X_AXIS; + break; + case IIO_MOD_Y: + axis = ADXL380_Y_AXIS; + break; + case IIO_MOD_Z: + axis = ADXL380_Z_AXIS; + break; + default: + return -EINVAL; + } + + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl380_act_inact_config(st, ADXL380_ACTIVITY, state); + case IIO_EV_DIR_FALLING: + return adxl380_act_inact_config(st, ADXL380_INACTIVITY, state); + case IIO_EV_DIR_SINGLETAP: + return adxl380_tap_config(st, axis, ADXL380_SINGLE_TAP, state); + case IIO_EV_DIR_DOUBLETAP: + return adxl380_tap_config(st, axis, ADXL380_DOUBLE_TAP, state); + default: + return -EINVAL; + } +} + +static int adxl380_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct adxl380_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + switch (type) { + case IIO_EV_TYPE_THRESH: + switch (info) { + case IIO_EV_INFO_VALUE: { + switch (dir) { + case IIO_EV_DIR_RISING: + *val = st->act_threshold; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = st->inact_threshold; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = st->act_time_ms; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + *val = st->inact_time_ms; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + *val = st->tap_threshold; + return IIO_VAL_INT; + case IIO_EV_INFO_RESET_TIMEOUT: + *val = st->tap_window_us; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_INFO_TAP2_MIN_DELAY: + *val = st->tap_latent_us; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl380_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct adxl380_state *st = iio_priv(indio_dev); + u32 val_ms, val_us; + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (type) { + case IIO_EV_TYPE_THRESH: + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl380_set_act_inact_threshold(indio_dev, + ADXL380_ACTIVITY, val); + case IIO_EV_DIR_FALLING: + return adxl380_set_act_inact_threshold(indio_dev, + ADXL380_INACTIVITY, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + val_ms = val * 1000 + DIV_ROUND_UP(val2, 1000); + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl380_set_act_inact_time_ms(st, + ADXL380_ACTIVITY, val_ms); + case IIO_EV_DIR_FALLING: + return adxl380_set_act_inact_time_ms(st, + ADXL380_INACTIVITY, val_ms); + default: + return -EINVAL; + } + + default: + return -EINVAL; + } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + return adxl380_set_tap_threshold_value(indio_dev, val); + case IIO_EV_INFO_RESET_TIMEOUT: + val_us = val * 1000000 + val2; + return adxl380_write_tap_time_us(st, + ADXL380_TAP_TIME_WINDOW, + val_us); + case IIO_EV_INFO_TAP2_MIN_DELAY: + val_us = val * 1000000 + val2; + return adxl380_write_tap_time_us(st, + ADXL380_TAP_TIME_LATENT, + val_us); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int vals[2]; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl380_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + vals[0] = st->tap_duration_us; + vals[1] = MICRO; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, vals); +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl380_state *st = iio_priv(indio_dev); + int ret, val_int, val_fract_us; + + guard(mutex)(&st->lock); + + ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract_us); + if (ret) + return ret; + + /* maximum value is 255 * 625 us = 0.159375 seconds */ + if (val_int || val_fract_us > 159375 || val_fract_us < 0) + return -EINVAL; + + ret = adxl380_write_tap_dur_us(indio_dev, val_fract_us); + if (ret) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_maxtomin_time, 0); + +static struct attribute *adxl380_event_attributes[] = { + &iio_dev_attr_in_accel_gesture_tap_maxtomin_time.dev_attr.attr, + NULL +}; + +static const struct attribute_group adxl380_event_attribute_group = { + .attrs = adxl380_event_attributes, +}; + +static int adxl380_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adxl380_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int adxl380_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct adxl380_state *st = iio_priv(indio_dev); + + st->watermark = min(val, ADXL380_FIFO_SAMPLES); + + return 0; +} + +static const struct iio_info adxl380_info = { + .read_raw = adxl380_read_raw, + .read_avail = &adxl380_read_avail, + .write_raw = adxl380_write_raw, + .write_raw_get_fmt = adxl380_write_raw_get_fmt, + .read_event_config = adxl380_read_event_config, + .write_event_config = adxl380_write_event_config, + .read_event_value = adxl380_read_event_value, + .write_event_value = adxl380_write_event_value, + .event_attrs = &adxl380_event_attribute_group, + .debugfs_reg_access = &adxl380_reg_access, + .hwfifo_set_watermark = adxl380_set_watermark, +}; + +static const struct iio_event_spec adxl380_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_SINGLETAP, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_DOUBLETAP, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT) | + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), + }, +}; + +#define ADXL380_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .event_spec = adxl380_events, \ + .num_event_specs = ARRAY_SIZE(adxl380_events) \ +} + +static const struct iio_chan_spec adxl380_channels[] = { + ADXL380_ACCEL_CHANNEL(0, ADXL380_X_DATA_H_REG, X), + ADXL380_ACCEL_CHANNEL(1, ADXL380_Y_DATA_H_REG, Y), + ADXL380_ACCEL_CHANNEL(2, ADXL380_Z_DATA_H_REG, Z), + { + .type = IIO_TEMP, + .address = ADXL380_T_DATA_H_REG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .shift = 4, + .endianness = IIO_BE, + }, + }, +}; + +static int adxl380_config_irq(struct iio_dev *indio_dev) +{ + struct adxl380_state *st = iio_priv(indio_dev); + unsigned long irq_flag; + struct irq_data *desc; + u32 irq_type; + u8 polarity; + int ret; + + st->irq = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT0"); + if (st->irq > 0) { + st->int_map[0] = ADXL380_INT0_MAP0_REG; + st->int_map[1] = ADXL380_INT0_MAP1_REG; + } else { + st->irq = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT1"); + if (st->irq > 0) + return dev_err_probe(st->dev, -ENODEV, + "no interrupt name specified"); + st->int_map[0] = ADXL380_INT1_MAP0_REG; + st->int_map[1] = ADXL380_INT1_MAP1_REG; + } + + desc = irq_get_irq_data(st->irq); + if (!desc) + return dev_err_probe(st->dev, -EINVAL, "Could not find IRQ %d\n", st->irq); + + irq_type = irqd_get_trigger_type(desc); + if (irq_type == IRQ_TYPE_LEVEL_HIGH) { + polarity = 0; + irq_flag = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + } else if (irq_type == IRQ_TYPE_LEVEL_LOW) { + polarity = 1; + irq_flag = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + } else { + return dev_err_probe(st->dev, -EINVAL, + "Invalid interrupt 0x%x. Only level interrupts supported\n", + irq_type); + } + + ret = regmap_update_bits(st->regmap, ADXL380_INT0_REG, + ADXL380_INT0_POL_MSK, + FIELD_PREP(ADXL380_INT0_POL_MSK, polarity)); + if (ret) + return ret; + + return devm_request_threaded_irq(st->dev, st->irq, NULL, + adxl380_irq_handler, irq_flag, + indio_dev->name, indio_dev); +} + +static int adxl380_setup(struct iio_dev *indio_dev) +{ + unsigned int reg_val; + u16 part_id, chip_id; + int ret, i; + struct adxl380_state *st = iio_priv(indio_dev); + + ret = regmap_read(st->regmap, ADXL380_DEVID_AD_REG, ®_val); + if (ret) + return ret; + + if (reg_val != ADXL380_DEVID_AD_VAL) + dev_warn(st->dev, "Unknown chip id %x\n", reg_val); + + ret = regmap_bulk_read(st->regmap, ADLX380_PART_ID_REG, + &st->transf_buf, 2); + if (ret) + return ret; + + part_id = get_unaligned_be16(st->transf_buf); + part_id >>= 4; + + if (part_id != ADXL380_ID_VAL) + dev_warn(st->dev, "Unknown part id %x\n", part_id); + + ret = regmap_read(st->regmap, ADXL380_MISC_0_REG, ®_val); + if (ret) + return ret; + + /* Bit to differentiate between ADXL380/382. */ + if (reg_val & ADXL380_XL382_MSK) + chip_id = ADXL382_ID_VAL; + else + chip_id = ADXL380_ID_VAL; + + if (chip_id != st->chip_info->chip_id) + dev_warn(st->dev, "Unknown chip id %x\n", chip_id); + + ret = regmap_write(st->regmap, ADXL380_RESET_REG, ADXL380_RESET_CODE); + if (ret) + return ret; + + /* + * A latency of approximately 0.5 ms is required after soft reset. + * Stated in the register REG_RESET description. + */ + fsleep(500); + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, + ADXL380_CHAN_EN_MSK(i), + 1 << (4 + i)); + if (ret) + return ret; + } + + ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG, + ADXL380_FIFO_MODE_MSK, + FIELD_PREP(ADXL380_FIFO_MODE_MSK, ADXL380_FIFO_STREAMED)); + if (ret) + return ret; + + /* Select all 3 axis for act/inact detection. */ + ret = regmap_update_bits(st->regmap, ADXL380_SNSR_AXIS_EN_REG, + ADXL380_ACT_INACT_AXIS_EN_MSK, + FIELD_PREP(ADXL380_ACT_INACT_AXIS_EN_MSK, + ADXL380_ACT_INACT_AXIS_EN_MSK)); + if (ret) + return ret; + + ret = adxl380_config_irq(indio_dev); + if (ret) + return ret; + + ret = adxl380_fill_lpf_tbl(st); + if (ret) + return ret; + + ret = adxl380_fill_hpf_tbl(st); + if (ret) + return ret; + + return adxl380_set_measure_en(st, true); +} + +int adxl380_probe(struct device *dev, struct regmap *regmap, + const struct adxl380_chip_info *chip_info) +{ + struct iio_dev *indio_dev; + struct adxl380_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->dev = dev; + st->regmap = regmap; + st->chip_info = chip_info; + + mutex_init(&st->lock); + + indio_dev->channels = adxl380_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl380_channels); + indio_dev->name = chip_info->name; + indio_dev->info = &adxl380_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_regulator_get_enable(dev, "vddio"); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to get vddio regulator\n"); + + ret = devm_regulator_get_enable(st->dev, "vsupply"); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to get vsupply regulator\n"); + + ret = adxl380_setup(indio_dev); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev, + &adxl380_buffer_ops, + adxl380_fifo_attributes); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(adxl380_probe, IIO_ADXL380); + +MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl380.h b/drivers/iio/accel/adxl380.h new file mode 100644 index 000000000000..a683625d897a --- /dev/null +++ b/drivers/iio/accel/adxl380.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ADXL380 3-Axis Digital Accelerometer + * + * Copyright 2024 Analog Devices Inc. + */ + +#ifndef _ADXL380_H_ +#define _ADXL380_H_ + +struct adxl380_chip_info { + const char *name; + const int scale_tbl[3][2]; + const int samp_freq_tbl[3]; + const int temp_offset; + const u16 chip_id; +}; + +extern const struct adxl380_chip_info adxl380_chip_info; +extern const struct adxl380_chip_info adxl382_chip_info; + +int adxl380_probe(struct device *dev, struct regmap *regmap, + const struct adxl380_chip_info *chip_info); +bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg); + +#endif /* _ADXL380_H_ */ diff --git a/drivers/iio/accel/adxl380_i2c.c b/drivers/iio/accel/adxl380_i2c.c new file mode 100644 index 000000000000..1dc1e77be815 --- /dev/null +++ b/drivers/iio/accel/adxl380_i2c.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL380 3-Axis Digital Accelerometer I2C driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl380.h" + +static const struct regmap_config adxl380_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_noinc_reg = adxl380_readable_noinc_reg, +}; + +static int adxl380_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + const struct adxl380_chip_info *chip_data; + + chip_data = i2c_get_match_data(client); + + regmap = devm_regmap_init_i2c(client, &adxl380_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl380_probe(&client->dev, regmap, chip_data); +} + +static const struct i2c_device_id adxl380_i2c_id[] = { + { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, + { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adxl380_i2c_id); + +static const struct of_device_id adxl380_of_match[] = { + { .compatible = "adi,adxl380", .data = &adxl380_chip_info }, + { .compatible = "adi,adxl382", .data = &adxl382_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl380_of_match); + +static struct i2c_driver adxl380_i2c_driver = { + .driver = { + .name = "adxl380_i2c", + .of_match_table = adxl380_of_match, + }, + .probe = adxl380_i2c_probe, + .id_table = adxl380_i2c_id, +}; + +module_i2c_driver(adxl380_i2c_driver); + +MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL380); diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c new file mode 100644 index 000000000000..e7b5778cb6cf --- /dev/null +++ b/drivers/iio/accel/adxl380_spi.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL380 3-Axis Digital Accelerometer SPI driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl380.h" + +static const struct regmap_config adxl380_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .readable_noinc_reg = adxl380_readable_noinc_reg, +}; + +static int adxl380_spi_probe(struct spi_device *spi) +{ + const struct adxl380_chip_info *chip_data; + struct regmap *regmap; + + chip_data = spi_get_device_match_data(spi); + + regmap = devm_regmap_init_spi(spi, &adxl380_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl380_probe(&spi->dev, regmap, chip_data); +} + +static const struct spi_device_id adxl380_spi_id[] = { + { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, + { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, adxl380_spi_id); + +static const struct of_device_id adxl380_of_match[] = { + { .compatible = "adi,adxl380", .data = &adxl380_chip_info }, + { .compatible = "adi,adxl382", .data = &adxl382_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl380_of_match); + +static struct spi_driver adxl380_spi_driver = { + .driver = { + .name = "adxl380_spi", + .of_match_table = adxl380_of_match, + }, + .probe = adxl380_spi_probe, + .id_table = adxl380_spi_id, +}; + +module_spi_driver(adxl380_spi_driver); + +MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL380); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 6581772cb0c4..2445a0f7bc2b 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -876,8 +876,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = bma180_get_data_reg(data, bit); if (ret < 0) { mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index e90e2f01550a..89db242f06e0 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -13,6 +13,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> @@ -795,21 +796,19 @@ static int bma400_enable_steps(struct bma400_data *data, int val) static int bma400_get_steps_reg(struct bma400_data *data, int *val) { - u8 *steps_raw; int ret; - steps_raw = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL); + u8 *steps_raw __free(kfree) = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL); if (!steps_raw) return -ENOMEM; ret = regmap_bulk_read(data->regmap, BMA400_STEP_CNT0_REG, steps_raw, BMA400_STEP_RAW_LEN); - if (ret) { - kfree(steps_raw); + if (ret) return ret; - } + *val = get_unaligned_le24(steps_raw); - kfree(steps_raw); + return IIO_VAL_INT; } diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index ec13c044b304..765d8c4a4c4d 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -53,7 +53,7 @@ static int bma400_regmap_spi_write(void *context, const void *data, return spi_write(spi, data, count); } -static struct regmap_bus bma400_regmap_bus = { +static const struct regmap_bus bma400_regmap_bus = { .read = bma400_regmap_spi_read, .write = bma400_regmap_spi_write, .read_flag_mask = BIT(7), diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index ae0cd48a3e29..0f32c1e92b4d 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -10,9 +10,9 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/acpi.h> -#include <linux/of_irq.h> #include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> @@ -387,7 +387,7 @@ static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev, struct iio_mount_matrix *orientation) { struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct acpi_device *adev = ACPI_COMPANION(dev); + acpi_handle handle = ACPI_HANDLE(dev); char *name, *alt_name, *label; if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { @@ -398,9 +398,9 @@ static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev, label = "accel-display"; } - if (acpi_has_method(adev->handle, "ROTM")) { + if (acpi_has_method(handle, "ROTM")) { name = "ROTM"; - } else if (acpi_has_method(adev->handle, alt_name)) { + } else if (acpi_has_method(handle, alt_name)) { name = alt_name; indio_dev->label = label; } else { @@ -514,7 +514,7 @@ static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev, */ irq_info = bmc150_accel_interrupts_int1; if (data->type == BOSCH_BMC156 || - irq == of_irq_get_byname(dev->of_node, "INT2")) + irq == fwnode_irq_get_byname(dev_fwnode(dev), "INT2")) irq_info = bmc150_accel_interrupts_int2; for (i = 0; i < BMC150_ACCEL_INTERRUPTS; i++) @@ -1007,8 +1007,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, int j, bit; j = 0; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) + iio_for_each_active_channel(indio_dev, bit) memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit], sizeof(data->scan.channels[0])); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 7b419a7b2478..df1adc059aa9 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -36,7 +36,7 @@ static int bmi088_regmap_spi_read(void *context, const void *reg, return spi_write_then_read(spi, addr, sizeof(addr), val, val_size); } -static struct regmap_bus bmi088_regmap_bus = { +static const struct regmap_bus bmi088_regmap_bus = { .write = bmi088_regmap_spi_write, .read = bmi088_regmap_spi_read, }; diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 0f403342b1fc..f7e4dc02b34d 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -62,7 +62,7 @@ static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev, return ret; } - for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) { *data = st->resp->dump.sensor[sensor_num].data[i] * st->sign[i]; data++; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index d25e31613413..acadabec4df7 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -966,8 +966,7 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) int j, bit; j = 0; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit], sizeof(data->scan.channels[0])); } diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 8280d2bef0a3..b76df8816323 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -173,6 +173,7 @@ enum kx_chipset { KXCJ91008, KXTJ21009, KXTF9, + KX0221020, KX0231025, KX_MAX_CHIPS /* this must be last */ }; @@ -580,8 +581,8 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) return ret; } - /* On KX023, route all used interrupts to INT1 for now */ - if (data->chipset == KX0231025 && data->client->irq > 0) { + /* On KX023 and KX022, route all used interrupts to INT1 for now */ + if ((data->chipset == KX0231025 || data->chipset == KX0221020) && data->client->irq > 0) { ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4, KX023_REG_INC4_DRDY1 | KX023_REG_INC4_WUFI1); @@ -1507,6 +1508,7 @@ static int kxcjk1013_probe(struct i2c_client *client) case KXTF9: data->regs = &kxtf9_regs; break; + case KX0221020: case KX0231025: data->regs = &kx0231025_regs; break; @@ -1712,6 +1714,7 @@ static const struct i2c_device_id kxcjk1013_id[] = { {"kxcj91008", KXCJ91008}, {"kxtj21009", KXTJ21009}, {"kxtf9", KXTF9}, + {"kx022-1020", KX0221020}, {"kx023-1025", KX0231025}, {"SMO8500", KXCJ91008}, {} @@ -1724,6 +1727,7 @@ static const struct of_device_id kxcjk1013_of_match[] = { { .compatible = "kionix,kxcj91008", }, { .compatible = "kionix,kxtj21009", }, { .compatible = "kionix,kxtf9", }, + { .compatible = "kionix,kx022-1020", }, { .compatible = "kionix,kx023-1025", }, { } }; diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index 4cdbf5424a53..57025354c7cd 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -900,8 +900,7 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p) mutex_lock(&msa311->lock); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { chan = &msa311_channels[bit]; err = msa311_get_axis(msa311, chan, &axis); diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index 306482b70fad..fca77d660625 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -494,8 +494,7 @@ static irqreturn_t sca3300_trigger_handler(int irq, void *p) int bit, ret, val, i = 0; s16 *channels = (s16 *)data->buffer; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = sca3300_read_reg(data, indio_dev->channels[bit].address, &val); if (ret) { dev_err_ratelimited(&data->spi->dev, diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index b3534d5751b9..abead190254b 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -448,8 +448,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) goto err; } } else { - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = stk8312_read_accel(data, bit); if (ret < 0) { mutex_unlock(&data->lock); diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 6d3c7f444d21..a32a77324e92 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -330,8 +330,7 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) goto err; } } else { - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = stk8ba50_read_accel(data, stk8ba50_channel_table[bit]); if (ret < 0) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f60fe85a30d5..97ece1a4b7e3 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -21,6 +21,18 @@ config AD_SIGMA_DELTA select IIO_BUFFER select IIO_TRIGGERED_BUFFER +config AD4000 + tristate "Analog Devices AD4000 ADC Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD4000 high speed + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4000. + config AD4130 tristate "Analog Device AD4130 ADC Driver" depends on SPI @@ -36,6 +48,17 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. +config AD4695 + tristate "Analog Device AD4695 ADC Driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD4695 and similar + analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4695. + config AD7091R tristate @@ -991,6 +1014,19 @@ config NPCM_ADC This driver can also be built as a module. If so, the module will be called npcm_adc. +config PAC1921 + tristate "Microchip Technology PAC1921 driver" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Microchip Technology's PAC1921 + High-Side Power/Current Monitor with Analog Output. + + This driver can also be built as a module. If so, the module + will be called pac1921. + config PAC1934 tristate "Microchip Technology PAC1934 driver" depends on I2C @@ -1156,6 +1192,16 @@ config SC27XX_ADC This driver can also be built as a module. If so, the module will be called sc27xx_adc. +config SOPHGO_CV1800B_ADC + tristate "Sophgo CV1800B SARADC" + depends on ARCH_SOPHGO || COMPILE_TEST + help + Say yes here to build support for the SARADC integrated inside + the Sophgo CV1800B SoC. + + This driver can also be built as a module. If so, the module + will be called sophgo_cv1800b_adc. + config SPEAR_ADC tristate "ST SPEAr ADC" depends on PLAT_SPEAR || COMPILE_TEST @@ -1171,6 +1217,7 @@ config SD_ADC_MODULATOR tristate "Generic sigma delta modulator" select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_BACKEND help Select this option to enables sigma delta modulator. This driver can support generic sigma delta modulators. @@ -1225,6 +1272,7 @@ config STM32_DFSDM_ADC select IIO_BUFFER select IIO_BUFFER_HW_CONSUMER select IIO_TRIGGERED_BUFFER + select IIO_BACKEND help Select this option to support ADCSigma delta modulator for STMicroelectronics STM32 digital filter for sigma delta converter. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d370e066544e..7b91cd98c0e0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,7 +6,9 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o +obj-$(CONFIG_AD4000) += ad4000.o obj-$(CONFIG_AD4130) += ad4130.o +obj-$(CONFIG_AD4695) += ad4695.o obj-$(CONFIG_AD7091R) += ad7091r-base.o obj-$(CONFIG_AD7091R5) += ad7091r5.o obj-$(CONFIG_AD7091R8) += ad7091r8.o @@ -90,6 +92,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_NPCM_ADC) += npcm_adc.o +obj-$(CONFIG_PAC1921) += pac1921.o obj-$(CONFIG_PAC1934) += pac1934.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o @@ -105,6 +108,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o +obj-$(CONFIG_SOPHGO_CV1800B_ADC) += sophgo-cv1800b-adc.o obj-$(CONFIG_SPEAR_ADC) += spear_adc.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c new file mode 100644 index 000000000000..6ea491245084 --- /dev/null +++ b/drivers/iio/adc/ad4000.c @@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD4000 SPI ADC driver + * + * Copyright 2024 Analog Devices Inc. + */ +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/byteorder/generic.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/units.h> +#include <linux/util_macros.h> +#include <linux/iio/iio.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#define AD4000_READ_COMMAND 0x54 +#define AD4000_WRITE_COMMAND 0x14 + +#define AD4000_CONFIG_REG_DEFAULT 0xE1 + +/* AD4000 Configuration Register programmable bits */ +#define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */ +#define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */ + +#define AD4000_SCALE_OPTIONS 2 + +#define AD4000_TQUIET1_NS 190 +#define AD4000_TQUIET2_NS 60 +#define AD4000_TCONV_NS 320 + +#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = 1, \ + .channel = 0, \ + .channel2 = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_type = { \ + .sign = _sign, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = _storage_bits - _real_bits, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \ + __AD4000_DIFF_CHANNEL((_sign), (_real_bits), \ + ((_real_bits) > 16 ? 32 : 16), (_reg_access)) + +#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_type = { \ + .sign = _sign, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = _storage_bits - _real_bits, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \ + __AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \ + ((_real_bits) > 16 ? 32 : 16), (_reg_access)) + +static const char * const ad4000_power_supplies[] = { + "vdd", "vio" +}; + +enum ad4000_sdi { + AD4000_SDI_MOSI, + AD4000_SDI_VIO, + AD4000_SDI_CS, + AD4000_SDI_GND, +}; + +/* maps adi,sdi-pin property value to enum */ +static const char * const ad4000_sdi_pin[] = { + [AD4000_SDI_MOSI] = "sdi", + [AD4000_SDI_VIO] = "high", + [AD4000_SDI_CS] = "cs", + [AD4000_SDI_GND] = "low", +}; + +/* Gains stored as fractions of 1000 so they can be expressed by integers. */ +static const int ad4000_gains[] = { + 454, 909, 1000, 1900, +}; + +struct ad4000_chip_info { + const char *dev_name; + struct iio_chan_spec chan_spec; + struct iio_chan_spec reg_access_chan_spec; + bool has_hardware_gain; +}; + +static const struct ad4000_chip_info ad4000_chip_info = { + .dev_name = "ad4000", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), +}; + +static const struct ad4000_chip_info ad4001_chip_info = { + .dev_name = "ad4001", + .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), +}; + +static const struct ad4000_chip_info ad4002_chip_info = { + .dev_name = "ad4002", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), +}; + +static const struct ad4000_chip_info ad4003_chip_info = { + .dev_name = "ad4003", + .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), +}; + +static const struct ad4000_chip_info ad4004_chip_info = { + .dev_name = "ad4004", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), +}; + +static const struct ad4000_chip_info ad4005_chip_info = { + .dev_name = "ad4005", + .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), +}; + +static const struct ad4000_chip_info ad4006_chip_info = { + .dev_name = "ad4006", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), +}; + +static const struct ad4000_chip_info ad4007_chip_info = { + .dev_name = "ad4007", + .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), +}; + +static const struct ad4000_chip_info ad4008_chip_info = { + .dev_name = "ad4008", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), +}; + +static const struct ad4000_chip_info ad4010_chip_info = { + .dev_name = "ad4010", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), +}; + +static const struct ad4000_chip_info ad4011_chip_info = { + .dev_name = "ad4011", + .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), +}; + +static const struct ad4000_chip_info ad4020_chip_info = { + .dev_name = "ad4020", + .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), +}; + +static const struct ad4000_chip_info ad4021_chip_info = { + .dev_name = "ad4021", + .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), +}; + +static const struct ad4000_chip_info ad4022_chip_info = { + .dev_name = "ad4022", + .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), +}; + +static const struct ad4000_chip_info adaq4001_chip_info = { + .dev_name = "adaq4001", + .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .has_hardware_gain = true, +}; + +static const struct ad4000_chip_info adaq4003_chip_info = { + .dev_name = "adaq4003", + .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .has_hardware_gain = true, +}; + +struct ad4000_state { + struct spi_device *spi; + struct gpio_desc *cnv_gpio; + struct spi_transfer xfers[2]; + struct spi_message msg; + struct mutex lock; /* Protect read modify write cycle */ + int vref_mv; + enum ad4000_sdi sdi_pin; + bool span_comp; + u16 gain_milli; + int scale_tbl[AD4000_SCALE_OPTIONS][2]; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + struct { + union { + __be16 sample_buf16; + __be32 sample_buf32; + } data; + s64 timestamp __aligned(8); + } scan __aligned(IIO_DMA_MINALIGN); + u8 tx_buf[2]; + u8 rx_buf[2]; +}; + +static void ad4000_fill_scale_tbl(struct ad4000_state *st, + struct iio_chan_spec const *chan) +{ + int val, tmp0, tmp1; + int scale_bits; + u64 tmp2; + + /* + * ADCs that output two's complement code have one less bit to express + * voltage magnitude. + */ + if (chan->scan_type.sign == 's') + scale_bits = chan->scan_type.realbits - 1; + else + scale_bits = chan->scan_type.realbits; + + /* + * The gain is stored as a fraction of 1000 and, as we need to + * divide vref_mv by the gain, we invert the gain/1000 fraction. + * Also multiply by an extra MILLI to preserve precision. + * Thus, we have MILLI * MILLI equals MICRO as fraction numerator. + */ + val = mult_frac(st->vref_mv, MICRO, st->gain_milli); + + /* Would multiply by NANO here but we multiplied by extra MILLI */ + tmp2 = shift_right((u64)val * MICRO, scale_bits); + tmp0 = div_s64_rem(tmp2, NANO, &tmp1); + + /* Store scale for when span compression is disabled */ + st->scale_tbl[0][0] = tmp0; /* Integer part */ + st->scale_tbl[0][1] = abs(tmp1); /* Fractional part */ + + /* Store scale for when span compression is enabled */ + st->scale_tbl[1][0] = tmp0; + + /* The integer part is always zero so don't bother to divide it. */ + if (chan->differential) + st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 4, 5); + else + st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 9, 10); +} + +static int ad4000_write_reg(struct ad4000_state *st, uint8_t val) +{ + st->tx_buf[0] = AD4000_WRITE_COMMAND; + st->tx_buf[1] = val; + return spi_write(st->spi, st->tx_buf, ARRAY_SIZE(st->tx_buf)); +} + +static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val) +{ + struct spi_transfer t = { + .tx_buf = st->tx_buf, + .rx_buf = st->rx_buf, + .len = 2, + }; + int ret; + + st->tx_buf[0] = AD4000_READ_COMMAND; + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret < 0) + return ret; + + *val = st->rx_buf[1]; + return ret; +} + +static int ad4000_convert_and_acquire(struct ad4000_state *st) +{ + int ret; + + /* + * In 4-wire mode, the CNV line is held high for the entire conversion + * and acquisition process. In other modes, the CNV GPIO is optional + * and, if provided, replaces controller CS. If CNV GPIO is not defined + * gpiod_set_value_cansleep() has no effect. + */ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ret = spi_sync(st->spi, &st->msg); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + + return ret; +} + +static int ad4000_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad4000_state *st = iio_priv(indio_dev); + u32 sample; + int ret; + + ret = ad4000_convert_and_acquire(st); + if (ret < 0) + return ret; + + if (chan->scan_type.storagebits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32); + else + sample = be16_to_cpu(st->scan.data.sample_buf16); + + sample >>= chan->scan_type.shift; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(sample, chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int ad4000_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4000_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return ad4000_single_conversion(indio_dev, chan, val); + unreachable(); + case IIO_CHAN_INFO_SCALE: + *val = st->scale_tbl[st->span_comp][0]; + *val2 = st->scale_tbl[st->span_comp][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = 0; + if (st->span_comp) + *val = mult_frac(st->vref_mv, 1, 10); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4000_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4000_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_tbl; + *length = AD4000_SCALE_OPTIONS * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4000_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_PLUS_MICRO; + } +} + +static int ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ad4000_state *st = iio_priv(indio_dev); + unsigned int reg_val; + bool span_comp_en; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + guard(mutex)(&st->lock); + + ret = ad4000_read_reg(st, ®_val); + if (ret < 0) + return ret; + + span_comp_en = val2 == st->scale_tbl[1][1]; + reg_val &= ~AD4000_CFG_SPAN_COMP; + reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en); + + ret = ad4000_write_reg(st, reg_val); + if (ret < 0) + return ret; + + st->span_comp = span_comp_en; + return 0; + } + unreachable(); + default: + return -EINVAL; + } +} + +static irqreturn_t ad4000_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4000_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4000_convert_and_acquire(st); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const struct iio_info ad4000_reg_access_info = { + .read_raw = &ad4000_read_raw, + .read_avail = &ad4000_read_avail, + .write_raw = &ad4000_write_raw, + .write_raw_get_fmt = &ad4000_write_raw_get_fmt, +}; + +static const struct iio_info ad4000_info = { + .read_raw = &ad4000_read_raw, +}; + +/* + * This executes a data sample transfer for when the device connections are + * in "3-wire" mode, selected when the adi,sdi-pin device tree property is + * absent or set to "high". In this connection mode, the ADC SDI pin is + * connected to MOSI or to VIO and ADC CNV pin is connected either to a SPI + * controller CS or to a GPIO. + * AD4000 series of devices initiate conversions on the rising edge of CNV pin. + * + * If the CNV pin is connected to an SPI controller CS line (which is by default + * active low), the ADC readings would have a latency (delay) of one read. + * Moreover, since we also do ADC sampling for filling the buffer on triggered + * buffer mode, the timestamps of buffer readings would be disarranged. + * To prevent the read latency and reduce the time discrepancy between the + * sample read request and the time of actual sampling by the ADC, do a + * preparatory transfer to pulse the CS/CNV line. + */ +static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + unsigned int cnv_pulse_time = AD4000_TCONV_NS; + struct spi_transfer *xfers = st->xfers; + + xfers[0].cs_change = 1; + xfers[0].cs_change_delay.value = cnv_pulse_time; + xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &st->scan.data; + xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); + xfers[1].delay.value = AD4000_TQUIET2_NS; + xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; + + spi_message_init_with_transfers(&st->msg, st->xfers, 2); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +/* + * This executes a data sample transfer for when the device connections are + * in "4-wire" mode, selected when the adi,sdi-pin device tree property is + * set to "cs". In this connection mode, the controller CS pin is connected to + * ADC SDI pin and a GPIO is connected to ADC CNV pin. + * The GPIO connected to ADC CNV pin is set outside of the SPI transfer. + */ +static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + unsigned int cnv_to_sdi_time = AD4000_TCONV_NS; + struct spi_transfer *xfers = st->xfers; + + /* + * Dummy transfer to cause enough delay between CNV going high and SDI + * going low. + */ + xfers[0].cs_off = 1; + xfers[0].delay.value = cnv_to_sdi_time; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &st->scan.data; + xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); + + spi_message_init_with_transfers(&st->msg, st->xfers, 2); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +static int ad4000_config(struct ad4000_state *st) +{ + unsigned int reg_val = AD4000_CONFIG_REG_DEFAULT; + + if (device_property_present(&st->spi->dev, "adi,high-z-input")) + reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1); + + return ad4000_write_reg(st, reg_val); +} + +static int ad4000_probe(struct spi_device *spi) +{ + const struct ad4000_chip_info *chip; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4000_state *st; + int gain_idx, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + chip = spi_get_device_match_data(spi); + if (!chip) + return -EINVAL; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies), + ad4000_power_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable power supplies\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get ref regulator reference\n"); + st->vref_mv = ret / 1000; + + st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_HIGH); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get CNV GPIO"); + + ret = device_property_match_property_string(dev, "adi,sdi-pin", + ad4000_sdi_pin, + ARRAY_SIZE(ad4000_sdi_pin)); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "getting adi,sdi-pin property failed\n"); + + /* Default to usual SPI connections if pin properties are not present */ + st->sdi_pin = ret == -EINVAL ? AD4000_SDI_MOSI : ret; + switch (st->sdi_pin) { + case AD4000_SDI_MOSI: + indio_dev->info = &ad4000_reg_access_info; + indio_dev->channels = &chip->reg_access_chan_spec; + + /* + * In "3-wire mode", the ADC SDI line must be kept high when + * data is not being clocked out of the controller. + * Request the SPI controller to make MOSI idle high. + */ + spi->mode |= SPI_MOSI_IDLE_HIGH; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + if (ret) + return ret; + + ret = ad4000_config(st); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to config device\n"); + + break; + case AD4000_SDI_VIO: + indio_dev->info = &ad4000_info; + indio_dev->channels = &chip->chan_spec; + ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + if (ret) + return ret; + + break; + case AD4000_SDI_CS: + indio_dev->info = &ad4000_info; + indio_dev->channels = &chip->chan_spec; + ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels); + if (ret) + return ret; + + break; + case AD4000_SDI_GND: + return dev_err_probe(dev, -EPROTONOSUPPORT, + "Unsupported connection mode\n"); + + default: + return dev_err_probe(dev, -EINVAL, "Unrecognized connection mode\n"); + } + + indio_dev->name = chip->dev_name; + indio_dev->num_channels = 1; + + devm_mutex_init(dev, &st->lock); + + st->gain_milli = 1000; + if (chip->has_hardware_gain) { + ret = device_property_read_u16(dev, "adi,gain-milli", + &st->gain_milli); + if (!ret) { + /* Match gain value from dt to one of supported gains */ + gain_idx = find_closest(st->gain_milli, ad4000_gains, + ARRAY_SIZE(ad4000_gains)); + st->gain_milli = ad4000_gains[gain_idx]; + } else { + return dev_err_probe(dev, ret, + "Failed to read gain property\n"); + } + } + + ad4000_fill_scale_tbl(st, indio_dev->channels); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad4000_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4000_id[] = { + { "ad4000", (kernel_ulong_t)&ad4000_chip_info }, + { "ad4001", (kernel_ulong_t)&ad4001_chip_info }, + { "ad4002", (kernel_ulong_t)&ad4002_chip_info }, + { "ad4003", (kernel_ulong_t)&ad4003_chip_info }, + { "ad4004", (kernel_ulong_t)&ad4004_chip_info }, + { "ad4005", (kernel_ulong_t)&ad4005_chip_info }, + { "ad4006", (kernel_ulong_t)&ad4006_chip_info }, + { "ad4007", (kernel_ulong_t)&ad4007_chip_info }, + { "ad4008", (kernel_ulong_t)&ad4008_chip_info }, + { "ad4010", (kernel_ulong_t)&ad4010_chip_info }, + { "ad4011", (kernel_ulong_t)&ad4011_chip_info }, + { "ad4020", (kernel_ulong_t)&ad4020_chip_info }, + { "ad4021", (kernel_ulong_t)&ad4021_chip_info }, + { "ad4022", (kernel_ulong_t)&ad4022_chip_info }, + { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info }, + { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4000_id); + +static const struct of_device_id ad4000_of_match[] = { + { .compatible = "adi,ad4000", .data = &ad4000_chip_info }, + { .compatible = "adi,ad4001", .data = &ad4001_chip_info }, + { .compatible = "adi,ad4002", .data = &ad4002_chip_info }, + { .compatible = "adi,ad4003", .data = &ad4003_chip_info }, + { .compatible = "adi,ad4004", .data = &ad4004_chip_info }, + { .compatible = "adi,ad4005", .data = &ad4005_chip_info }, + { .compatible = "adi,ad4006", .data = &ad4006_chip_info }, + { .compatible = "adi,ad4007", .data = &ad4007_chip_info }, + { .compatible = "adi,ad4008", .data = &ad4008_chip_info }, + { .compatible = "adi,ad4010", .data = &ad4010_chip_info }, + { .compatible = "adi,ad4011", .data = &ad4011_chip_info }, + { .compatible = "adi,ad4020", .data = &ad4020_chip_info }, + { .compatible = "adi,ad4021", .data = &ad4021_chip_info }, + { .compatible = "adi,ad4022", .data = &ad4022_chip_info }, + { .compatible = "adi,adaq4001", .data = &adaq4001_chip_info }, + { .compatible = "adi,adaq4003", .data = &adaq4003_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4000_of_match); + +static struct spi_driver ad4000_driver = { + .driver = { + .name = "ad4000", + .of_match_table = ad4000_of_match, + }, + .probe = ad4000_probe, + .id_table = ad4000_id, +}; +module_spi_driver(ad4000_driver); + +MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c new file mode 100644 index 000000000000..595ec4158e73 --- /dev/null +++ b/drivers/iio/adc/ad4695.c @@ -0,0 +1,1185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI ADC driver for Analog Devices Inc. AD4695 and similar chips + * + * https://www.analog.com/en/products/ad4695.html + * https://www.analog.com/en/products/ad4696.html + * https://www.analog.com/en/products/ad4697.html + * https://www.analog.com/en/products/ad4698.html + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 BayLibre, SAS + */ + +#include <linux/align.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/minmax.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/units.h> + +#include <dt-bindings/iio/adi,ad4695.h> + +/* AD4695 registers */ +#define AD4695_REG_SPI_CONFIG_A 0x0000 +#define AD4695_REG_SPI_CONFIG_A_SW_RST (BIT(7) | BIT(0)) +#define AD4695_REG_SPI_CONFIG_A_ADDR_DIR BIT(5) +#define AD4695_REG_SPI_CONFIG_B 0x0001 +#define AD4695_REG_SPI_CONFIG_B_INST_MODE BIT(7) +#define AD4695_REG_DEVICE_TYPE 0x0003 +#define AD4695_REG_SCRATCH_PAD 0x000A +#define AD4695_REG_VENDOR_L 0x000C +#define AD4695_REG_VENDOR_H 0x000D +#define AD4695_REG_LOOP_MODE 0x000E +#define AD4695_REG_SPI_CONFIG_C 0x0010 +#define AD4695_REG_SPI_CONFIG_C_MB_STRICT BIT(7) +#define AD4695_REG_SPI_STATUS 0x0011 +#define AD4695_REG_STATUS 0x0014 +#define AD4695_REG_ALERT_STATUS1 0x0015 +#define AD4695_REG_ALERT_STATUS2 0x0016 +#define AD4695_REG_CLAMP_STATUS 0x001A +#define AD4695_REG_SETUP 0x0020 +#define AD4695_REG_SETUP_LDO_EN BIT(4) +#define AD4695_REG_SETUP_SPI_MODE BIT(2) +#define AD4695_REG_SETUP_SPI_CYC_CTRL BIT(1) +#define AD4695_REG_REF_CTRL 0x0021 +#define AD4695_REG_REF_CTRL_OV_MODE BIT(7) +#define AD4695_REG_REF_CTRL_VREF_SET GENMASK(4, 2) +#define AD4695_REG_REF_CTRL_REFHIZ_EN BIT(1) +#define AD4695_REG_REF_CTRL_REFBUF_EN BIT(0) +#define AD4695_REG_SEQ_CTRL 0x0022 +#define AD4695_REG_SEQ_CTRL_STD_SEQ_EN BIT(7) +#define AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS GENMASK(6, 0) +#define AD4695_REG_AC_CTRL 0x0023 +#define AD4695_REG_STD_SEQ_CONFIG 0x0024 +#define AD4695_REG_GPIO_CTRL 0x0026 +#define AD4695_REG_GP_MODE 0x0027 +#define AD4695_REG_TEMP_CTRL 0x0029 +#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0) +#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n)) +#define AD4695_REG_CONFIG_IN_MODE BIT(6) +#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4) +#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3) +#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n))) +#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n))) +#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n))) +#define AD4695_REG_OFFSET_IN(n) (0x00A0 | (2 * (n))) +#define AD4695_REG_GAIN_IN(n) (0x00C0 | (2 * (n))) +#define AD4695_REG_AS_SLOT(n) (0x0100 | (n)) +#define AD4695_REG_AS_SLOT_INX GENMASK(3, 0) + +/* Conversion mode commands */ +#define AD4695_CMD_EXIT_CNV_MODE 0x0A +#define AD4695_CMD_TEMP_CHAN 0x0F +#define AD4695_CMD_VOLTAGE_CHAN(n) (0x10 | (n)) + +/* timing specs */ +#define AD4695_T_CONVERT_NS 415 +#define AD4695_T_WAKEUP_HW_MS 3 +#define AD4695_T_WAKEUP_SW_MS 3 +#define AD4695_T_REFBUF_MS 100 +#define AD4695_T_REGCONFIG_NS 20 +#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA) + +/* Max number of voltage input channels. */ +#define AD4695_MAX_CHANNELS 16 +/* Max size of 1 raw sample in bytes. */ +#define AD4695_MAX_CHANNEL_SIZE 2 + +enum ad4695_in_pair { + AD4695_IN_PAIR_REFGND, + AD4695_IN_PAIR_COM, + AD4695_IN_PAIR_EVEN_ODD, +}; + +struct ad4695_chip_info { + const char *name; + int max_sample_rate; + u32 t_acq_ns; + u8 num_voltage_inputs; +}; + +struct ad4695_channel_config { + unsigned int channel; + bool highz_en; + bool bipolar; + enum ad4695_in_pair pin_pairing; + unsigned int common_mode_mv; +}; + +struct ad4695_state { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *regmap16; + struct gpio_desc *reset_gpio; + /* voltages channels plus temperature and timestamp */ + struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2]; + struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS]; + const struct ad4695_chip_info *chip_info; + /* Reference voltage. */ + unsigned int vref_mv; + /* Common mode input pin voltage. */ + unsigned int com_mv; + /* 1 per voltage and temperature chan plus 1 xfer to trigger 1st CNV */ + struct spi_transfer buf_read_xfer[AD4695_MAX_CHANNELS + 2]; + struct spi_message buf_read_msg; + /* Raw conversion data received. */ + u8 buf[ALIGN((AD4695_MAX_CHANNELS + 2) * AD4695_MAX_CHANNEL_SIZE, + sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); + u16 raw_data; + /* Commands to send for single conversion. */ + u16 cnv_cmd; + u8 cnv_cmd2; +}; + +static const struct regmap_range ad4695_regmap_rd_ranges[] = { + regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B), + regmap_reg_range(AD4695_REG_DEVICE_TYPE, AD4695_REG_DEVICE_TYPE), + regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD), + regmap_reg_range(AD4695_REG_VENDOR_L, AD4695_REG_LOOP_MODE), + regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS), + regmap_reg_range(AD4695_REG_STATUS, AD4695_REG_ALERT_STATUS2), + regmap_reg_range(AD4695_REG_CLAMP_STATUS, AD4695_REG_CLAMP_STATUS), + regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL), + regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL), + regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)), + regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)), +}; + +static const struct regmap_access_table ad4695_regmap_rd_table = { + .yes_ranges = ad4695_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_rd_ranges), +}; + +static const struct regmap_range ad4695_regmap_wr_ranges[] = { + regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B), + regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD), + regmap_reg_range(AD4695_REG_LOOP_MODE, AD4695_REG_LOOP_MODE), + regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS), + regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL), + regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL), + regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)), + regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)), +}; + +static const struct regmap_access_table ad4695_regmap_wr_table = { + .yes_ranges = ad4695_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_wr_ranges), +}; + +static const struct regmap_config ad4695_regmap_config = { + .name = "ad4695-8", + .reg_bits = 16, + .val_bits = 8, + .max_register = AD4695_REG_AS_SLOT(127), + .rd_table = &ad4695_regmap_rd_table, + .wr_table = &ad4695_regmap_wr_table, + .can_multi_write = true, +}; + +static const struct regmap_range ad4695_regmap16_rd_ranges[] = { + regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG), + regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)), +}; + +static const struct regmap_access_table ad4695_regmap16_rd_table = { + .yes_ranges = ad4695_regmap16_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_rd_ranges), +}; + +static const struct regmap_range ad4695_regmap16_wr_ranges[] = { + regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG), + regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)), +}; + +static const struct regmap_access_table ad4695_regmap16_wr_table = { + .yes_ranges = ad4695_regmap16_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_wr_ranges), +}; + +static const struct regmap_config ad4695_regmap16_config = { + .name = "ad4695-16", + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = AD4695_REG_GAIN_IN(15), + .rd_table = &ad4695_regmap16_rd_table, + .wr_table = &ad4695_regmap16_wr_table, + .can_multi_write = true, +}; + +static const struct iio_chan_spec ad4695_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct iio_chan_spec ad4695_temp_channel_template = { + .address = AD4695_CMD_TEMP_CHAN, + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct iio_chan_spec ad4695_soft_timestamp_channel_template = + IIO_CHAN_SOFT_TIMESTAMP(0); + +static const char * const ad4695_power_supplies[] = { + "avdd", "vio" +}; + +static const struct ad4695_chip_info ad4695_chip_info = { + .name = "ad4695", + .max_sample_rate = 500 * KILO, + .t_acq_ns = 1715, + .num_voltage_inputs = 16, +}; + +static const struct ad4695_chip_info ad4696_chip_info = { + .name = "ad4696", + .max_sample_rate = 1 * MEGA, + .t_acq_ns = 715, + .num_voltage_inputs = 16, +}; + +static const struct ad4695_chip_info ad4697_chip_info = { + .name = "ad4697", + .max_sample_rate = 500 * KILO, + .t_acq_ns = 1715, + .num_voltage_inputs = 8, +}; + +static const struct ad4695_chip_info ad4698_chip_info = { + .name = "ad4698", + .max_sample_rate = 1 * MEGA, + .t_acq_ns = 715, + .num_voltage_inputs = 8, +}; + +/** + * ad4695_set_single_cycle_mode - Set the device in single cycle mode + * @st: The AD4695 state + * @channel: The first channel to read + * + * As per the datasheet, to enable single cycle mode, we need to set + * STD_SEQ_EN=0, NUM_SLOTS_AS=0 and CYC_CTRL=1 (Table 15). Setting SPI_MODE=1 + * triggers the first conversion using the channel in AS_SLOT0. + * + * Context: can sleep, must be called with iio_device_claim_direct held + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_set_single_cycle_mode(struct ad4695_state *st, + unsigned int channel) +{ + int ret; + + ret = regmap_clear_bits(st->regmap, AD4695_REG_SEQ_CTRL, + AD4695_REG_SEQ_CTRL_STD_SEQ_EN | + AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(0), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, channel)); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_SPI_MODE | + AD4695_REG_SETUP_SPI_CYC_CTRL); +} + +/** + * ad4695_enter_advanced_sequencer_mode - Put the ADC in advanced sequencer mode + * @st: The driver state + * @n: The number of slots to use - must be >= 2, <= 128 + * + * As per the datasheet, to enable advanced sequencer, we need to set + * STD_SEQ_EN=0, NUM_SLOTS_AS=n-1 and CYC_CTRL=0 (Table 15). Setting SPI_MODE=1 + * triggers the first conversion using the channel in AS_SLOT0. + * + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n) +{ + int ret; + + ret = regmap_update_bits(st->regmap, AD4695_REG_SEQ_CTRL, + AD4695_REG_SEQ_CTRL_STD_SEQ_EN | + AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, + FIELD_PREP(AD4695_REG_SEQ_CTRL_STD_SEQ_EN, 0) | + FIELD_PREP(AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, n - 1)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_SPI_MODE | AD4695_REG_SETUP_SPI_CYC_CTRL, + FIELD_PREP(AD4695_REG_SETUP_SPI_MODE, 1) | + FIELD_PREP(AD4695_REG_SETUP_SPI_CYC_CTRL, 0)); +} + +/** + * ad4695_exit_conversion_mode - Exit conversion mode + * @st: The AD4695 state + * + * Sends SPI command to exit conversion mode. + * + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_exit_conversion_mode(struct ad4695_state *st) +{ + struct spi_transfer xfer = { + .tx_buf = &st->cnv_cmd2, + .len = 1, + .delay.value = AD4695_T_REGCONFIG_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + }; + + /* + * Technically, could do a 5-bit transfer, but shifting to start of + * 8 bits instead for better SPI controller support. + */ + st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv) +{ + u8 val; + + if (vref_mv >= 2400 && vref_mv <= 2750) + val = 0; + else if (vref_mv > 2750 && vref_mv <= 3250) + val = 1; + else if (vref_mv > 3250 && vref_mv <= 3750) + val = 2; + else if (vref_mv > 3750 && vref_mv <= 4500) + val = 3; + else if (vref_mv > 4500 && vref_mv <= 5100) + val = 4; + else + return -EINVAL; + + return regmap_update_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_VREF_SET, + FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val)); +} + +static int ad4695_write_chn_cfg(struct ad4695_state *st, + struct ad4695_channel_config *cfg) +{ + u32 mask, val; + + mask = AD4695_REG_CONFIG_IN_MODE; + val = FIELD_PREP(AD4695_REG_CONFIG_IN_MODE, cfg->bipolar ? 1 : 0); + + mask |= AD4695_REG_CONFIG_IN_PAIR; + val |= FIELD_PREP(AD4695_REG_CONFIG_IN_PAIR, cfg->pin_pairing); + + mask |= AD4695_REG_CONFIG_IN_AINHIGHZ_EN; + val |= FIELD_PREP(AD4695_REG_CONFIG_IN_AINHIGHZ_EN, + cfg->highz_en ? 1 : 0); + + return regmap_update_bits(st->regmap, + AD4695_REG_CONFIG_IN(cfg->channel), + mask, val); +} + +static int ad4695_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct spi_transfer *xfer; + u8 temp_chan_bit = st->chip_info->num_voltage_inputs; + u32 bit, num_xfer, num_slots; + u32 temp_en = 0; + int ret; + + /* + * We are using the advanced sequencer since it is the only way to read + * multiple channels that allows individual configuration of each + * voltage input channel. Slot 0 in the advanced sequencer is used to + * account for the gap between trigger polls - we don't read data from + * this slot. Each enabled voltage channel is assigned a slot starting + * with slot 1. + */ + num_slots = 1; + + memset(st->buf_read_xfer, 0, sizeof(st->buf_read_xfer)); + + /* First xfer is only to trigger conversion of slot 1, so no rx. */ + xfer = &st->buf_read_xfer[0]; + xfer->cs_change = 1; + xfer->delay.value = st->chip_info->t_acq_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + num_xfer = 1; + + iio_for_each_active_channel(indio_dev, bit) { + xfer = &st->buf_read_xfer[num_xfer]; + xfer->bits_per_word = 16; + xfer->rx_buf = &st->buf[(num_xfer - 1) * 2]; + xfer->len = 2; + xfer->cs_change = 1; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + if (bit == temp_chan_bit) { + temp_en = 1; + } else { + ret = regmap_write(st->regmap, + AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit)); + if (ret) + return ret; + + num_slots++; + } + + num_xfer++; + } + + /* + * The advanced sequencer requires that at least 2 slots are enabled. + * Since slot 0 is always used for other purposes, we need only 1 + * enabled voltage channel to meet this requirement. If the temperature + * channel is the only enabled channel, we need to add one more slot + * in the sequence but not read from it. + */ + if (num_slots < 2) { + /* move last xfer so we can insert one more xfer before it */ + st->buf_read_xfer[num_xfer] = *xfer; + num_xfer++; + + /* modify 2nd to last xfer for extra slot */ + memset(xfer, 0, sizeof(*xfer)); + xfer->cs_change = 1; + xfer->delay.value = st->chip_info->t_acq_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + xfer++; + + /* and add the extra slot in the sequencer */ + ret = regmap_write(st->regmap, + AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, 0)); + if (ret) + return ret; + + num_slots++; + } + + /* + * Don't keep CS asserted after last xfer. Also triggers conversion of + * slot 0. + */ + xfer->cs_change = 0; + + /* + * Temperature channel isn't included in the sequence, but rather + * controlled by setting a bit in the TEMP_CTRL register. + */ + + ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL, + AD4695_REG_TEMP_CTRL_TEMP_EN, + FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, temp_en)); + if (ret) + return ret; + + spi_message_init_with_transfers(&st->buf_read_msg, st->buf_read_xfer, + num_xfer); + + ret = spi_optimize_message(st->spi, &st->buf_read_msg); + if (ret) + return ret; + + /* This triggers conversion of slot 0. */ + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + if (ret) + spi_unoptimize_message(&st->buf_read_msg); + + return ret; +} + +static int ad4695_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4695_exit_conversion_mode(st); + if (ret) + return ret; + + spi_unoptimize_message(&st->buf_read_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4695_buffer_setup_ops = { + .preenable = ad4695_buffer_preenable, + .postdisable = ad4695_buffer_postdisable, +}; + +static irqreturn_t ad4695_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4695_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->buf_read_msg); + if (ret) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->buf, pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/** + * ad4695_read_one_sample - Read a single sample using single-cycle mode + * @st: The AD4695 state + * @address: The address of the channel to read + * + * Upon successful return, the sample will be stored in `st->raw_data`. + * + * Context: can sleep, must be called with iio_device_claim_direct held + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address) +{ + struct spi_transfer xfer[2] = { }; + int ret, i = 0; + + ret = ad4695_set_single_cycle_mode(st, address); + if (ret) + return ret; + + /* + * Setting the first channel to the temperature channel isn't supported + * in single-cycle mode, so we have to do an extra xfer to read the + * temperature. + */ + if (address == AD4695_CMD_TEMP_CHAN) { + /* We aren't reading, so we can make this a short xfer. */ + st->cnv_cmd2 = AD4695_CMD_TEMP_CHAN << 3; + xfer[0].tx_buf = &st->cnv_cmd2; + xfer[0].len = 1; + xfer[0].cs_change = 1; + xfer[0].cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + i = 1; + } + + /* Then read the result and exit conversion mode. */ + st->cnv_cmd = AD4695_CMD_EXIT_CNV_MODE << 11; + xfer[i].bits_per_word = 16; + xfer[i].tx_buf = &st->cnv_cmd; + xfer[i].rx_buf = &st->raw_data; + xfer[i].len = 2; + + return spi_sync_transfer(st->spi, xfer, i + 1); +} + +static int ad4695_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index]; + u8 realbits = chan->scan_type.realbits; + unsigned int reg_val; + int ret, tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = ad4695_read_one_sample(st, chan->address); + if (ret) + return ret; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(st->raw_data, realbits - 1); + else + *val = st->raw_data; + + return IIO_VAL_INT; + } + unreachable(); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = st->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */ + *val = st->vref_mv * -556; + *val2 = 16; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (cfg->pin_pairing == AD4695_IN_PAIR_COM) + *val = st->com_mv * (1 << realbits) / st->vref_mv; + else if (cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) + *val = cfg->common_mode_mv * (1 << realbits) / st->vref_mv; + else + *val = 0; + + return IIO_VAL_INT; + case IIO_TEMP: + /* T_offset (°C) = -725 mV / (-1.8 mV/°C) */ + /* T_offset (raw) = T_offset (°C) * (-1.8 mV/°C) * 2^16 / V_REF (mV) */ + *val = -47513600; + *val2 = st->vref_mv; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = regmap_read(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + ®_val); + if (ret) + return ret; + + *val = reg_val; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + } + unreachable(); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = regmap_read(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + ®_val); + if (ret) + return ret; + + tmp = sign_extend32(reg_val, 15); + + *val = tmp / 4; + *val2 = abs(tmp) % 4 * MICRO / 4; + + if (tmp < 0 && *val2) { + *val *= -1; + *val2 *= -1; + } + + return IIO_VAL_INT_PLUS_MICRO; + } + unreachable(); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4695_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int reg_val; + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (val < 0 || val2 < 0) + reg_val = 0; + else if (val > 1) + reg_val = U16_MAX; + else + reg_val = (val * (1 << 16) + + mul_u64_u32_div(val2, 1 << 16, + MICRO)) / 2; + + return regmap_write(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + reg_val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + if (val2 >= 0 && val > S16_MAX / 4) + reg_val = S16_MAX; + else if ((val2 < 0 ? -val : val) < S16_MIN / 4) + reg_val = S16_MIN; + else if (val2 < 0) + reg_val = clamp_t(int, + -(val * 4 + -val2 * 4 / MICRO), + S16_MIN, S16_MAX); + else if (val < 0) + reg_val = clamp_t(int, + val * 4 - val2 * 4 / MICRO, + S16_MIN, S16_MAX); + else + reg_val = clamp_t(int, + val * 4 + val2 * 4 / MICRO, + S16_MIN, S16_MAX); + + return regmap_write(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + reg_val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } + } + unreachable(); +} + +static int ad4695_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + static const int ad4695_calibscale_available[6] = { + /* Range of 0 (inclusive) to 2 (exclusive) */ + 0, 15, 1, 15, U16_MAX, 15 + }; + static const int ad4695_calibbias_available[6] = { + /* + * Datasheet says FSR/8 which translates to signed/4. The step + * depends on oversampling ratio which is always 1 for now. + */ + S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4 + }; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad4695_calibscale_available; + *type = IIO_VAL_FRACTIONAL_LOG2; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad4695_calibbias_available; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad4695_state *st = iio_priv(indio_dev); + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + if (readval) { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_rd_table)) + return regmap_read(st->regmap, reg, readval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_rd_table)) + return regmap_read(st->regmap16, reg, readval); + } else { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_wr_table)) + return regmap_write(st->regmap, reg, writeval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_wr_table)) + return regmap_write(st->regmap16, reg, writeval); + } + } + + return -EINVAL; +} + +static const struct iio_info ad4695_info = { + .read_raw = &ad4695_read_raw, + .write_raw = &ad4695_write_raw, + .read_avail = &ad4695_read_avail, + .debugfs_reg_access = &ad4695_debugfs_reg_access, +}; + +static int ad4695_parse_channel_cfg(struct ad4695_state *st) +{ + struct device *dev = &st->spi->dev; + struct ad4695_channel_config *chan_cfg; + struct iio_chan_spec *iio_chan; + int ret, i; + + /* populate defaults */ + for (i = 0; i < st->chip_info->num_voltage_inputs; i++) { + chan_cfg = &st->channels_cfg[i]; + iio_chan = &st->iio_chan[i]; + + chan_cfg->highz_en = true; + chan_cfg->channel = i; + + *iio_chan = ad4695_channel_template; + iio_chan->channel = i; + iio_chan->scan_index = i; + iio_chan->address = AD4695_CMD_VOLTAGE_CHAN(i); + } + + /* modify based on firmware description */ + device_for_each_child_node_scoped(dev, child) { + u32 reg, val; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "failed to read reg property (%s)\n", + fwnode_get_name(child)); + + if (reg >= st->chip_info->num_voltage_inputs) + return dev_err_probe(dev, -EINVAL, + "reg out of range (%s)\n", + fwnode_get_name(child)); + + iio_chan = &st->iio_chan[reg]; + chan_cfg = &st->channels_cfg[reg]; + + chan_cfg->highz_en = + !fwnode_property_read_bool(child, "adi,no-high-z"); + chan_cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); + + ret = fwnode_property_read_u32(child, "common-mode-channel", + &val); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "failed to read common-mode-channel (%s)\n", + fwnode_get_name(child)); + + if (ret == -EINVAL || val == AD4695_COMMON_MODE_REFGND) + chan_cfg->pin_pairing = AD4695_IN_PAIR_REFGND; + else if (val == AD4695_COMMON_MODE_COM) + chan_cfg->pin_pairing = AD4695_IN_PAIR_COM; + else + chan_cfg->pin_pairing = AD4695_IN_PAIR_EVEN_ODD; + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD && + val % 2 == 0) + return dev_err_probe(dev, -EINVAL, + "common-mode-channel must be odd number (%s)\n", + fwnode_get_name(child)); + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD && + val != reg + 1) + return dev_err_probe(dev, -EINVAL, + "common-mode-channel must be next consecutive channel (%s)\n", + fwnode_get_name(child)); + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) { + char name[5]; + + snprintf(name, sizeof(name), "in%d", reg + 1); + + ret = devm_regulator_get_enable_read_voltage(dev, name); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get %s voltage (%s)\n", + name, fwnode_get_name(child)); + + chan_cfg->common_mode_mv = ret / 1000; + } + + if (chan_cfg->bipolar && + chan_cfg->pin_pairing == AD4695_IN_PAIR_REFGND) + return dev_err_probe(dev, -EINVAL, + "bipolar mode is not available for inputs paired with REFGND (%s).\n", + fwnode_get_name(child)); + + if (chan_cfg->bipolar) + iio_chan->scan_type.sign = 's'; + + ret = ad4695_write_chn_cfg(st, chan_cfg); + if (ret) + return ret; + } + + /* Temperature channel must be next scan index after voltage channels. */ + st->iio_chan[i] = ad4695_temp_channel_template; + st->iio_chan[i].scan_index = i; + i++; + + st->iio_chan[i] = ad4695_soft_timestamp_channel_template; + st->iio_chan[i].scan_index = i; + + return 0; +} + +static int ad4695_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad4695_state *st; + struct iio_dev *indio_dev; + struct gpio_desc *cnv_gpio; + bool use_internal_ldo_supply; + bool use_internal_ref_buffer; + int ret; + + cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(cnv_gpio), + "Failed to get CNV GPIO\n"); + + /* Driver currently requires CNV pin to be connected to SPI CS */ + if (cnv_gpio) + return dev_err_probe(dev, -ENODEV, + "CNV GPIO is not supported\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + /* Registers cannot be read at the max allowable speed */ + spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ; + + st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + st->regmap16 = devm_regmap_init_spi(spi, &ad4695_regmap16_config); + if (IS_ERR(st->regmap16)) + return dev_err_probe(dev, PTR_ERR(st->regmap16), + "Failed to initialize regmap16\n"); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4695_power_supplies), + ad4695_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable power supplies\n"); + + /* If LDO_IN supply is present, then we are using internal LDO. */ + ret = devm_regulator_get_enable_optional(dev, "ldo-in"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to enable LDO_IN supply\n"); + + use_internal_ldo_supply = ret == 0; + + if (!use_internal_ldo_supply) { + /* Otherwise we need an external VDD supply. */ + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to enable VDD supply\n"); + } + + /* If REFIN supply is given, then we are using internal buffer */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n"); + + if (ret != -ENODEV) { + st->vref_mv = ret / 1000; + use_internal_ref_buffer = true; + } else { + /* Otherwise, we need an external reference. */ + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get REF voltage\n"); + + st->vref_mv = ret / 1000; + use_internal_ref_buffer = false; + } + + ret = devm_regulator_get_enable_read_voltage(dev, "com"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get COM voltage\n"); + + st->com_mv = ret == -ENODEV ? 0 : ret / 1000; + + /* + * Reset the device using hardware reset if available or fall back to + * software reset. + */ + + st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return PTR_ERR(st->reset_gpio); + + if (st->reset_gpio) { + gpiod_set_value(st->reset_gpio, 0); + msleep(AD4695_T_WAKEUP_HW_MS); + } else { + ret = regmap_write(st->regmap, AD4695_REG_SPI_CONFIG_A, + AD4695_REG_SPI_CONFIG_A_SW_RST); + if (ret) + return ret; + + msleep(AD4695_T_WAKEUP_SW_MS); + } + + /* Needed for regmap16 to be able to work correctly. */ + ret = regmap_set_bits(st->regmap, AD4695_REG_SPI_CONFIG_A, + AD4695_REG_SPI_CONFIG_A_ADDR_DIR); + if (ret) + return ret; + + /* Disable internal LDO if it isn't needed. */ + ret = regmap_update_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_LDO_EN, + FIELD_PREP(AD4695_REG_SETUP_LDO_EN, + use_internal_ldo_supply ? 1 : 0)); + if (ret) + return ret; + + /* configure reference supply */ + + if (device_property_present(dev, "adi,no-ref-current-limit")) { + ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_OV_MODE); + if (ret) + return ret; + } + + if (device_property_present(dev, "adi,no-ref-high-z")) { + if (use_internal_ref_buffer) + return dev_err_probe(dev, -EINVAL, + "Cannot disable high-Z mode for internal reference buffer\n"); + + ret = regmap_clear_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_REFHIZ_EN); + if (ret) + return ret; + } + + ret = ad4695_set_ref_voltage(st, st->vref_mv); + if (ret) + return ret; + + if (use_internal_ref_buffer) { + ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_REFBUF_EN); + if (ret) + return ret; + + /* Give the capacitor some time to charge up. */ + msleep(AD4695_T_REFBUF_MS); + } + + ret = ad4695_parse_channel_cfg(st); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad4695_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->iio_chan; + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4695_trigger_handler, + &ad4695_buffer_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4695_spi_id_table[] = { + { .name = "ad4695", .driver_data = (kernel_ulong_t)&ad4695_chip_info }, + { .name = "ad4696", .driver_data = (kernel_ulong_t)&ad4696_chip_info }, + { .name = "ad4697", .driver_data = (kernel_ulong_t)&ad4697_chip_info }, + { .name = "ad4698", .driver_data = (kernel_ulong_t)&ad4698_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4695_spi_id_table); + +static const struct of_device_id ad4695_of_match_table[] = { + { .compatible = "adi,ad4695", .data = &ad4695_chip_info, }, + { .compatible = "adi,ad4696", .data = &ad4696_chip_info, }, + { .compatible = "adi,ad4697", .data = &ad4697_chip_info, }, + { .compatible = "adi,ad4698", .data = &ad4698_chip_info, }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4695_of_match_table); + +static struct spi_driver ad4695_driver = { + .driver = { + .name = "ad4695", + .of_match_table = ad4695_of_match_table, + }, + .probe = ad4695_probe, + .id_table = ad4695_spi_id_table, +}; +module_spi_driver(ad4695_driver); + +MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); +MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>"); +MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index a75837334157..1b59708abf30 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -112,13 +112,13 @@ static int ad7091r5_i2c_probe(struct i2c_client *i2c) static const struct of_device_id ad7091r5_dt_ids[] = { { .compatible = "adi,ad7091r5", .data = &ad7091r5_init_info }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); static const struct i2c_device_id ad7091r5_i2c_ids[] = { - {"ad7091r5", (kernel_ulong_t)&ad7091r5_init_info }, - {} + { "ad7091r5", (kernel_ulong_t)&ad7091r5_init_info }, + { } }; MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c index 700564305057..c9e014d6a77c 100644 --- a/drivers/iio/adc/ad7091r8.c +++ b/drivers/iio/adc/ad7091r8.c @@ -159,7 +159,7 @@ static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg, return spi_write(spi, &st->tx_buf, 2); } -static struct regmap_bus ad7091r8_regmap_bus = { +static const struct regmap_bus ad7091r8_regmap_bus = { .reg_read = ad7091r_regmap_bus_reg_read, .reg_write = ad7091r_regmap_bus_reg_write, .reg_format_endian_default = REGMAP_ENDIAN_BIG, diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 108e9ccab1ef..a5d91933f505 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -382,8 +382,7 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe cfg->vref_mv = 2500; st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); - return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, - 2, st->adc_control); + return 0; default: dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); return -EINVAL; @@ -401,24 +400,17 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co tmp = (cfg->buf_positive << 1) + cfg->buf_negative; val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) | - AD7124_CONFIG_IN_BUFF(tmp); - ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val); - if (ret < 0) - return ret; + AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits); - tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type); - ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK, - tmp, 3); - if (ret < 0) - return ret; - - ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK, - AD7124_FILTER_FS(cfg->odr_sel_bits), 3); + ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val); if (ret < 0) return ret; - return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK, - AD7124_CONFIG_PGA(cfg->pga_bits), 2); + tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type) | + AD7124_FILTER_FS(cfg->odr_sel_bits); + return ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), + AD7124_FILTER_TYPE_MSK | AD7124_FILTER_FS_MSK, + tmp, 3); } static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st) @@ -907,9 +899,9 @@ static int ad7124_setup(struct ad7124_state *st) /* Set the power mode */ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK; st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode); - ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); - if (ret < 0) - return ret; + + st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK; + st->adc_control |= AD7124_ADC_CTRL_MODE(AD_SD_MODE_IDLE); mutex_init(&st->cfgs_lock); INIT_KFIFO(st->live_cfgs_fifo); @@ -927,6 +919,10 @@ static int ad7124_setup(struct ad7124_state *st) ad7124_set_channel_odr(st, i, 10); } + ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); + if (ret < 0) + return ret; + return ret; } @@ -1016,14 +1012,14 @@ static const struct of_device_id ad7124_of_match[] = { .data = &ad7124_chip_info_tbl[ID_AD7124_4], }, { .compatible = "adi,ad7124-8", .data = &ad7124_chip_info_tbl[ID_AD7124_8], }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7124_of_match); static const struct spi_device_id ad71124_ids[] = { { "ad7124-4", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_4] }, { "ad7124-8", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_8] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad71124_ids); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 334ab90991d4..7042ddfdfc03 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -8,6 +8,7 @@ #include <linux/interrupt.h> #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -201,6 +202,7 @@ struct ad7192_chip_info { struct ad7192_state { const struct ad7192_chip_info *chip_info; struct clk *mclk; + struct clk_hw int_clk_hw; u16 int_vref_mv; u32 aincom_mv; u32 fclk; @@ -284,7 +286,7 @@ static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { &ad7192_syscalib_mode_enum), IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, &ad7192_syscalib_mode_enum), - {} + { } }; static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd) @@ -396,25 +398,162 @@ static inline bool ad7192_valid_external_frequency(u32 freq) freq <= AD7192_EXT_FREQ_MHZ_MAX); } -static int ad7192_clock_select(struct ad7192_state *st) +/* + * Position 0 of ad7192_clock_names, xtal, corresponds to clock source + * configuration AD7192_CLK_EXT_MCLK1_2 and position 1, mclk, corresponds to + * AD7192_CLK_EXT_MCLK2 + */ +static const char *const ad7192_clock_names[] = { + "xtal", + "mclk" +}; + +static struct ad7192_state *clk_hw_to_ad7192(struct clk_hw *hw) +{ + return container_of(hw, struct ad7192_state, int_clk_hw); +} + +static unsigned long ad7192_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD7192_INT_FREQ_MHZ; +} + +static int ad7192_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + + return st->clock_sel == AD7192_CLK_INT_CO; +} + +static int ad7192_clk_prepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT_CO; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return ret; + + st->clock_sel = AD7192_CLK_INT_CO; + + return 0; +} + +static void ad7192_clk_unprepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return; + + st->clock_sel = AD7192_CLK_INT; +} + +static const struct clk_ops ad7192_int_clk_ops = { + .recalc_rate = ad7192_clk_recalc_rate, + .is_enabled = ad7192_clk_output_is_enabled, + .prepare = ad7192_clk_prepare, + .unprepare = ad7192_clk_unprepare, +}; + +static int ad7192_register_clk_provider(struct ad7192_state *st) { struct device *dev = &st->sd.spi->dev; - unsigned int clock_sel; + struct clk_init_data init = {}; + int ret; - clock_sel = AD7192_CLK_INT; + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; - /* use internal clock */ - if (!st->mclk) { - if (device_property_read_bool(dev, "adi,int-clock-output-enable")) - clock_sel = AD7192_CLK_INT_CO; - } else { - if (device_property_read_bool(dev, "adi,clock-xtal")) - clock_sel = AD7192_CLK_EXT_MCLK1_2; - else - clock_sel = AD7192_CLK_EXT_MCLK2; + if (!device_property_present(dev, "#clock-cells")) + return 0; + + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-clk", + fwnode_get_name(dev_fwnode(dev))); + if (!init.name) + return -ENOMEM; + + init.ops = &ad7192_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad7192_clock_setup(struct ad7192_state *st) +{ + struct device *dev = &st->sd.spi->dev; + int ret; + + /* + * The following two if branches are kept for backward compatibility but + * the use of the two devicetree properties is highly discouraged. Clock + * configuration should be done according to the bindings. + */ + + if (device_property_read_bool(dev, "adi,int-clock-output-enable")) { + st->clock_sel = AD7192_CLK_INT_CO; + st->fclk = AD7192_INT_FREQ_MHZ; + dev_warn(dev, "Property adi,int-clock-output-enable is deprecated! Check bindings!\n"); + return 0; + } + + if (device_property_read_bool(dev, "adi,clock-xtal")) { + st->clock_sel = AD7192_CLK_EXT_MCLK1_2; + st->mclk = devm_clk_get_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get mclk\n"); + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) + return dev_err_probe(dev, -EINVAL, + "External clock frequency out of bounds\n"); + + dev_warn(dev, "Property adi,clock-xtal is deprecated! Check bindings!\n"); + return 0; + } + + ret = device_property_match_property_string(dev, "clock-names", + ad7192_clock_names, + ARRAY_SIZE(ad7192_clock_names)); + if (ret < 0) { + st->clock_sel = AD7192_CLK_INT; + st->fclk = AD7192_INT_FREQ_MHZ; + + ret = ad7192_register_clk_provider(st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register clock provider\n"); + return 0; } - return clock_sel; + st->clock_sel = AD7192_CLK_EXT_MCLK1_2 + ret; + + st->mclk = devm_clk_get_enabled(dev, ad7192_clock_names[ret]); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get clock source\n"); + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) + return dev_err_probe(dev, -EINVAL, + "External clock frequency out of bounds\n"); + + return 0; } static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) @@ -1275,21 +1414,9 @@ static int ad7192_probe(struct spi_device *spi) if (ret) return ret; - st->fclk = AD7192_INT_FREQ_MHZ; - - st->mclk = devm_clk_get_optional_enabled(dev, "mclk"); - if (IS_ERR(st->mclk)) - return PTR_ERR(st->mclk); - - st->clock_sel = ad7192_clock_select(st); - - if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || - st->clock_sel == AD7192_CLK_EXT_MCLK2) { - st->fclk = clk_get_rate(st->mclk); - if (!ad7192_valid_external_frequency(st->fclk)) - return dev_err_probe(dev, -EINVAL, - "External clock frequency out of bounds\n"); - } + ret = ad7192_clock_setup(st); + if (ret) + return ret; ret = ad7192_setup(indio_dev, dev); if (ret) @@ -1304,7 +1431,7 @@ static const struct of_device_id ad7192_of_match[] = { { .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] }, { .compatible = "adi,ad7194", .data = &ad7192_chip_info_tbl[ID_AD7194] }, { .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad7192_of_match); @@ -1314,7 +1441,7 @@ static const struct spi_device_id ad7192_ids[] = { { "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, { "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, { "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7192_ids); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index bdac020045b4..7949b076fb87 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -123,7 +123,8 @@ static int ad7266_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct ad7266_state *st = iio_priv(indio_dev); - unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength); + unsigned int nr = find_first_bit(scan_mask, + iio_get_masklength(indio_dev)); ad7266_select_input(st, nr); @@ -456,8 +457,8 @@ static int ad7266_probe(struct spi_device *spi) } static const struct spi_device_id ad7266_id[] = { - {"ad7265", 0}, - {"ad7266", 0}, + { "ad7265", 0 }, + { "ad7266", 0 }, { } }; MODULE_DEVICE_TABLE(spi, ad7266_id); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index d4a4e15c8244..35aa39fe4bde 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/delay.h> #include <linux/device.h> @@ -803,16 +804,16 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct ad7280_state *st = iio_priv(indio_dev); - unsigned int *channels; int i, ret; - channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL); + unsigned int *channels __free(kfree) = kcalloc(st->scan_cnt, sizeof(*channels), + GFP_KERNEL); if (!channels) return IRQ_HANDLED; ret = ad7280_read_all_channels(st, st->scan_cnt, channels); if (ret < 0) - goto out; + return IRQ_HANDLED; for (i = 0; i < st->scan_cnt; i++) { unsigned int val; @@ -852,9 +853,6 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) } } -out: - kfree(channels); - return IRQ_HANDLED; } @@ -1092,8 +1090,8 @@ static int ad7280_probe(struct spi_device *spi) } static const struct spi_device_id ad7280_id[] = { - {"ad7280a", 0}, - {} + { "ad7280a", 0 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7280_id); diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index b59b2a51623c..4c7f887adbbf 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -537,14 +537,14 @@ static int ad7291_probe(struct i2c_client *client) static const struct i2c_device_id ad7291_id[] = { { "ad7291" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad7291_id); static const struct of_device_id ad7291_of_match[] = { { .compatible = "adi,ad7291" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad7291_of_match); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index ede80f380911..a398973f313d 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -301,13 +301,13 @@ static int ad7292_probe(struct spi_device *spi) static const struct spi_device_id ad7292_id_table[] = { { "ad7292", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7292_id_table); static const struct of_device_id ad7292_of_match[] = { { .compatible = "adi,ad7292" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7292_of_match); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index c0430f71f592..b35bd4d9ef81 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -109,7 +109,8 @@ static int ad7298_update_scan_mode(struct iio_dev *indio_dev, int scan_count; /* Now compute overall size */ - scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength); + scan_count = bitmap_weight(active_scan_mask, + iio_get_masklength(indio_dev)); command = AD7298_WRITE | st->ext_ref; @@ -354,8 +355,8 @@ static const struct acpi_device_id ad7298_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids); static const struct spi_device_id ad7298_id[] = { - {"ad7298", 0}, - {} + { "ad7298", 0 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7298_id); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index 7568cd0a2b32..e8bddfb0d07d 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -8,9 +8,11 @@ * Datasheets of supported parts: * ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf * ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf + * ad7386/7/8 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7386-7387-7388.pdf * ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf * ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf * ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf + * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf */ #include <linux/align.h> @@ -31,7 +33,7 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -#define MAX_NUM_CHANNELS 4 +#define MAX_NUM_CHANNELS 8 /* 2.5V internal reference voltage */ #define AD7380_INTERNAL_REF_MV 2500 @@ -49,6 +51,8 @@ #define AD7380_REG_ADDR_ALERT_LOW_TH 0x4 #define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5 +#define AD7380_CONFIG1_CH BIT(11) +#define AD7380_CONFIG1_SEQ BIT(10) #define AD7380_CONFIG1_OS_MODE BIT(9) #define AD7380_CONFIG1_OSR GENMASK(8, 6) #define AD7380_CONFIG1_CRC_W BIT(5) @@ -80,6 +84,8 @@ struct ad7380_chip_info { const char *name; const struct iio_chan_spec *channels; unsigned int num_channels; + unsigned int num_simult_channels; + bool has_mux; const char * const *vcm_supplies; unsigned int num_vcm_supplies; const unsigned long *available_scan_masks; @@ -91,82 +97,151 @@ enum { AD7380_SCAN_TYPE_RESOLUTION_BOOST, }; -/* Extended scan types for 14-bit chips. */ -static const struct iio_scan_type ad7380_scan_type_14[] = { +/* Extended scan types for 12-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_12_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit signed chips. */ +static const struct iio_scan_type ad7380_scan_type_14_s[] = { [AD7380_SCAN_TYPE_NORMAL] = { .sign = 's', .realbits = 14, .storagebits = 16, - .endianness = IIO_CPU + .endianness = IIO_CPU, }, [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { .sign = 's', .realbits = 16, .storagebits = 16, - .endianness = IIO_CPU + .endianness = IIO_CPU, }, }; -/* Extended scan types for 16-bit chips. */ -static const struct iio_scan_type ad7380_scan_type_16[] = { +/* Extended scan types for 14-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_14_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit signed_chips. */ +static const struct iio_scan_type ad7380_scan_type_16_s[] = { [AD7380_SCAN_TYPE_NORMAL] = { .sign = 's', .realbits = 16, .storagebits = 16, - .endianness = IIO_CPU + .endianness = IIO_CPU, }, [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { .sign = 's', .realbits = 18, .storagebits = 32, - .endianness = IIO_CPU + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_16_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, }, }; -#define AD7380_CHANNEL(index, bits, diff) { \ - .type = IIO_VOLTAGE, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ - .info_mask_shared_by_type_available = \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ - .indexed = 1, \ - .differential = (diff), \ - .channel = (diff) ? (2 * (index)) : (index), \ - .channel2 = (diff) ? (2 * (index) + 1) : 0, \ - .scan_index = (index), \ - .has_ext_scan_type = 1, \ - .ext_scan_type = ad7380_scan_type_##bits, \ - .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits),\ +#define AD7380_CHANNEL(index, bits, diff, sign) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .indexed = 1, \ + .differential = (diff), \ + .channel = (diff) ? (2 * (index)) : (index), \ + .channel2 = (diff) ? (2 * (index) + 1) : 0, \ + .scan_index = (index), \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad7380_scan_type_##bits##_##sign, \ + .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \ } -#define DEFINE_AD7380_2_CHANNEL(name, bits, diff) \ +#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ - AD7380_CHANNEL(0, bits, diff), \ - AD7380_CHANNEL(1, bits, diff), \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ IIO_CHAN_SOFT_TIMESTAMP(2), \ } -#define DEFINE_AD7380_4_CHANNEL(name, bits, diff) \ +#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ - AD7380_CHANNEL(0, bits, diff), \ - AD7380_CHANNEL(1, bits, diff), \ - AD7380_CHANNEL(2, bits, diff), \ - AD7380_CHANNEL(3, bits, diff), \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ IIO_CHAN_SOFT_TIMESTAMP(4), \ } +#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ + AD7380_CHANNEL(4, bits, diff, sign), \ + AD7380_CHANNEL(5, bits, diff, sign), \ + AD7380_CHANNEL(6, bits, diff, sign), \ + AD7380_CHANNEL(7, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + /* fully differential */ -DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1); -DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1); -DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1); -DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1); +DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s); +DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s); +DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s); +DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s); /* pseudo differential */ -DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0); -DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0); -DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0); -DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0); +DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s); +DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s); +DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0, s); +DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0, s); + +/* Single ended */ +DEFINE_AD7380_4_CHANNEL(ad7386_channels, 16, 0, u); +DEFINE_AD7380_4_CHANNEL(ad7387_channels, 14, 0, u); +DEFINE_AD7380_4_CHANNEL(ad7388_channels, 12, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u); static const char * const ad7380_2_channel_vcm_supplies[] = { "aina", "ainb", @@ -187,6 +262,60 @@ static const unsigned long ad7380_4_channel_scan_masks[] = { 0 }; +/* + * Single ended parts have a 2:1 multiplexer in front of each ADC. + * + * From an IIO point of view, all inputs are exported, i.e ad7386/7/8 + * export 4 channels and ad7386-4/7-4/8-4 export 8 channels. + * + * Inputs AinX0 of multiplexers correspond to the first half of IIO channels + * (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or + * 4-7). Example for AD7386/7/8 (2 channels parts): + * + * IIO | AD7386/7/8 + * | +---------------------------- + * | | _____ ______ + * | | | | | | + * voltage0 | AinA0 --|--->| | | | + * | | | mux |----->| ADCA |--- + * voltage2 | AinA1 --|--->| | | | + * | | |_____| |_____ | + * | | _____ ______ + * | | | | | | + * voltage1 | AinB0 --|--->| | | | + * | | | mux |----->| ADCB |--- + * voltage3 | AinB1 --|--->| | | | + * | | |_____| |______| + * | | + * | +---------------------------- + * + * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate + * scan masks. + * When sequencer mode is enabled, chip automatically cycles through + * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all + * channels, at the cost of an extra read, thus dividing the maximum rate by + * two. + */ +enum { + AD7380_SCAN_MASK_CH_0, + AD7380_SCAN_MASK_CH_1, + AD7380_SCAN_MASK_SEQ, +}; + +static const unsigned long ad7380_2x2_channel_scan_masks[] = { + [AD7380_SCAN_MASK_CH_0] = GENMASK(1, 0), + [AD7380_SCAN_MASK_CH_1] = GENMASK(3, 2), + [AD7380_SCAN_MASK_SEQ] = GENMASK(3, 0), + 0 +}; + +static const unsigned long ad7380_2x4_channel_scan_masks[] = { + [AD7380_SCAN_MASK_CH_0] = GENMASK(3, 0), + [AD7380_SCAN_MASK_CH_1] = GENMASK(7, 4), + [AD7380_SCAN_MASK_SEQ] = GENMASK(7, 0), + 0 +}; + static const struct ad7380_timing_specs ad7380_timing = { .t_csh_ns = 10, }; @@ -208,6 +337,7 @@ static const struct ad7380_chip_info ad7380_chip_info = { .name = "ad7380", .channels = ad7380_channels, .num_channels = ARRAY_SIZE(ad7380_channels), + .num_simult_channels = 2, .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, }; @@ -216,6 +346,7 @@ static const struct ad7380_chip_info ad7381_chip_info = { .name = "ad7381", .channels = ad7381_channels, .num_channels = ARRAY_SIZE(ad7381_channels), + .num_simult_channels = 2, .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, }; @@ -224,6 +355,7 @@ static const struct ad7380_chip_info ad7383_chip_info = { .name = "ad7383", .channels = ad7383_channels, .num_channels = ARRAY_SIZE(ad7383_channels), + .num_simult_channels = 2, .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, @@ -234,16 +366,48 @@ static const struct ad7380_chip_info ad7384_chip_info = { .name = "ad7384", .channels = ad7384_channels, .num_channels = ARRAY_SIZE(ad7384_channels), + .num_simult_channels = 2, .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, }; +static const struct ad7380_chip_info ad7386_chip_info = { + .name = "ad7386", + .channels = ad7386_channels, + .num_channels = ARRAY_SIZE(ad7386_channels), + .num_simult_channels = 2, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, +}; + +static const struct ad7380_chip_info ad7387_chip_info = { + .name = "ad7387", + .channels = ad7387_channels, + .num_channels = ARRAY_SIZE(ad7387_channels), + .num_simult_channels = 2, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, +}; + +static const struct ad7380_chip_info ad7388_chip_info = { + .name = "ad7388", + .channels = ad7388_channels, + .num_channels = ARRAY_SIZE(ad7388_channels), + .num_simult_channels = 2, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, +}; + static const struct ad7380_chip_info ad7380_4_chip_info = { .name = "ad7380-4", .channels = ad7380_4_channels, .num_channels = ARRAY_SIZE(ad7380_4_channels), + .num_simult_channels = 4, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, }; @@ -252,6 +416,7 @@ static const struct ad7380_chip_info ad7381_4_chip_info = { .name = "ad7381-4", .channels = ad7381_4_channels, .num_channels = ARRAY_SIZE(ad7381_4_channels), + .num_simult_channels = 4, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, }; @@ -260,6 +425,7 @@ static const struct ad7380_chip_info ad7383_4_chip_info = { .name = "ad7383-4", .channels = ad7383_4_channels, .num_channels = ARRAY_SIZE(ad7383_4_channels), + .num_simult_channels = 4, .vcm_supplies = ad7380_4_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, @@ -270,23 +436,58 @@ static const struct ad7380_chip_info ad7384_4_chip_info = { .name = "ad7384-4", .channels = ad7384_4_channels, .num_channels = ARRAY_SIZE(ad7384_4_channels), + .num_simult_channels = 4, .vcm_supplies = ad7380_4_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, }; +static const struct ad7380_chip_info ad7386_4_chip_info = { + .name = "ad7386-4", + .channels = ad7386_4_channels, + .num_channels = ARRAY_SIZE(ad7386_4_channels), + .num_simult_channels = 4, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + +static const struct ad7380_chip_info ad7387_4_chip_info = { + .name = "ad7387-4", + .channels = ad7387_4_channels, + .num_channels = ARRAY_SIZE(ad7387_4_channels), + .num_simult_channels = 4, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + +static const struct ad7380_chip_info ad7388_4_chip_info = { + .name = "ad7388-4", + .channels = ad7388_4_channels, + .num_channels = ARRAY_SIZE(ad7388_4_channels), + .num_simult_channels = 4, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + struct ad7380_state { const struct ad7380_chip_info *chip_info; struct spi_device *spi; struct regmap *regmap; unsigned int oversampling_ratio; bool resolution_boost_enabled; + unsigned int ch; + bool seq; unsigned int vref_mv; unsigned int vcm_mv[MAX_NUM_CHANNELS]; /* xfers, message an buffer for reading sample data */ - struct spi_transfer xfer[2]; - struct spi_message msg; + struct spi_transfer normal_xfer[2]; + struct spi_message normal_msg; + struct spi_transfer seq_xfer[4]; + struct spi_message seq_msg; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -379,6 +580,43 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg, unreachable(); } +/* + * When switching channel, the ADC require an additional settling time. + * According to the datasheet, data is value on the third CS low. We already + * have an extra toggle before each read (either direct reads or buffered reads) + * to sample correct data, so we just add a single CS toggle at the end of the + * register write. + */ +static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) +{ + struct spi_transfer xfer = { + .delay = { + .value = T_CONVERT_NS, + .unit = SPI_DELAY_UNIT_NSECS, + } + }; + int ret; + + if (st->ch == ch) + return 0; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_CH, + FIELD_PREP(AD7380_CONFIG1_CH, ch)); + + if (ret) + return ret; + + st->ch = ch; + + if (st->oversampling_ratio > 1) + xfer.delay.value = T_CONVERT_0_NS + + T_CONVERT_X_NS * (st->oversampling_ratio - 1); + + return spi_sync_transfer(st->spi, &xfer, 1); +} + /** * ad7380_update_xfers - update the SPI transfers base on the current scan type * @st: device instance specific state @@ -387,33 +625,47 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg, static void ad7380_update_xfers(struct ad7380_state *st, const struct iio_scan_type *scan_type) { - /* - * First xfer only triggers conversion and has to be long enough for - * all conversions to complete, which can be multiple conversion in the - * case of oversampling. Technically T_CONVERT_X_NS is lower for some - * chips, but we use the maximum value for simplicity for now. - */ - if (st->oversampling_ratio > 1) - st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS * - (st->oversampling_ratio - 1); - else - st->xfer[0].delay.value = T_CONVERT_NS; - - st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS; + struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer; + unsigned int t_convert = T_CONVERT_NS; /* - * Second xfer reads all channels. Data size depends on if resolution - * boost is enabled or not. + * In the case of oversampling, conversion time is higher than in normal + * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use + * the maximum value for simplicity for now. */ - st->xfer[1].bits_per_word = scan_type->realbits; - st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) * - (st->chip_info->num_channels - 1); + if (st->oversampling_ratio > 1) + t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * + (st->oversampling_ratio - 1); + + if (st->seq) { + xfer[0].delay.value = xfer[1].delay.value = t_convert; + xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS; + xfer[2].bits_per_word = xfer[3].bits_per_word = + scan_type->realbits; + xfer[2].len = xfer[3].len = + BITS_TO_BYTES(scan_type->storagebits) * + st->chip_info->num_simult_channels; + xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len; + /* Additional delay required here when oversampling is enabled */ + if (st->oversampling_ratio > 1) + xfer[2].delay.value = t_convert; + else + xfer[2].delay.value = 0; + xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS; + } else { + xfer[0].delay.value = t_convert; + xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS; + xfer[1].bits_per_word = scan_type->realbits; + xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) * + st->chip_info->num_simult_channels; + } } static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) { struct ad7380_state *st = iio_priv(indio_dev); const struct iio_scan_type *scan_type; + struct spi_message *msg = &st->normal_msg; /* * Currently, we always read all channels at the same time. The scan_type @@ -423,16 +675,63 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) if (IS_ERR(scan_type)) return PTR_ERR(scan_type); + if (st->chip_info->has_mux) { + unsigned int index; + int ret; + + /* + * Depending on the requested scan_mask and current state, + * we need to either change CH bit, or enable sequencer mode + * to sample correct data. + * Sequencer mode is enabled if active mask corresponds to all + * IIO channels enabled. Otherwise, CH bit is set. + */ + ret = iio_active_scan_mask_index(indio_dev); + if (ret < 0) + return ret; + + index = ret; + if (index == AD7380_SCAN_MASK_SEQ) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 1)); + if (ret) + return ret; + msg = &st->seq_msg; + st->seq = true; + } else { + ret = ad7380_set_ch(st, index); + if (ret) + return ret; + } + + } + ad7380_update_xfers(st, scan_type); - return spi_optimize_message(st->spi, &st->msg); + return spi_optimize_message(st->spi, msg); } static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev) { struct ad7380_state *st = iio_priv(indio_dev); + struct spi_message *msg = &st->normal_msg; + int ret; + + if (st->seq) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 0)); + if (ret) + return ret; + + msg = &st->seq_msg; + st->seq = false; + } - spi_unoptimize_message(&st->msg); + spi_unoptimize_message(msg); return 0; } @@ -447,9 +746,10 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ad7380_state *st = iio_priv(indio_dev); + struct spi_message *msg = st->seq ? &st->seq_msg : &st->normal_msg; int ret; - ret = spi_sync(st->spi, &st->msg); + ret = spi_sync(st->spi, msg); if (ret) goto out; @@ -465,20 +765,43 @@ out: static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index, const struct iio_scan_type *scan_type, int *val) { + unsigned int index = scan_index; int ret; + if (st->chip_info->has_mux) { + unsigned int ch = 0; + + if (index >= st->chip_info->num_simult_channels) { + index -= st->chip_info->num_simult_channels; + ch = 1; + } + + ret = ad7380_set_ch(st, ch); + if (ret) + return ret; + } + ad7380_update_xfers(st, scan_type); - ret = spi_sync(st->spi, &st->msg); + ret = spi_sync(st->spi, &st->normal_msg); if (ret < 0) return ret; - if (scan_type->storagebits > 16) - *val = sign_extend32(*(u32 *)(st->scan_data + 4 * scan_index), - scan_type->realbits - 1); - else - *val = sign_extend32(*(u16 *)(st->scan_data + 2 * scan_index), - scan_type->realbits - 1); + if (scan_type->storagebits > 16) { + if (scan_type->sign == 's') + *val = sign_extend32(*(u32 *)(st->scan_data + 4 * index), + scan_type->realbits - 1); + else + *val = *(u32 *)(st->scan_data + 4 * index) & + GENMASK(scan_type->realbits - 1, 0); + } else { + if (scan_type->sign == 's') + *val = sign_extend32(*(u16 *)(st->scan_data + 2 * index), + scan_type->realbits - 1); + else + *val = *(u16 *)(st->scan_data + 2 * index) & + GENMASK(scan_type->realbits - 1, 0); + } return IIO_VAL_INT; } @@ -655,6 +978,8 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref) /* This is the default value after reset. */ st->oversampling_ratio = 1; + st->ch = 0; + st->seq = false; /* SPI 1-wire mode */ return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, @@ -756,21 +1081,45 @@ static int ad7380_probe(struct spi_device *spi) "failed to allocate register map\n"); /* - * Setting up a low latency read for getting sample data. Used for both - * direct read an triggered buffer. Additional fields will be set up in - * ad7380_update_xfers() based on the current state of the driver at the - * time of the read. + * Setting up xfer structures for both normal and sequence mode. These + * struct are used for both direct read and triggered buffer. Additional + * fields will be set up in ad7380_update_xfers() based on the current + * state of the driver at the time of the read. + */ + + /* + * In normal mode a read is composed of two steps: + * - first, toggle CS (no data xfer) to trigger a conversion + * - then, read data */ + st->normal_xfer[0].cs_change = 1; + st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + st->normal_xfer[1].rx_buf = st->scan_data; - /* toggle CS (no data xfer) to trigger a conversion */ - st->xfer[0].cs_change = 1; - st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; - st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer, + ARRAY_SIZE(st->normal_xfer)); + /* + * In sequencer mode a read is composed of four steps: + * - CS toggle (no data xfer) to get the right point in the sequence + * - CS toggle (no data xfer) to trigger a conversion of AinX0 and + * acquisition of AinX1 + * - 2 data reads, to read AinX0 and AinX1 + */ + st->seq_xfer[0].cs_change = 1; + st->seq_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + st->seq_xfer[1].cs_change = 1; + st->seq_xfer[1].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[1].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; - /* then do a second xfer to read the data */ - st->xfer[1].rx_buf = st->scan_data; + st->seq_xfer[2].rx_buf = st->scan_data; + st->seq_xfer[2].cs_change = 1; + st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; - spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer)); + spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer, + ARRAY_SIZE(st->seq_xfer)); indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; @@ -798,10 +1147,16 @@ static const struct of_device_id ad7380_of_match_table[] = { { .compatible = "adi,ad7381", .data = &ad7381_chip_info }, { .compatible = "adi,ad7383", .data = &ad7383_chip_info }, { .compatible = "adi,ad7384", .data = &ad7384_chip_info }, + { .compatible = "adi,ad7386", .data = &ad7386_chip_info }, + { .compatible = "adi,ad7387", .data = &ad7387_chip_info }, + { .compatible = "adi,ad7388", .data = &ad7388_chip_info }, { .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info }, { .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info }, { .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info }, { .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info }, + { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info }, + { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info }, + { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info }, { } }; @@ -810,10 +1165,16 @@ static const struct spi_device_id ad7380_id_table[] = { { "ad7381", (kernel_ulong_t)&ad7381_chip_info }, { "ad7383", (kernel_ulong_t)&ad7383_chip_info }, { "ad7384", (kernel_ulong_t)&ad7384_chip_info }, + { "ad7386", (kernel_ulong_t)&ad7386_chip_info }, + { "ad7387", (kernel_ulong_t)&ad7387_chip_info }, + { "ad7388", (kernel_ulong_t)&ad7388_chip_info }, { "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info }, { "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info }, { "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info }, { "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info }, + { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info }, + { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info }, + { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7380_id_table); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 80aebed47d1f..aeb8e383fe71 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -409,35 +409,35 @@ static int ad7476_probe(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { - {"ad7091", ID_AD7091}, - {"ad7091r", ID_AD7091R}, - {"ad7273", ID_AD7273}, - {"ad7274", ID_AD7274}, - {"ad7276", ID_AD7276}, - {"ad7277", ID_AD7277}, - {"ad7278", ID_AD7278}, - {"ad7466", ID_AD7466}, - {"ad7467", ID_AD7467}, - {"ad7468", ID_AD7468}, - {"ad7475", ID_AD7475}, - {"ad7476", ID_AD7466}, - {"ad7476a", ID_AD7466}, - {"ad7477", ID_AD7467}, - {"ad7477a", ID_AD7467}, - {"ad7478", ID_AD7468}, - {"ad7478a", ID_AD7468}, - {"ad7495", ID_AD7495}, - {"ad7910", ID_AD7467}, - {"ad7920", ID_AD7466}, - {"ad7940", ID_AD7940}, - {"adc081s", ID_ADC081S}, - {"adc101s", ID_ADC101S}, - {"adc121s", ID_ADC121S}, - {"ads7866", ID_ADS7866}, - {"ads7867", ID_ADS7867}, - {"ads7868", ID_ADS7868}, - {"ltc2314-14", ID_LTC2314_14}, - {} + { "ad7091", ID_AD7091 }, + { "ad7091r", ID_AD7091R }, + { "ad7273", ID_AD7273 }, + { "ad7274", ID_AD7274 }, + { "ad7276", ID_AD7276}, + { "ad7277", ID_AD7277 }, + { "ad7278", ID_AD7278 }, + { "ad7466", ID_AD7466 }, + { "ad7467", ID_AD7467 }, + { "ad7468", ID_AD7468 }, + { "ad7475", ID_AD7475 }, + { "ad7476", ID_AD7466 }, + { "ad7476a", ID_AD7466 }, + { "ad7477", ID_AD7467 }, + { "ad7477a", ID_AD7467 }, + { "ad7478", ID_AD7468 }, + { "ad7478a", ID_AD7468 }, + { "ad7495", ID_AD7495 }, + { "ad7910", ID_AD7467 }, + { "ad7920", ID_AD7466 }, + { "ad7940", ID_AD7940 }, + { "adc081s", ID_ADC081S }, + { "adc101s", ID_ADC101S }, + { "adc121s", ID_ADC121S }, + { "ads7866", ID_ADS7866 }, + { "ads7867", ID_ADS7867 }, + { "ads7868", ID_ADS7868 }, + { "ltc2314-14", ID_LTC2314_14 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7476_id); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index c321c6ef48df..9b457472d49c 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -70,19 +70,17 @@ static int ad7606_reg_access(struct iio_dev *indio_dev, struct ad7606_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + if (readval) { ret = st->bops->reg_read(st, reg); if (ret < 0) - goto err_unlock; + return ret; *readval = ret; - ret = 0; + return 0; } else { - ret = st->bops->reg_write(st, reg, writeval); + return st->bops->reg_write(st, reg, writeval); } -err_unlock: - mutex_unlock(&st->lock); - return ret; } static int ad7606_read_samples(struct ad7606_state *st) @@ -100,19 +98,19 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p) struct ad7606_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = ad7606_read_samples(st); - if (ret == 0) - iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns(indio_dev)); + if (ret) + goto error_ret; + iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_get_time_ns(indio_dev)); +error_ret: iio_trigger_notify_done(indio_dev->trig); /* The rising edge of the CONVST signal starts a new conversion. */ gpiod_set_value(st->gpio_convst, 1); - mutex_unlock(&st->lock); - return IRQ_HANDLED; } @@ -212,9 +210,9 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) struct ad7606_state *st = iio_priv(indio_dev); DECLARE_BITMAP(values, 3); - values[0] = val; + values[0] = val & GENMASK(2, 0); - gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, + gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc, st->gpio_os->info, values); /* AD7616 requires a reset to update value */ @@ -233,19 +231,17 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, struct ad7606_state *st = iio_priv(indio_dev); int i, ret, ch = 0; + guard(mutex)(&st->lock); + switch (mask) { case IIO_CHAN_INFO_SCALE: - mutex_lock(&st->lock); i = find_closest(val2, st->scale_avail, st->num_scales); if (st->sw_mode_en) ch = chan->address; ret = st->write_scale(indio_dev, ch, i); - if (ret < 0) { - mutex_unlock(&st->lock); + if (ret < 0) return ret; - } st->range[ch] = i; - mutex_unlock(&st->lock); return 0; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -253,14 +249,9 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, return -EINVAL; i = find_closest(val, st->oversampling_avail, st->num_os_ratios); - mutex_lock(&st->lock); ret = st->write_os(indio_dev, i); - if (ret < 0) { - mutex_unlock(&st->lock); + if (ret < 0) return ret; - } - st->oversampling = st->oversampling_avail[i]; - mutex_unlock(&st->lock); return 0; default: @@ -419,7 +410,7 @@ static int ad7606_request_gpios(struct ad7606_state *st) return PTR_ERR(st->gpio_range); st->gpio_standby = devm_gpiod_get_optional(dev, "standby", - GPIOD_OUT_HIGH); + GPIOD_OUT_LOW); if (IS_ERR(st->gpio_standby)) return PTR_ERR(st->gpio_standby); @@ -662,7 +653,7 @@ static int ad7606_suspend(struct device *dev) if (st->gpio_standby) { gpiod_set_value(st->gpio_range, 1); - gpiod_set_value(st->gpio_standby, 0); + gpiod_set_value(st->gpio_standby, 1); } return 0; diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c index 6bc587b20f05..02d8c309304e 100644 --- a/drivers/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -125,7 +125,7 @@ static const struct of_device_id ad7606_of_match[] = { { .compatible = "adi,ad7606-4" }, { .compatible = "adi,ad7606-6" }, { .compatible = "adi,ad7606-8" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7606_of_match); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 263a778bcf25..62ec12195307 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -249,8 +249,9 @@ static int ad7616_sw_mode_config(struct iio_dev *indio_dev) static int ad7606B_sw_mode_config(struct iio_dev *indio_dev) { struct ad7606_state *st = iio_priv(indio_dev); - unsigned long os[3] = {1}; + DECLARE_BITMAP(os, 3); + bitmap_fill(os, 3); /* * Software mode is enabled when all three oversampling * pins are set to high. If oversampling gpios are defined @@ -258,7 +259,7 @@ static int ad7606B_sw_mode_config(struct iio_dev *indio_dev) * otherwise, they must be hardwired to VDD */ if (st->gpio_os) { - gpiod_set_array_value(ARRAY_SIZE(os), + gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc, st->gpio_os->info, os); } /* OS of 128 and 256 are available only in software mode */ @@ -333,7 +334,7 @@ static const struct spi_device_id ad7606_id_table[] = { { "ad7606-8", ID_AD7606_8 }, { "ad7606b", ID_AD7606B }, { "ad7616", ID_AD7616 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7606_id_table); @@ -344,7 +345,7 @@ static const struct of_device_id ad7606_of_match[] = { { .compatible = "adi,ad7606-8" }, { .compatible = "adi,ad7606b" }, { .compatible = "adi,ad7616" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7606_of_match); diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 3079a0872947..4d570383ef02 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -291,13 +291,13 @@ static int ad7766_probe(struct spi_device *spi) } static const struct spi_device_id ad7766_id[] = { - {"ad7766", ID_AD7766}, - {"ad7766-1", ID_AD7766_1}, - {"ad7766-2", ID_AD7766_2}, - {"ad7767", ID_AD7766}, - {"ad7767-1", ID_AD7766_1}, - {"ad7767-2", ID_AD7766_2}, - {} + { "ad7766", ID_AD7766 }, + { "ad7766-1", ID_AD7766_1 }, + { "ad7766-2", ID_AD7766_2 }, + { "ad7767", ID_AD7766 }, + { "ad7767-1", ID_AD7766_1 }, + { "ad7767-2", ID_AD7766_2 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7766_id); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 70a25949142c..113703fb7245 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -544,13 +544,10 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev, { struct ad7768_state *st = iio_priv(indio_dev); struct device *device = indio_dev->dev.parent; - struct fwnode_handle *fwnode; - struct fwnode_handle *child; const char *label; int crt_ch = 0; - fwnode = dev_fwnode(device); - fwnode_for_each_child_node(fwnode, child) { + device_for_each_child_node_scoped(device, child) { if (fwnode_property_read_u32(child, "reg", &crt_ch)) continue; @@ -658,7 +655,7 @@ MODULE_DEVICE_TABLE(spi, ad7768_id_table); static const struct of_device_id ad7768_of_match[] = { { .compatible = "adi,ad7768-1" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7768_of_match); diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index a813fe04787c..e9b0c577c9cc 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -355,11 +355,11 @@ static int ad7780_probe(struct spi_device *spi) } static const struct spi_device_id ad7780_id[] = { - {"ad7170", ID_AD7170}, - {"ad7171", ID_AD7171}, - {"ad7780", ID_AD7780}, - {"ad7781", ID_AD7781}, - {} + { "ad7170", ID_AD7170 }, + { "ad7171", ID_AD7171 }, + { "ad7780", ID_AD7780 }, + { "ad7781", ID_AD7781 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7780_id); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index d4ad7e0b515a..abebd519cafa 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -824,16 +824,16 @@ static int ad7793_probe(struct spi_device *spi) } static const struct spi_device_id ad7793_id[] = { - {"ad7785", ID_AD7785}, - {"ad7792", ID_AD7792}, - {"ad7793", ID_AD7793}, - {"ad7794", ID_AD7794}, - {"ad7795", ID_AD7795}, - {"ad7796", ID_AD7796}, - {"ad7797", ID_AD7797}, - {"ad7798", ID_AD7798}, - {"ad7799", ID_AD7799}, - {} + { "ad7785", ID_AD7785 }, + { "ad7792", ID_AD7792 }, + { "ad7793", ID_AD7793 }, + { "ad7794", ID_AD7794 }, + { "ad7795", ID_AD7795 }, + { "ad7796", ID_AD7796 }, + { "ad7797", ID_AD7797 }, + { "ad7798", ID_AD7798 }, + { "ad7799", ID_AD7799 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7793_id); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 965bdc8aa696..6265ce7df703 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -329,8 +329,8 @@ static int ad7887_probe(struct spi_device *spi) } static const struct spi_device_id ad7887_id[] = { - {"ad7887", ID_AD7887}, - {} + { "ad7887", ID_AD7887 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7887_id); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 9d6bf6d0927a..09680015a7ab 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -361,14 +361,14 @@ static int ad7923_probe(struct spi_device *spi) } static const struct spi_device_id ad7923_id[] = { - {"ad7904", AD7904}, - {"ad7914", AD7914}, - {"ad7923", AD7924}, - {"ad7924", AD7924}, - {"ad7908", AD7908}, - {"ad7918", AD7918}, - {"ad7928", AD7928}, - {} + { "ad7904", AD7904 }, + { "ad7914", AD7914 }, + { "ad7923", AD7924 }, + { "ad7924", AD7924 }, + { "ad7908", AD7908 }, + { "ad7918", AD7918 }, + { "ad7928", AD7928 }, + { } }; MODULE_DEVICE_TABLE(spi, ad7923_id); @@ -380,7 +380,7 @@ static const struct of_device_id ad7923_of_match[] = { { .compatible = "adi,ad7908", }, { .compatible = "adi,ad7918", }, { .compatible = "adi,ad7928", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7923_of_match); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 0f0dcd9ca6b6..0f107e3fc2c8 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -237,7 +237,8 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev, if (!st->rx_buf) return -ENOMEM; - st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2; + st->transfer_size = bitmap_weight(scan_mask, + iio_get_masklength(indio_dev)) * 2; switch (st->id) { case ad7992: diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 41c1b519c573..05fb7a75531f 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/spi/spi.h> +#include <linux/seq_file.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> @@ -104,7 +105,30 @@ #define AD9467_DEF_OUTPUT_MODE 0x08 #define AD9467_REG_VREF_MASK 0x0F +/* + * Analog Devices AD9643 14-Bit, 170/210/250 MSPS ADC + */ + +#define CHIPID_AD9643 0x82 +#define AD9643_REG_VREF_MASK 0x1F + +/* + * Analog Devices AD9652 16-bit 310 MSPS ADC + */ + +#define CHIPID_AD9652 0xC1 +#define AD9652_REG_VREF_MASK 0xC0 + +/* + * Analog Devices AD9649 14-bit 20/40/65/80 MSPS ADC + */ + +#define CHIPID_AD9649 0x6F +#define AD9649_TEST_POINTS 8 + #define AD9647_MAX_TEST_POINTS 32 +#define AD9467_CAN_INVERT(st) \ + (!(st)->info->has_dco || (st)->info->has_dco_invert) struct ad9467_chip_info { const char *name; @@ -113,12 +137,23 @@ struct ad9467_chip_info { unsigned int num_channels; const unsigned int (*scale_table)[2]; int num_scales; + unsigned long test_mask; + unsigned int test_mask_len; unsigned long max_rate; unsigned int default_output_mode; unsigned int vref_mask; unsigned int num_lanes; + unsigned int dco_en; + unsigned int test_points; /* data clock output */ bool has_dco; + bool has_dco_invert; +}; + +struct ad9467_chan_test_mode { + struct ad9467_state *st; + unsigned int idx; + u8 mode; }; struct ad9467_state { @@ -126,6 +161,8 @@ struct ad9467_state { struct iio_backend *back; struct spi_device *spi; struct clk *clk; + /* used for debugfs */ + struct ad9467_chan_test_mode *chan_test; unsigned int output_mode; unsigned int (*scales)[2]; /* @@ -138,6 +175,8 @@ struct ad9467_state { * at the io delay control section. */ DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2); + /* number of bits of the map */ + unsigned int calib_map_size; struct gpio_desc *pwrdown_gpio; /* ensure consistent state obtained on multiple related accesses */ struct mutex lock; @@ -211,6 +250,24 @@ static const unsigned int ad9467_scale_table[][2] = { {2300, 8}, {2400, 9}, {2500, 10}, }; +static const unsigned int ad9643_scale_table[][2] = { + {2087, 0x0F}, {2065, 0x0E}, {2042, 0x0D}, {2020, 0x0C}, {1997, 0x0B}, + {1975, 0x0A}, {1952, 0x09}, {1930, 0x08}, {1907, 0x07}, {1885, 0x06}, + {1862, 0x05}, {1840, 0x04}, {1817, 0x03}, {1795, 0x02}, {1772, 0x01}, + {1750, 0x00}, {1727, 0x1F}, {1704, 0x1E}, {1681, 0x1D}, {1658, 0x1C}, + {1635, 0x1B}, {1612, 0x1A}, {1589, 0x19}, {1567, 0x18}, {1544, 0x17}, + {1521, 0x16}, {1498, 0x15}, {1475, 0x14}, {1452, 0x13}, {1429, 0x12}, + {1406, 0x11}, {1383, 0x10}, +}; + +static const unsigned int ad9649_scale_table[][2] = { + {2000, 0}, +}; + +static const unsigned int ad9652_scale_table[][2] = { + {1250, 0}, {1125, 1}, {1200, 2}, {1250, 3}, {1000, 5}, +}; + static void __ad9467_get_scale(struct ad9467_state *st, int index, unsigned int *val, unsigned int *val2) { @@ -224,14 +281,14 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index, *val2 = tmp % 1000000; } -#define AD9467_CHAN(_chan, _si, _bits, _sign) \ +#define AD9467_CHAN(_chan, avai_mask, _si, _bits, _sign) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = _chan, \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = avai_mask, \ .scan_index = _si, \ .scan_type = { \ .sign = _sign, \ @@ -241,11 +298,42 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index, } static const struct iio_chan_spec ad9434_channels[] = { - AD9467_CHAN(0, 0, 12, 's'), + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'), }; static const struct iio_chan_spec ad9467_channels[] = { - AD9467_CHAN(0, 0, 16, 's'), + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'), +}; + +static const struct iio_chan_spec ad9643_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 14, 's'), + AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 14, 's'), +}; + +static const struct iio_chan_spec ad9649_channels[] = { + AD9467_CHAN(0, 0, 0, 14, 's'), +}; + +static const struct iio_chan_spec ad9652_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'), + AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 16, 's'), +}; + +static const char * const ad9467_test_modes[] = { + [AN877_ADC_TESTMODE_OFF] = "off", + [AN877_ADC_TESTMODE_MIDSCALE_SHORT] = "midscale_short", + [AN877_ADC_TESTMODE_POS_FULLSCALE] = "pos_fullscale", + [AN877_ADC_TESTMODE_NEG_FULLSCALE] = "neg_fullscale", + [AN877_ADC_TESTMODE_ALT_CHECKERBOARD] = "checkerboard", + [AN877_ADC_TESTMODE_PN23_SEQ] = "prbs23", + [AN877_ADC_TESTMODE_PN9_SEQ] = "prbs9", + [AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle", + [AN877_ADC_TESTMODE_USER] = "user", + [AN877_ADC_TESTMODE_BIT_TOGGLE] = "bit_toggle", + [AN877_ADC_TESTMODE_SYNC] = "sync", + [AN877_ADC_TESTMODE_ONE_BIT_HIGH] = "one_bit_high", + [AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY] = "mixed_bit_frequency", + [AN877_ADC_TESTMODE_RAMP] = "ramp", }; static const struct ad9467_chip_info ad9467_chip_tbl = { @@ -256,6 +344,10 @@ static const struct ad9467_chip_info ad9467_chip_tbl = { .num_scales = ARRAY_SIZE(ad9467_scale_table), .channels = ad9467_channels, .num_channels = ARRAY_SIZE(ad9467_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, .default_output_mode = AD9467_DEF_OUTPUT_MODE, .vref_mask = AD9467_REG_VREF_MASK, .num_lanes = 8, @@ -269,6 +361,9 @@ static const struct ad9467_chip_info ad9434_chip_tbl = { .num_scales = ARRAY_SIZE(ad9434_scale_table), .channels = ad9434_channels, .num_channels = ARRAY_SIZE(ad9434_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_USER, AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_USER + 1, .default_output_mode = AD9434_DEF_OUTPUT_MODE, .vref_mask = AD9434_REG_VREF_MASK, .num_lanes = 6, @@ -282,17 +377,78 @@ static const struct ad9467_chip_info ad9265_chip_tbl = { .num_scales = ARRAY_SIZE(ad9265_scale_table), .channels = ad9467_channels, .num_channels = ARRAY_SIZE(ad9467_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, .default_output_mode = AD9265_DEF_OUTPUT_MODE, .vref_mask = AD9265_REG_VREF_MASK, .has_dco = true, + .has_dco_invert = true, +}; + +static const struct ad9467_chip_info ad9643_chip_tbl = { + .name = "ad9643", + .id = CHIPID_AD9643, + .max_rate = 250000000UL, + .scale_table = ad9643_scale_table, + .num_scales = ARRAY_SIZE(ad9643_scale_table), + .channels = ad9643_channels, + .num_channels = ARRAY_SIZE(ad9643_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = BIT(AN877_ADC_TESTMODE_RAMP) | + GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_RAMP + 1, + .vref_mask = AD9643_REG_VREF_MASK, + .has_dco = true, + .has_dco_invert = true, + .dco_en = AN877_ADC_DCO_DELAY_ENABLE, +}; + +static const struct ad9467_chip_info ad9649_chip_tbl = { + .name = "ad9649", + .id = CHIPID_AD9649, + .max_rate = 80000000UL, + .scale_table = ad9649_scale_table, + .num_scales = ARRAY_SIZE(ad9649_scale_table), + .channels = ad9649_channels, + .num_channels = ARRAY_SIZE(ad9649_channels), + .test_points = AD9649_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1, + .has_dco = true, + .has_dco_invert = true, + .dco_en = AN877_ADC_DCO_DELAY_ENABLE, +}; + +static const struct ad9467_chip_info ad9652_chip_tbl = { + .name = "ad9652", + .id = CHIPID_AD9652, + .max_rate = 310000000UL, + .scale_table = ad9652_scale_table, + .num_scales = ARRAY_SIZE(ad9652_scale_table), + .channels = ad9652_channels, + .num_channels = ARRAY_SIZE(ad9652_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, + .vref_mask = AD9652_REG_VREF_MASK, + .has_dco = true, }; static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) { const struct ad9467_chip_info *info = st->info; - unsigned int i, vref_val; + unsigned int vref_val; + unsigned int i = 0; int ret; + /* nothing to read if we only have one possible scale */ + if (info->num_scales == 1) + goto out_get_scale; + ret = ad9467_spi_read(st, AN877_ADC_REG_VREF); if (ret < 0) return ret; @@ -307,6 +463,7 @@ static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) if (i == info->num_scales) return -ERANGE; +out_get_scale: __ad9467_get_scale(st, i, val, val2); return IIO_VAL_INT_PLUS_MICRO; @@ -321,6 +478,8 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2) if (val != 0) return -EINVAL; + if (info->num_scales == 1) + return -EOPNOTSUPP; for (i = 0; i < info->num_scales; i++) { __ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]); @@ -352,40 +511,96 @@ static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode) AN877_ADC_TRANSFER_SYNC); } -static int ad9647_calibrate_prepare(struct ad9467_state *st) +static int ad9467_testmode_set(struct ad9467_state *st, unsigned int chan, + unsigned int test_mode) +{ + int ret; + + if (st->info->num_channels > 1) { + /* so that the test mode is only applied to one channel */ + ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, BIT(chan)); + if (ret) + return ret; + } + + ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, test_mode); + if (ret) + return ret; + + if (st->info->num_channels > 1) { + /* go to default state where all channels get write commands */ + ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, + GENMASK(st->info->num_channels - 1, 0)); + if (ret) + return ret; + } + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_backend_testmode_on(struct ad9467_state *st, + unsigned int chan, + enum iio_backend_test_pattern pattern) { struct iio_backend_data_fmt data = { .enable = false, }; - unsigned int c; int ret; - ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, - AN877_ADC_TESTMODE_PN9_SEQ); + ret = iio_backend_data_format_set(st->back, chan, &data); + if (ret) + return ret; + + ret = iio_backend_test_pattern_set(st->back, chan, pattern); + if (ret) + return ret; + + return iio_backend_chan_enable(st->back, chan); +} + +static int ad9467_backend_testmode_off(struct ad9467_state *st, + unsigned int chan) +{ + struct iio_backend_data_fmt data = { + .enable = true, + .sign_extend = true, + }; + int ret; + + ret = iio_backend_chan_disable(st->back, chan); if (ret) return ret; - ret = ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, - AN877_ADC_TRANSFER_SYNC); + ret = iio_backend_test_pattern_set(st->back, chan, + IIO_BACKEND_NO_TEST_PATTERN); if (ret) return ret; + return iio_backend_data_format_set(st->back, chan, &data); +} + +static int ad9647_calibrate_prepare(struct ad9467_state *st) +{ + unsigned int c; + int ret; + ret = ad9467_outputmode_set(st, st->info->default_output_mode); if (ret) return ret; for (c = 0; c < st->info->num_channels; c++) { - ret = iio_backend_data_format_set(st->back, c, &data); + ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_PN9_SEQ); if (ret) return ret; - } - ret = iio_backend_test_pattern_set(st->back, 0, - IIO_BACKEND_ADI_PRBS_9A); - if (ret) - return ret; + ret = ad9467_backend_testmode_on(st, c, + IIO_BACKEND_ADI_PRBS_9A); + if (ret) + return ret; + } - return iio_backend_chan_enable(st->back, 0); + return 0; } static int ad9647_calibrate_polarity_set(struct ad9467_state *st, @@ -442,7 +657,7 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val) if (st->info->has_dco) { ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY, - val); + val | st->info->dco_en); if (ret) return ret; @@ -461,57 +676,38 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val) static int ad9647_calibrate_stop(struct ad9467_state *st) { - struct iio_backend_data_fmt data = { - .sign_extend = true, - .enable = true, - }; unsigned int c, mode; int ret; - ret = iio_backend_chan_disable(st->back, 0); - if (ret) - return ret; - - ret = iio_backend_test_pattern_set(st->back, 0, - IIO_BACKEND_NO_TEST_PATTERN); - if (ret) - return ret; - for (c = 0; c < st->info->num_channels; c++) { - ret = iio_backend_data_format_set(st->back, c, &data); + ret = ad9467_backend_testmode_off(st, c); + if (ret) + return ret; + + ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_OFF); if (ret) return ret; } mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; - ret = ad9467_outputmode_set(st, mode); - if (ret) - return ret; - - ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, - AN877_ADC_TESTMODE_OFF); - if (ret) - return ret; - - return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, - AN877_ADC_TRANSFER_SYNC); + return ad9467_outputmode_set(st, mode); } static int ad9467_calibrate(struct ad9467_state *st) { - unsigned int point, val, inv_val, cnt, inv_cnt = 0; + unsigned int point, val, inv_val, cnt, inv_cnt = 0, c; /* * Half of the bitmap is for the inverted signal. The number of test * points is the same though... */ - unsigned int test_points = AD9647_MAX_TEST_POINTS; + unsigned int test_points = st->info->test_points; unsigned long sample_rate = clk_get_rate(st->clk); struct device *dev = &st->spi->dev; bool invert = false, stat; int ret; /* all points invalid */ - bitmap_fill(st->calib_map, BITS_PER_TYPE(st->calib_map)); + bitmap_fill(st->calib_map, st->calib_map_size); ret = ad9647_calibrate_prepare(st); if (ret) @@ -521,16 +717,31 @@ retune: if (ret) return ret; - for (point = 0; point < test_points; point++) { + for (point = 0; point < st->info->test_points; point++) { ret = ad9467_calibrate_apply(st, point); if (ret) return ret; - ret = iio_backend_chan_status(st->back, 0, &stat); - if (ret) - return ret; + for (c = 0; c < st->info->num_channels; c++) { + ret = iio_backend_chan_status(st->back, c, &stat); + if (ret) + return ret; - __assign_bit(point + invert * test_points, st->calib_map, stat); + /* + * A point is considered valid if all channels report no + * error. If one reports an error, then we consider the + * point as invalid and we can break the loop right away. + */ + if (stat) { + dev_dbg(dev, "Invalid point(%u, inv:%u) for CH:%u\n", + point, invert, c); + break; + } + + if (c == st->info->num_channels - 1) + __clear_bit(point + invert * test_points, + st->calib_map); + } } if (!invert) { @@ -541,8 +752,13 @@ retune: * a row. */ if (cnt < 3) { - invert = true; - goto retune; + if (AD9467_CAN_INVERT(st)) { + invert = true; + goto retune; + } + + if (!cnt) + return -EIO; } } else { inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points, @@ -679,7 +895,7 @@ static int ad9467_update_scan_mode(struct iio_dev *indio_dev, return 0; } -static const struct iio_info ad9467_info = { +static struct iio_info ad9467_info = { .read_raw = ad9467_read_raw, .write_raw = ad9467_write_raw, .update_scan_mode = ad9467_update_scan_mode, @@ -762,12 +978,134 @@ static int ad9467_iio_backend_get(struct ad9467_state *st) return -ENODEV; } +static int ad9467_test_mode_available_show(struct seq_file *s, void *ignored) +{ + struct ad9467_state *st = s->private; + unsigned int bit; + + for_each_set_bit(bit, &st->info->test_mask, st->info->test_mask_len) + seq_printf(s, "%s\n", ad9467_test_modes[bit]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9467_test_mode_available); + +static ssize_t ad9467_chan_test_mode_read(struct file *file, + char __user *userbuf, size_t count, + loff_t *ppos) +{ + struct ad9467_chan_test_mode *chan = file->private_data; + struct ad9467_state *st = chan->st; + char buf[128] = {0}; + size_t len; + int ret; + + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t", + ad9467_test_modes[chan->mode]); + + ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx, + buf + len, + sizeof(buf) - len); + if (ret < 0) + return ret; + len += ret; + } else if (chan->mode == AN877_ADC_TESTMODE_OFF) { + len = scnprintf(buf, sizeof(buf), "No test Running...\n"); + } else { + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test on CH:%u\n", + ad9467_test_modes[chan->mode], chan->idx); + } + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static ssize_t ad9467_chan_test_mode_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad9467_chan_test_mode *chan = file->private_data; + struct ad9467_state *st = chan->st; + char test_mode[32] = {0}; + unsigned int mode; + int ret; + + ret = simple_write_to_buffer(test_mode, sizeof(test_mode) - 1, ppos, + userbuf, count); + if (ret < 0) + return ret; + + for_each_set_bit(mode, &st->info->test_mask, st->info->test_mask_len) { + if (sysfs_streq(test_mode, ad9467_test_modes[mode])) + break; + } + + if (mode == st->info->test_mask_len) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (mode == AN877_ADC_TESTMODE_OFF) { + unsigned int out_mode; + + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_off(st, chan->idx); + if (ret) + return ret; + } + + ret = ad9467_testmode_set(st, chan->idx, mode); + if (ret) + return ret; + + out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; + ret = ad9467_outputmode_set(st, out_mode); + if (ret) + return ret; + } else { + ret = ad9467_outputmode_set(st, st->info->default_output_mode); + if (ret) + return ret; + + ret = ad9467_testmode_set(st, chan->idx, mode); + if (ret) + return ret; + + /* some patterns have a backend matching monitoring block */ + if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, + IIO_BACKEND_ADI_PRBS_9A); + if (ret) + return ret; + } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, + IIO_BACKEND_ADI_PRBS_23A); + if (ret) + return ret; + } + } + + chan->mode = mode; + + return count; +} + +static const struct file_operations ad9467_chan_test_mode_fops = { + .open = simple_open, + .read = ad9467_chan_test_mode_read, + .write = ad9467_chan_test_mode_write, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + static ssize_t ad9467_dump_calib_table(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ad9467_state *st = file->private_data; - unsigned int bit, size = BITS_PER_TYPE(st->calib_map); + unsigned int bit; /* +2 for the newline and +1 for the string termination */ unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3]; ssize_t len = 0; @@ -776,8 +1114,8 @@ static ssize_t ad9467_dump_calib_table(struct file *file, if (*ppos) goto out_read; - for (bit = 0; bit < size; bit++) { - if (bit == size / 2) + for (bit = 0; bit < st->calib_map_size; bit++) { + if (AD9467_CAN_INVERT(st) && bit == st->calib_map_size / 2) len += scnprintf(map + len, sizeof(map) - len, "\n"); len += scnprintf(map + len, sizeof(map) - len, "%c", @@ -800,12 +1138,33 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev) { struct dentry *d = iio_get_debugfs_dentry(indio_dev); struct ad9467_state *st = iio_priv(indio_dev); + char attr_name[32]; + unsigned int chan; if (!IS_ENABLED(CONFIG_DEBUG_FS)) return; + st->chan_test = devm_kcalloc(&st->spi->dev, st->info->num_channels, + sizeof(*st->chan_test), GFP_KERNEL); + if (!st->chan_test) + return; + debugfs_create_file("calibration_table_dump", 0400, d, st, &ad9467_calib_table_fops); + + for (chan = 0; chan < st->info->num_channels; chan++) { + snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode", + chan); + st->chan_test[chan].idx = chan; + st->chan_test[chan].st = st; + debugfs_create_file(attr_name, 0600, d, &st->chan_test[chan], + &ad9467_chan_test_mode_fops); + } + + debugfs_create_file("in_voltage_test_mode_available", 0400, d, st, + &ad9467_test_mode_available_fops); + + iio_backend_debugfs_add(st->back, indio_dev); } static int ad9467_probe(struct spi_device *spi) @@ -826,6 +1185,10 @@ static int ad9467_probe(struct spi_device *spi) if (!st->info) return -ENODEV; + st->calib_map_size = st->info->test_points; + if (AD9467_CAN_INVERT(st)) + st->calib_map_size *= 2; + st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); if (IS_ERR(st->clk)) return PTR_ERR(st->clk); @@ -850,6 +1213,8 @@ static int ad9467_probe(struct spi_device *spi) return -ENODEV; } + if (st->info->num_scales > 1) + ad9467_info.read_avail = ad9467_read_avail; indio_dev->name = st->info->name; indio_dev->channels = st->info->channels; indio_dev->num_channels = st->info->num_channels; @@ -884,7 +1249,10 @@ static const struct of_device_id ad9467_of_match[] = { { .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, }, { .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, }, { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, }, - {} + { .compatible = "adi,ad9643", .data = &ad9643_chip_tbl, }, + { .compatible = "adi,ad9649", .data = &ad9649_chip_tbl, }, + { .compatible = "adi,ad9652", .data = &ad9652_chip_tbl, }, + { } }; MODULE_DEVICE_TABLE(of, ad9467_of_match); @@ -892,7 +1260,10 @@ static const struct spi_device_id ad9467_ids[] = { { "ad9265", (kernel_ulong_t)&ad9265_chip_tbl }, { "ad9434", (kernel_ulong_t)&ad9434_chip_tbl }, { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl }, - {} + { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl }, + { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, }, + { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, }, + { } }; MODULE_DEVICE_TABLE(spi, ad9467_ids); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index dcd557e93586..e2bed2d648f2 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -351,7 +351,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (sigma_delta->num_slots == 1) { channel = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ad_sigma_delta_set_channel(sigma_delta, indio_dev->channels[channel].address); if (ret) @@ -364,7 +364,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) * implementation is mandatory. */ slot = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { sigma_delta->slots[slot] = indio_dev->channels[i].address; slot++; } @@ -526,7 +526,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots; + return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots; } static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 21ce7564e83d..5c8c87eb36d1 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -61,6 +61,10 @@ #define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40) #define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1) +/* out of sync */ +#define ADI_AXI_ADC_CHAN_STAT_PN_OOS BIT(1) +/* spurious out of sync */ +#define ADI_AXI_ADC_CHAN_STAT_PN_ERR BIT(2) #define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40) #define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16) @@ -199,17 +203,19 @@ static int axi_adc_test_pattern_set(struct iio_backend *back, return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan), ADI_AXI_ADC_CHAN_PN_SEL_MASK, FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0)); + case IIO_BACKEND_ADI_PRBS_23A: + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan), + ADI_AXI_ADC_CHAN_PN_SEL_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 1)); default: return -EINVAL; } } -static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, - bool *error) +static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan, + unsigned int *status) { - struct adi_axi_adc_state *st = iio_backend_get_priv(back); int ret; - u32 val; guard(mutex)(&st->lock); /* reset test bits by setting them */ @@ -221,7 +227,18 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, /* let's give enough time to validate or erroring the incoming pattern */ fsleep(1000); - ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), &val); + return regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), + status); +} + +static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, + bool *error) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = axi_adc_read_chan_status(st, chan, &val); if (ret) return ret; @@ -233,6 +250,30 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, return 0; } +static int axi_adc_debugfs_print_chan_status(struct iio_backend *back, + unsigned int chan, char *buf, + size_t len) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = axi_adc_read_chan_status(st, chan, &val); + if (ret) + return ret; + + /* + * PN_ERR is cleared in case out of sync is set. Hence, no point in + * checking both bits. + */ + if (val & ADI_AXI_ADC_CHAN_STAT_PN_OOS) + return scnprintf(buf, len, "CH%u: Out of Sync.\n", chan); + if (val & ADI_AXI_ADC_CHAN_STAT_PN_ERR) + return scnprintf(buf, len, "CH%u: Spurious Out of Sync.\n", chan); + + return scnprintf(buf, len, "CH%u: OK.\n", chan); +} + static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); @@ -267,13 +308,24 @@ static void axi_adc_free_buffer(struct iio_backend *back, iio_dmaengine_buffer_free(buffer); } +static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + static const struct regmap_config axi_adc_regmap_config = { .val_bits = 32, .reg_bits = 32, .reg_stride = 4, }; -static const struct iio_backend_ops adi_axi_adc_generic = { +static const struct iio_backend_ops adi_axi_adc_ops = { .enable = axi_adc_enable, .disable = axi_adc_disable, .data_format_set = axi_adc_data_format_set, @@ -285,6 +337,13 @@ static const struct iio_backend_ops adi_axi_adc_generic = { .iodelay_set = axi_adc_iodelays_set, .test_pattern_set = axi_adc_test_pattern_set, .chan_status = axi_adc_chan_status, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info adi_axi_adc_generic = { + .name = "axi-adc", + .ops = &adi_axi_adc_ops, }; static int adi_axi_adc_probe(struct platform_device *pdev) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 090416c0d622..1d5fd5f534b8 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -555,8 +555,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; - if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", - NULL)) { + if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) { if (data->model_data->bat_sense_sup) { data->battery_sensing = 1; if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & @@ -695,7 +694,7 @@ static const struct of_device_id aspeed_adc_matches[] = { { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, - {}, + { } }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index eb501e3c86a5..9c39acff17e6 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -7,6 +7,7 @@ #include <linux/bitmap.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> @@ -268,9 +269,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) struct iio_chan_spec const *chan; int i, j = 0; - for (i = 0; i < idev->masklength; i++) { - if (!test_bit(i, idev->active_scan_mask)) - continue; + iio_for_each_active_channel(idev, i) { chan = idev->channels + i; st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel)); j++; @@ -543,22 +542,18 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev, int i; for (i = 0; i < st->caps->trigger_number; i++) { - char *name = kasprintf(GFP_KERNEL, - "%s-dev%d-%s", - idev->name, - iio_device_id(idev), - triggers[i].name); + char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s", + idev->name, + iio_device_id(idev), + triggers[i].name); if (!name) return -ENOMEM; if (strcmp(trigger_name, name) == 0) { - kfree(name); if (triggers[i].value == 0) return -EINVAL; return triggers[i].value; } - - kfree(name); } return -EINVAL; @@ -1340,7 +1335,7 @@ static const struct of_device_id at91_adc_dt_ids[] = { { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps }, { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps }, { .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps }, - {}, + { } }; MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index b487e577befb..d43c8d124a0c 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -5,6 +5,7 @@ * Quentin Schulz <quentin.schulz@free-electrons.com> */ +#include <asm/unaligned.h> #include <linux/bitfield.h> #include <linux/completion.h> #include <linux/interrupt.h> @@ -30,6 +31,8 @@ #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) +#define AXP717_ADC_EN1_MASK GENMASK(7, 0) + #define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0) #define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1) #define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2) @@ -43,6 +46,13 @@ #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) +#define AXP717_ADC_DATA_TS 0x00 +#define AXP717_ADC_DATA_TEMP 0x01 +#define AXP717_ADC_DATA_VMID 0x02 +#define AXP717_ADC_DATA_BKUP_BATT 0x03 + +#define AXP717_ADC_DATA_MASK GENMASK(13, 0) + #define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) #define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) @@ -125,6 +135,20 @@ enum axp22x_adc_channel_i { AXP22X_BATT_DISCHRG_I, }; +enum axp717_adc_channel_v { + AXP717_BATT_V = 0, + AXP717_TS_IN, + AXP717_VBUS_V, + AXP717_VSYS_V, + AXP717_DIE_TEMP_V, + AXP717_VMID_V = 6, + AXP717_BKUP_BATT_V, +}; + +enum axp717_adc_channel_i { + AXP717_BATT_CHRG_I = 5, +}; + enum axp813_adc_channel_v { AXP813_TS_IN = 0, AXP813_GPIO0_V, @@ -179,6 +203,22 @@ static struct iio_map axp22x_maps[] = { }, { /* sentinel */ } }; +static struct iio_map axp717_maps[] = { + { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_v", + .adc_channel_label = "vbus_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, +}; + /* * Channels are mapped by physical system. Their channels share the same index. * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw. @@ -274,6 +314,29 @@ static const struct iio_chan_spec axp22x_adc_channels[] = { AXP22X_TS_ADC_H), }; +/* + * Scale and offset is unknown for temp, ts, batt_chrg_i, vmid_v, and + * bkup_batt_v channels. Leaving scale and offset undefined for now. + */ +static const struct iio_chan_spec axp717_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE, + AXP717_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP717_VBUS_V_H), + AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE, + AXP717_VSYS_V_H), + AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP717_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP717_VMID_V, "vmid_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_BKUP_BATT_V, "bkup_batt_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), +}; + static const struct iio_chan_spec axp813_adc_channels[] = { { .type = IIO_TEMP, @@ -354,6 +417,51 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } +static int axp717_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + u8 bulk_reg[2]; + int ret; + + /* + * A generic "ADC data" channel is used for TS, tdie, vmid, + * and vbackup. This channel must both first be enabled and + * also selected before it can be read. + */ + switch (chan->channel) { + case AXP717_TS_IN: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_TS); + break; + case AXP717_DIE_TEMP_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_TEMP); + break; + case AXP717_VMID_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_VMID); + break; + case AXP717_BKUP_BATT_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_BKUP_BATT); + break; + default: + break; + } + + /* + * All channels are 14 bits, with the first 2 bits on the high + * register reserved and the remaining bits as the ADC value. + */ + ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2); + if (ret < 0) + return ret; + + *val = FIELD_GET(AXP717_ADC_DATA_MASK, get_unaligned_be16(bulk_reg)); + return IIO_VAL_INT; +} + static int axp813_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -571,6 +679,27 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, } } +static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1; + return IIO_VAL_INT; + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val, int *val2) { @@ -746,6 +875,22 @@ static int axp22x_read_raw(struct iio_dev *indio_dev, } } +static int axp717_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return axp717_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp717_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + static int axp813_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -860,6 +1005,10 @@ static const struct iio_info axp22x_adc_iio_info = { .read_raw = axp22x_read_raw, }; +static const struct iio_info axp717_adc_iio_info = { + .read_raw = axp717_read_raw, +}; + static const struct iio_info axp813_adc_iio_info = { .read_raw = axp813_read_raw, }; @@ -889,7 +1038,9 @@ struct axp_data { const struct iio_info *iio_info; int num_channels; struct iio_chan_spec const *channels; + unsigned long adc_en1; unsigned long adc_en1_mask; + unsigned long adc_en2; unsigned long adc_en2_mask; int (*adc_rate)(struct axp20x_adc_iio *info, int rate); @@ -910,7 +1061,9 @@ static const struct axp_data axp20x_data = { .iio_info = &axp20x_adc_iio_info, .num_channels = ARRAY_SIZE(axp20x_adc_channels), .channels = axp20x_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_en2 = AXP20X_ADC_EN2, .adc_en2_mask = AXP20X_ADC_EN2_MASK, .adc_rate = axp20x_adc_rate, .maps = axp20x_maps, @@ -920,15 +1073,26 @@ static const struct axp_data axp22x_data = { .iio_info = &axp22x_adc_iio_info, .num_channels = ARRAY_SIZE(axp22x_adc_channels), .channels = axp22x_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp22x_adc_rate, .maps = axp22x_maps, }; +static const struct axp_data axp717_data = { + .iio_info = &axp717_adc_iio_info, + .num_channels = ARRAY_SIZE(axp717_adc_channels), + .channels = axp717_adc_channels, + .adc_en1 = AXP717_ADC_CH_EN_CONTROL, + .adc_en1_mask = AXP717_ADC_EN1_MASK, + .maps = axp717_maps, +}; + static const struct axp_data axp813_data = { .iio_info = &axp813_adc_iio_info, .num_channels = ARRAY_SIZE(axp813_adc_channels), .channels = axp813_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp813_adc_rate, .maps = axp22x_maps, @@ -938,6 +1102,7 @@ static const struct of_device_id axp20x_adc_of_match[] = { { .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, }, { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, }, { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, + { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, }, { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, }, { /* sentinel */ } }; @@ -947,6 +1112,7 @@ static const struct platform_device_id axp20x_adc_id_match[] = { { .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, }, { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, + { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, }, { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, }, { /* sentinel */ }, }; @@ -988,14 +1154,16 @@ static int axp20x_probe(struct platform_device *pdev) indio_dev->channels = info->data->channels; /* Enable the ADCs on IP */ - regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask); + regmap_write(info->regmap, info->data->adc_en1, + info->data->adc_en1_mask); if (info->data->adc_en2_mask) - regmap_set_bits(info->regmap, AXP20X_ADC_EN2, + regmap_set_bits(info->regmap, info->data->adc_en2, info->data->adc_en2_mask); /* Configure ADCs rate */ - info->data->adc_rate(info, 100); + if (info->data->adc_rate) + info->data->adc_rate(info, 100); ret = iio_map_array_register(indio_dev, info->data->maps); if (ret < 0) { @@ -1015,10 +1183,10 @@ fail_register: iio_map_array_unregister(indio_dev); fail_map: - regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + regmap_write(info->regmap, info->data->adc_en1, 0); if (info->data->adc_en2_mask) - regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + regmap_write(info->regmap, info->data->adc_en2, 0); return ret; } @@ -1031,10 +1199,10 @@ static void axp20x_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); iio_map_array_unregister(indio_dev); - regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + regmap_write(info->regmap, info->data->adc_en1, 0); if (info->data->adc_en2_mask) - regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + regmap_write(info->regmap, info->data->adc_en2, 0); } static struct platform_driver axp20x_adc_driver = { diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index f135cf2362df..8c3acc0cd7e9 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -299,7 +299,7 @@ static int axp288_adc_probe(struct platform_device *pdev) static const struct platform_device_id axp288_adc_id_table[] = { { .name = "axp288_adc" }, - {}, + { } }; static struct platform_driver axp288_adc_driver = { diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c index 6bc149c51414..cdfe304eaa20 100644 --- a/drivers/iio/adc/bcm_iproc_adc.c +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -606,7 +606,7 @@ static void iproc_adc_remove(struct platform_device *pdev) static const struct of_device_id iproc_adc_of_match[] = { {.compatible = "brcm,iproc-static-adc", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, iproc_adc_of_match); diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 4cdddc6e36e9..fa04e0a5f645 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -351,7 +351,7 @@ static int berlin2_adc_probe(struct platform_device *pdev) static const struct of_device_id berlin2_adc_match[] = { { .compatible = "marvell,berlin2-adc", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, berlin2_adc_match); diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c index a432342348ab..2c51b90b7101 100644 --- a/drivers/iio/adc/cc10001_adc.c +++ b/drivers/iio/adc/cc10001_adc.c @@ -157,9 +157,7 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p) i = 0; sample_invalid = false; - for_each_set_bit(scan_idx, indio_dev->active_scan_mask, - indio_dev->masklength) { - + iio_for_each_active_channel(indio_dev, scan_idx) { channel = indio_dev->channels[scan_idx].channel; cc10001_adc_start(adc_dev, channel); diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 06cfbbabaf8d..de7252a10047 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -108,7 +108,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) dln2->demux_count = 0; /* Optimize all 8-channels case */ - if (indio_dev->masklength && + if (iio_get_masklength(indio_dev) && (*indio_dev->active_scan_mask & 0xff) == 0xff) { dln2_adc_add_demux(dln2, 0, 0, 16); dln2->ts_pad_offset = 0; @@ -117,9 +117,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) } /* Build demux table from fixed 8-channels to active_scan_mask */ - for_each_set_bit(out_ind, - indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, out_ind) { /* Handle timestamp separately */ if (out_ind == DLN2_ADC_MAX_CHANNELS) break; @@ -541,7 +539,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev) /* Assign trigger channel based on first enabled channel */ trigger_chan = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); if (trigger_chan < DLN2_ADC_MAX_CHANNELS) { dln2->trigger_chan = trigger_chan; ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan, diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index 971942ce4c66..cc38d5e0608e 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -228,7 +228,7 @@ static void ep93xx_adc_remove(struct platform_device *pdev) static const struct of_device_id ep93xx_adc_of_ids[] = { { .compatible = "cirrus,ep9301-adc" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 78fada4b7b1c..4d00ee8dd14d 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -519,7 +519,7 @@ static const struct of_device_id exynos_adc_match[] = { .compatible = "samsung,exynos7-adc", .data = &exynos7_adc_data, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, exynos_adc_match); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 771fa12bdc02..fb635a756440 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -524,7 +524,7 @@ static int hi8435_probe(struct spi_device *spi) static const struct of_device_id hi8435_dt_ids[] = { { .compatible = "holt,hi8435" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, hi8435_dt_ids); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index b3372ccff7d5..8da0419ecfa3 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -363,10 +363,7 @@ static irqreturn_t hx711_trigger(int irq, void *p) memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer)); - for (i = 0; i < indio_dev->masklength; i++) { - if (!test_bit(i, indio_dev->active_scan_mask)) - continue; - + iio_for_each_active_channel(indio_dev, i) { hx711_data->buffer[j] = hx711_reset_read(hx711_data, indio_dev->channels[i].channel); j++; @@ -555,7 +552,7 @@ static int hx711_probe(struct platform_device *pdev) static const struct of_device_id of_hx711_match[] = { { .compatible = "avia,hx711", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_hx711_match); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 727e390bd979..48c95e12e791 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -755,8 +755,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) * Single register reads: bulk_read will not work with ina226/219 * as there is no auto-increment of the register pointer. */ - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { unsigned int val; ret = regmap_read(chip->regmap, @@ -1053,12 +1052,12 @@ static void ina2xx_remove(struct i2c_client *client) } static const struct i2c_device_id ina2xx_id[] = { - {"ina219", ina219}, - {"ina220", ina219}, - {"ina226", ina226}, - {"ina230", ina226}, - {"ina231", ina226}, - {} + { "ina219", ina219 }, + { "ina220", ina219 }, + { "ina226", ina226 }, + { "ina230", ina226 }, + { "ina231", ina226 }, + { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); @@ -1083,7 +1082,7 @@ static const struct of_device_id ina2xx_of_match[] = { .compatible = "ti,ina231", .data = (void *)ina226 }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ina2xx_of_match); diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index af70ca760797..1e802c8779a4 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -908,7 +908,7 @@ static const struct of_device_id ingenic_adc_of_match[] = { { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, }, { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, }, { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index e34ed7dacd89..43a7bc8158b5 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -217,7 +217,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) static const struct of_device_id lpc32xx_adc_match[] = { { .compatible = "nxp,lpc3220-adc" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, lpc32xx_adc_match); diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c index 2593fa4322eb..f06dd0b9a858 100644 --- a/drivers/iio/adc/ltc2496.c +++ b/drivers/iio/adc/ltc2496.c @@ -94,7 +94,7 @@ static const struct ltc2497_chip_info ltc2496_info = { static const struct of_device_id ltc2496_of_match[] = { { .compatible = "lltc,ltc2496", .data = <c2496_info, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ltc2496_of_match); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index 6401a7727c31..f010b2fd1202 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -151,7 +151,7 @@ MODULE_DEVICE_TABLE(i2c, ltc2497_id); static const struct of_device_id ltc2497_of_match[] = { { .compatible = "lltc,ltc2497", .data = <c2497_info[TYPE_LTC2497] }, { .compatible = "lltc,ltc2499", .data = <c2497_info[TYPE_LTC2499] }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ltc2497_of_match); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 136fcf753837..f5ba4a1b5a7d 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -73,13 +73,13 @@ enum max1027_id { }; static const struct spi_device_id max1027_id[] = { - {"max1027", max1027}, - {"max1029", max1029}, - {"max1031", max1031}, - {"max1227", max1227}, - {"max1229", max1229}, - {"max1231", max1231}, - {} + { "max1027", max1027 }, + { "max1029", max1029 }, + { "max1031", max1031 }, + { "max1227", max1227 }, + { "max1229", max1229 }, + { "max1231", max1231 }, + { } }; MODULE_DEVICE_TABLE(spi, max1027_id); @@ -90,7 +90,7 @@ static const struct of_device_id max1027_adc_dt_ids[] = { { .compatible = "maxim,max1227" }, { .compatible = "maxim,max1229" }, { .compatible = "maxim,max1231" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index 49e38dca8fe2..2f07437caec3 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -143,8 +143,8 @@ static int max11100_probe(struct spi_device *spi) } static const struct of_device_id max11100_ids[] = { - {.compatible = "maxim,max11100"}, - { }, + { .compatible = "maxim,max11100" }, + { } }; MODULE_DEVICE_TABLE(of, max11100_ids); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 75ab57d9aef7..3d0a7d0eb7ee 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -174,8 +174,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p) mutex_lock(&adc->lock); - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; int ret = max1118_read(indio_dev, scan_chan->channel); @@ -261,7 +260,7 @@ static const struct spi_device_id max1118_id[] = { { "max1117", max1117 }, { "max1118", max1118 }, { "max1119", max1119 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, max1118_id); @@ -269,7 +268,7 @@ static const struct of_device_id max1118_dt_ids[] = { { .compatible = "maxim,max1117" }, { .compatible = "maxim,max1118" }, { .compatible = "maxim,max1119" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, max1118_dt_ids); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index 500bb09ab19b..d62c1a011659 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -177,12 +177,12 @@ static int max1241_probe(struct spi_device *spi) static const struct spi_device_id max1241_id[] = { { "max1241", max1241 }, - {} + { } }; static const struct of_device_id max1241_dt_ids[] = { { .compatible = "maxim,max1241" }, - {} + { } }; MODULE_DEVICE_TABLE(of, max1241_dt_ids); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index bf4b6dc53fd2..d0c6e94f7204 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -13,6 +13,7 @@ */ #include <linux/interrupt.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/sysfs.h> @@ -818,7 +819,6 @@ static int max1363_read_event_config(struct iio_dev *indio_dev, static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) { - u8 *tx_buf; int ret, i = 3, j; unsigned long numelements; int len; @@ -850,11 +850,10 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) } numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); len = 3 * numelements + 3; - tx_buf = kmalloc(len, GFP_KERNEL); - if (!tx_buf) { - ret = -ENOMEM; - goto error_ret; - } + u8 *tx_buf __free(kfree) = kmalloc(len, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + tx_buf[0] = st->configbyte; tx_buf[1] = st->setupbyte; tx_buf[2] = (st->monitor_speed << 1); @@ -893,11 +892,9 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) ret = st->send(st->client, tx_buf, len); if (ret < 0) - goto error_ret; - if (ret != len) { - ret = -EIO; - goto error_ret; - } + return ret; + if (ret != len) + return -EIO; /* * Now that we hopefully have sensible thresholds in place it is @@ -910,18 +907,13 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; ret = st->send(st->client, tx_buf, 2); if (ret < 0) - goto error_ret; - if (ret != 2) { - ret = -EIO; - goto error_ret; - } - ret = 0; - st->monitor_on = true; -error_ret: + return ret; + if (ret != 2) + return -EIO; - kfree(tx_buf); + st->monitor_on = true; - return ret; + return 0; } /* diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c index 6c2ea2bc52c6..ffec22be2d59 100644 --- a/drivers/iio/adc/max34408.c +++ b/drivers/iio/adc/max34408.c @@ -250,14 +250,14 @@ static const struct of_device_id max34408_of_match[] = { .compatible = "maxim,max34409", .data = &max34409_model_data, }, - {} + { } }; MODULE_DEVICE_TABLE(of, max34408_of_match); static const struct i2c_device_id max34408_id[] = { { "max34408", (kernel_ulong_t)&max34408_model_data }, { "max34409", (kernel_ulong_t)&max34409_model_data }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, max34408_id); diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 76e517b7b1e4..14fe42fc4b7d 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -504,9 +504,9 @@ static int max9611_init(struct max9611_dev *max9611) } static const struct of_device_id max9611_of_table[] = { - {.compatible = "maxim,max9611", .data = "max9611"}, - {.compatible = "maxim,max9612", .data = "max9612"}, - { }, + { .compatible = "maxim,max9611", .data = "max9611" }, + { .compatible = "maxim,max9612", .data = "max9612" }, + { } }; MODULE_DEVICE_TABLE(of, max9611_of_table); diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index da1421bd7b62..57cff3772ebe 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -459,16 +459,6 @@ static int mcp320x_probe(struct spi_device *spi) } static const struct of_device_id mcp320x_dt_ids[] = { - /* NOTE: The use of compatibles with no vendor prefix is deprecated. */ - { .compatible = "mcp3001" }, - { .compatible = "mcp3002" }, - { .compatible = "mcp3004" }, - { .compatible = "mcp3008" }, - { .compatible = "mcp3201" }, - { .compatible = "mcp3202" }, - { .compatible = "mcp3204" }, - { .compatible = "mcp3208" }, - { .compatible = "mcp3301" }, { .compatible = "microchip,mcp3001" }, { .compatible = "microchip,mcp3002" }, { .compatible = "microchip,mcp3004" }, diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index d83bed0e63d2..a68f1cd6883e 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -349,8 +349,6 @@ struct mcp3564_chip_info { * struct mcp3564_state - working data for a ADC device * @chip_info: chip specific data * @spi: SPI device structure - * @vref: the regulator device used as a voltage reference in case - * external voltage reference is used * @vref_mv: voltage reference value in miliVolts * @lock: synchronize access to driver's state members * @dev_addr: hardware device address @@ -369,7 +367,6 @@ struct mcp3564_chip_info { struct mcp3564_state { const struct mcp3564_chip_info *chip_info; struct spi_device *spi; - struct regulator *vref; unsigned short vref_mv; struct mutex lock; /* Synchronize access to driver's state members */ u8 dev_addr; @@ -1085,11 +1082,6 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) return 0; } -static void mcp3564_disable_reg(void *reg) -{ - regulator_disable(reg); -} - static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) { unsigned int pow = adc->chip_info->resolution - 1; @@ -1110,7 +1102,7 @@ static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) } } -static int mcp3564_config(struct iio_dev *indio_dev) +static int mcp3564_config(struct iio_dev *indio_dev, bool *use_internal_vref_attr) { struct mcp3564_state *adc = iio_priv(indio_dev); struct device *dev = &adc->spi->dev; @@ -1119,6 +1111,7 @@ static int mcp3564_config(struct iio_dev *indio_dev) enum mcp3564_ids ids; int ret = 0; unsigned int tmp = 0x01; + bool internal_vref; bool err = false; /* @@ -1218,36 +1211,22 @@ static int mcp3564_config(struct iio_dev *indio_dev) dev_dbg(dev, "Found %s chip\n", adc->chip_info->name); - adc->vref = devm_regulator_get_optional(dev, "vref"); - if (IS_ERR(adc->vref)) { - if (PTR_ERR(adc->vref) != -ENODEV) - return dev_err_probe(dev, PTR_ERR(adc->vref), - "failed to get regulator\n"); + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get vref voltage\n"); + + internal_vref = ret == -ENODEV; + adc->vref_mv = internal_vref ? MCP3564R_INT_VREF_MV : ret / MILLI; + *use_internal_vref_attr = internal_vref; + if (internal_vref) { /* Check if chip has internal vref */ if (!adc->have_vref) - return dev_err_probe(dev, PTR_ERR(adc->vref), - "Unknown Vref\n"); - adc->vref = NULL; + return dev_err_probe(dev, -ENODEV, "Unknown Vref\n"); + dev_dbg(dev, "%s: Using internal Vref\n", __func__); } else { - ret = regulator_enable(adc->vref); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, mcp3564_disable_reg, - adc->vref); - if (ret) - return ret; - dev_dbg(dev, "%s: Using External Vref\n", __func__); - - ret = regulator_get_voltage(adc->vref); - if (ret < 0) - return dev_err_probe(dev, ret, - "Failed to read vref regulator\n"); - - adc->vref_mv = ret / MILLI; } ret = mcp3564_parse_fw_children(indio_dev); @@ -1350,10 +1329,8 @@ static int mcp3564_config(struct iio_dev *indio_dev) tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK); tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT; - if (!adc->vref) { + if (internal_vref) tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1); - adc->vref_mv = MCP3564R_INT_VREF_MV; - } ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg); @@ -1412,6 +1389,7 @@ static int mcp3564_probe(struct spi_device *spi) int ret; struct iio_dev *indio_dev; struct mcp3564_state *adc; + bool use_internal_vref_attr; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); if (!indio_dev) @@ -1428,7 +1406,7 @@ static int mcp3564_probe(struct spi_device *spi) * enable/disable certain channels * change the sampling rate to the requested value */ - ret = mcp3564_config(indio_dev); + ret = mcp3564_config(indio_dev, &use_internal_vref_attr); if (ret) return dev_err_probe(&spi->dev, ret, "Can't configure MCP356X device\n"); @@ -1440,7 +1418,7 @@ static int mcp3564_probe(struct spi_device *spi) indio_dev->name = adc->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - if (!adc->vref) + if (use_internal_vref_attr) indio_dev->info = &mcp3564r_info; else indio_dev->info = &mcp3564_info; diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 7a32e7a1be9d..d0e77971c5d3 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -103,7 +103,7 @@ struct mcp3911_chip_info { const struct iio_chan_spec *channels; unsigned int num_channels; - int (*config)(struct mcp3911 *adc); + int (*config)(struct mcp3911 *adc, bool external_vref); int (*get_osr)(struct mcp3911 *adc, u32 *val); int (*set_osr)(struct mcp3911 *adc, u32 val); int (*enable_offset)(struct mcp3911 *adc, bool enable); @@ -115,7 +115,6 @@ struct mcp3911_chip_info { struct mcp3911 { struct spi_device *spi; struct mutex lock; - struct regulator *vref; struct clk *clki; u32 dev_addr; struct iio_trigger *trig; @@ -385,23 +384,11 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, } } -static int mcp3911_calc_scale_table(struct mcp3911 *adc) +static int mcp3911_calc_scale_table(u32 vref_mv) { - struct device *dev = &adc->spi->dev; - u32 ref = MCP3911_INT_VREF_MV; u32 div; - int ret; u64 tmp; - if (adc->vref) { - ret = regulator_get_voltage(adc->vref); - if (ret < 0) { - return dev_err_probe(dev, ret, "failed to get vref voltage\n"); - } - - ref = ret / 1000; - } - /* * For 24-bit Conversion * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5 @@ -412,7 +399,7 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc) */ for (int i = 0; i < MCP3911_NUM_SCALES; i++) { div = 12582912 * BIT(i); - tmp = div_s64((s64)ref * 1000000000LL, div); + tmp = div_s64((s64)vref_mv * 1000000000LL, div); mcp3911_scale_table[i][0] = 0; mcp3911_scale_table[i][1] = tmp; @@ -523,7 +510,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) goto out; } - for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]); @@ -544,7 +531,7 @@ static const struct iio_info mcp3911_info = { .write_raw_get_fmt = mcp3911_write_raw_get_fmt, }; -static int mcp3911_config(struct mcp3911 *adc) +static int mcp3911_config(struct mcp3911 *adc, bool external_vref) { struct device *dev = &adc->spi->dev; u32 regval; @@ -555,7 +542,7 @@ static int mcp3911_config(struct mcp3911 *adc) return ret; regval &= ~MCP3911_CONFIG_VREFEXT; - if (adc->vref) { + if (external_vref) { dev_dbg(dev, "use external voltage reference\n"); regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1); } else { @@ -610,7 +597,7 @@ static int mcp3911_config(struct mcp3911 *adc) return mcp3911_write(adc, MCP3911_REG_GAIN, regval, 1); } -static int mcp3910_config(struct mcp3911 *adc) +static int mcp3910_config(struct mcp3911 *adc, bool external_vref) { struct device *dev = &adc->spi->dev; u32 regval; @@ -621,7 +608,7 @@ static int mcp3910_config(struct mcp3911 *adc) return ret; regval &= ~MCP3910_CONFIG1_VREFEXT; - if (adc->vref) { + if (external_vref) { dev_dbg(dev, "use external voltage reference\n"); regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 1); } else { @@ -677,11 +664,6 @@ static int mcp3910_config(struct mcp3911 *adc) return adc->chip->enable_offset(adc, 0); } -static void mcp3911_cleanup_regulator(void *vref) -{ - regulator_disable(vref); -} - static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable) { struct mcp3911 *adc = iio_trigger_get_drvdata(trig); @@ -704,6 +686,8 @@ static int mcp3911_probe(struct spi_device *spi) struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct mcp3911 *adc; + bool external_vref; + u32 vref_mv; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); @@ -714,23 +698,12 @@ static int mcp3911_probe(struct spi_device *spi) adc->spi = spi; adc->chip = spi_get_device_match_data(spi); - adc->vref = devm_regulator_get_optional(dev, "vref"); - if (IS_ERR(adc->vref)) { - if (PTR_ERR(adc->vref) == -ENODEV) { - adc->vref = NULL; - } else { - return dev_err_probe(dev, PTR_ERR(adc->vref), "failed to get regulator\n"); - } + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get vref voltage\n"); - } else { - ret = regulator_enable(adc->vref); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, mcp3911_cleanup_regulator, adc->vref); - if (ret) - return ret; - } + external_vref = ret != -ENODEV; + vref_mv = external_vref ? ret / 1000 : MCP3911_INT_VREF_MV; adc->clki = devm_clk_get_enabled(dev, NULL); if (IS_ERR(adc->clki)) { @@ -755,11 +728,11 @@ static int mcp3911_probe(struct spi_device *spi) } dev_dbg(dev, "use device address %i\n", adc->dev_addr); - ret = adc->chip->config(adc); + ret = adc->chip->config(adc, external_vref); if (ret) return ret; - ret = mcp3911_calc_scale_table(adc); + ret = mcp3911_calc_scale_table(vref_mv); if (ret) return ret; diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 5f672765d4a2..5fbf9b6abd9c 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -184,8 +184,8 @@ static void mp2629_adc_remove(struct platform_device *pdev) } static const struct of_device_id mp2629_adc_of_match[] = { - { .compatible = "mps,mp2629_adc"}, - {} + { .compatible = "mps,mp2629_adc" }, + { } }; MODULE_DEVICE_TABLE(of, mp2629_adc_of_match); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 3710473e526f..e2ec805e834f 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -268,7 +268,7 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p) int i = 0, bit, val, ret; memset(&data, 0, sizeof(data)); - for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = mt6360_adc_read_channel(mad, bit, &val); if (ret < 0) { dev_warn(&indio_dev->dev, "Failed to get channel %d conversion val\n", bit); @@ -355,7 +355,7 @@ static int mt6360_adc_probe(struct platform_device *pdev) static const struct of_device_id mt6360_adc_of_id[] = { { .compatible = "mediatek,mt6360-adc", }, - {} + { } }; MODULE_DEVICE_TABLE(of, mt6360_adc_of_id); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 600151a62f1f..458544cb8ee4 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -539,7 +539,7 @@ MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id); static const struct of_device_id nau7802_dt_ids[] = { { .compatible = "nuvoton,nau7802" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, nau7802_dt_ids); diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c new file mode 100644 index 000000000000..4c2a1c07bc39 --- /dev/null +++ b/drivers/iio/adc/pac1921.c @@ -0,0 +1,1261 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for PAC1921 High-Side Power/Current Monitor + * + * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com> + */ + +#include <asm/unaligned.h> +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/regmap.h> +#include <linux/units.h> + +/* pac1921 registers */ +#define PAC1921_REG_GAIN_CFG 0x00 +#define PAC1921_REG_INT_CFG 0x01 +#define PAC1921_REG_CONTROL 0x02 +#define PAC1921_REG_VBUS 0x10 +#define PAC1921_REG_VSENSE 0x12 +#define PAC1921_REG_OVERFLOW_STS 0x1C +#define PAC1921_REG_VPOWER 0x1D + +/* pac1921 gain configuration bits */ +#define PAC1921_GAIN_DI_GAIN_MASK GENMASK(5, 3) +#define PAC1921_GAIN_DV_GAIN_MASK GENMASK(2, 0) + +/* pac1921 integration configuration bits */ +#define PAC1921_INT_CFG_SMPL_MASK GENMASK(7, 4) +#define PAC1921_INT_CFG_VSFEN BIT(3) +#define PAC1921_INT_CFG_VBFEN BIT(2) +#define PAC1921_INT_CFG_RIOV BIT(1) +#define PAC1921_INT_CFG_INTEN BIT(0) + +/* pac1921 control bits */ +#define PAC1921_CONTROL_MXSL_MASK GENMASK(7, 6) +enum pac1921_mxsl { + PAC1921_MXSL_VPOWER_PIN = 0, + PAC1921_MXSL_VSENSE_FREE_RUN = 1, + PAC1921_MXSL_VBUS_FREE_RUN = 2, + PAC1921_MXSL_VPOWER_FREE_RUN = 3, +}; +#define PAC1921_CONTROL_SLEEP BIT(2) + +/* pac1921 result registers mask and resolution */ +#define PAC1921_RES_MASK GENMASK(15, 6) +#define PAC1921_RES_RESOLUTION 1023 + +/* pac1921 overflow status bits */ +#define PAC1921_OVERFLOW_VSOV BIT(2) +#define PAC1921_OVERFLOW_VBOV BIT(1) +#define PAC1921_OVERFLOW_VPOV BIT(0) + +/* pac1921 constants */ +#define PAC1921_MAX_VSENSE_MV 100 +#define PAC1921_MAX_VBUS_V 32 +/* Time to first communication after power up (tINT_T) */ +#define PAC1921_POWERUP_TIME_MS 20 +/* Time from Sleep State to Start of Integration Period (tSLEEP_TO_INT) */ +#define PAC1921_SLEEP_TO_INT_TIME_US 86 + +/* pac1921 defaults */ +#define PAC1921_DEFAULT_DV_GAIN 0 /* 2^(value): 1x gain (HW default) */ +#define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */ +#define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */ + +/* + * Pre-computed scale factors for BUS voltage + * format: IIO_VAL_INT_PLUS_NANO + * unit: mV + * + * Vbus scale (mV) = max_vbus (mV) / dv_gain / resolution + */ +static const int pac1921_vbus_scales[][2] = { + { 31, 280547409 }, /* dv_gain x1 */ + { 15, 640273704 }, /* dv_gain x2 */ + { 7, 820136852 }, /* dv_gain x4 */ + { 3, 910068426 }, /* dv_gain x8 */ + { 1, 955034213 }, /* dv_gain x16 */ + { 0, 977517106 }, /* dv_gain x32 */ +}; + +/* + * Pre-computed scales for SENSE voltage + * format: IIO_VAL_INT_PLUS_NANO + * unit: mV + * + * Vsense scale (mV) = max_vsense (mV) / di_gain / resolution + */ +static const int pac1921_vsense_scales[][2] = { + { 0, 97751710 }, /* di_gain x1 */ + { 0, 48875855 }, /* di_gain x2 */ + { 0, 24437927 }, /* di_gain x4 */ + { 0, 12218963 }, /* di_gain x8 */ + { 0, 6109481 }, /* di_gain x16 */ + { 0, 3054740 }, /* di_gain x32 */ + { 0, 1527370 }, /* di_gain x64 */ + { 0, 763685 }, /* di_gain x128 */ +}; + +/* + * Numbers of samples used to integrate measurements at the end of an + * integration period. + * + * Changing the number of samples affects the integration period: higher the + * number of samples, longer the integration period. + * + * These correspond to the oversampling ratios available exposed to userspace. + */ +static const int pac1921_int_num_samples[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 +}; + +/* + * The integration period depends on the configuration of number of integration + * samples, measurement resolution and post filters. The following array + * contains integration periods, in microsecs unit, based on table 4-5 from + * datasheet considering power integration mode, 14-Bit resolution and post + * filters on. Each index corresponds to a specific number of samples from 1 + * to 2048. + */ +static const unsigned int pac1921_int_periods_usecs[] = { + 2720, /* 1 sample */ + 4050, /* 2 samples */ + 6790, /* 4 samples */ + 12200, /* 8 samples */ + 23000, /* 16 samples */ + 46000, /* 32 samples */ + 92000, /* 64 samples */ + 184000, /* 128 samples */ + 368000, /* 256 samples */ + 736000, /* 512 samples */ + 1471000, /* 1024 samples */ + 2941000 /* 2048 samples */ +}; + +/* pac1921 regmap configuration */ +static const struct regmap_range pac1921_regmap_wr_ranges[] = { + regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL), +}; + +static const struct regmap_access_table pac1921_regmap_wr_table = { + .yes_ranges = pac1921_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_wr_ranges), +}; + +static const struct regmap_range pac1921_regmap_rd_ranges[] = { + regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL), + regmap_reg_range(PAC1921_REG_VBUS, PAC1921_REG_VPOWER + 1), +}; + +static const struct regmap_access_table pac1921_regmap_rd_table = { + .yes_ranges = pac1921_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_rd_ranges), +}; + +static const struct regmap_config pac1921_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &pac1921_regmap_rd_table, + .wr_table = &pac1921_regmap_wr_table, +}; + +enum pac1921_channels { + PAC1921_CHAN_VBUS = 0, + PAC1921_CHAN_VSENSE = 1, + PAC1921_CHAN_CURRENT = 2, + PAC1921_CHAN_POWER = 3, +}; +#define PAC1921_NUM_MEAS_CHANS 4 + +struct pac1921_priv { + struct i2c_client *client; + struct regmap *regmap; + struct regulator *vdd; + struct iio_info iio_info; + + /* + * Synchronize access to private members, and ensure atomicity of + * consecutive regmap operations. + */ + struct mutex lock; + + u32 rshunt_uohm; /* uOhm */ + u8 dv_gain; + u8 di_gain; + u8 n_samples; + u8 prev_ovf_flags; + u8 ovf_enabled_events; + + bool first_integr_started; + bool first_integr_done; + unsigned long integr_started_time_jiffies; + unsigned int integr_period_usecs; + + int current_scales[ARRAY_SIZE(pac1921_vsense_scales)][2]; + + struct { + u16 chan[PAC1921_NUM_MEAS_CHANS]; + s64 timestamp __aligned(8); + } scan; +}; + +/* + * Check if first integration after configuration update has completed. + * + * Must be called with lock held. + */ +static bool pac1921_data_ready(struct pac1921_priv *priv) +{ + if (!priv->first_integr_started) + return false; + + if (!priv->first_integr_done) { + unsigned long t_ready; + + /* + * Data valid after the device entered into integration state, + * considering worst case where the device was in sleep state, + * and completed the first integration period. + */ + t_ready = priv->integr_started_time_jiffies + + usecs_to_jiffies(PAC1921_SLEEP_TO_INT_TIME_US) + + usecs_to_jiffies(priv->integr_period_usecs); + + if (time_before(jiffies, t_ready)) + return false; + + priv->first_integr_done = true; + } + + return true; +} + +static inline void pac1921_calc_scale(int dividend, int divisor, int *val, + int *val2) +{ + s64 tmp; + + tmp = div_s64(dividend * (s64)NANO, divisor); + *val = (int)div_s64_rem(tmp, NANO, val2); +} + +/* + * Fill the table of scale factors for current + * format: IIO_VAL_INT_PLUS_NANO + * unit: mA + * + * Vsense LSB (nV) = max_vsense (nV) * di_gain / resolution + * Current scale (mA) = Vsense LSB (nV) / shunt (uOhm) + * + * Must be called with held lock when updating after first initialization. + */ +static void pac1921_calc_current_scales(struct pac1921_priv *priv) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(priv->current_scales); i++) { + int max = (PAC1921_MAX_VSENSE_MV * MICRO) >> i; + int vsense_lsb = DIV_ROUND_CLOSEST(max, PAC1921_RES_RESOLUTION); + + pac1921_calc_scale(vsense_lsb, (int)priv->rshunt_uohm, + &priv->current_scales[i][0], + &priv->current_scales[i][1]); + } +} + +/* + * Check if overflow occurred and if so, push the corresponding events. + * + * Must be called with lock held. + */ +static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + unsigned int flags; + int ret; + + ret = regmap_read(priv->regmap, PAC1921_REG_OVERFLOW_STS, &flags); + if (ret) + return ret; + + if (flags & PAC1921_OVERFLOW_VBOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VBOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_VOLTAGE, PAC1921_CHAN_VBUS, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + if (flags & PAC1921_OVERFLOW_VSOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VSOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_VOLTAGE, PAC1921_CHAN_VSENSE, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_CURRENT, PAC1921_CHAN_CURRENT, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + if (flags & PAC1921_OVERFLOW_VPOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VPOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_POWER, PAC1921_CHAN_POWER, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + + priv->prev_ovf_flags = (u8)flags; + + return 0; +} + +/* + * Read the value from a result register + * + * Result registers contain the most recent averaged values of Vbus, Vsense and + * Vpower. Each value is 10 bits wide and spread across two consecutive 8 bit + * registers, with 6 bit LSB zero padding. + */ +static int pac1921_read_res(struct pac1921_priv *priv, unsigned long reg, + u16 *val) +{ + int ret = regmap_bulk_read(priv->regmap, (unsigned int)reg, val, + sizeof(*val)); + if (ret) + return ret; + + *val = FIELD_GET(PAC1921_RES_MASK, get_unaligned_be16(val)); + + return 0; +} + +static int pac1921_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + s64 ts; + u16 res_val; + int ret; + + if (!pac1921_data_ready(priv)) + return -EBUSY; + + ts = iio_get_time_ns(indio_dev); + + ret = pac1921_check_push_overflow(indio_dev, ts); + if (ret) + return ret; + + ret = pac1921_read_res(priv, chan->address, &res_val); + if (ret) + return ret; + + *val = (int)res_val; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + *val = pac1921_vbus_scales[priv->dv_gain][0]; + *val2 = pac1921_vbus_scales[priv->dv_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_VSENSE: + *val = pac1921_vsense_scales[priv->di_gain][0]; + *val2 = pac1921_vsense_scales[priv->di_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_CURRENT: + *val = priv->current_scales[priv->di_gain][0]; + *val2 = priv->current_scales[priv->di_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_POWER: { + /* + * Power scale factor in mW: + * Current scale (mA) * max_vbus (V) / dv_gain + */ + + /* Get current scale based on di_gain */ + int *curr_scale = priv->current_scales[priv->di_gain]; + + /* Convert current_scale from INT_PLUS_NANO to INT */ + s64 tmp = curr_scale[0] * (s64)NANO + curr_scale[1]; + + /* Multiply by max_vbus (V) / dv_gain */ + tmp *= PAC1921_MAX_VBUS_V >> (int)priv->dv_gain; + + /* Convert back to INT_PLUS_NANO */ + *val = (int)div_s64_rem(tmp, NANO, val2); + + return IIO_VAL_INT_PLUS_NANO; + } + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = pac1921_int_num_samples[priv->n_samples]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * The sampling frequency (Hz) is read-only and corresponds to + * how often the device provides integrated measurements into + * the result registers, thus it's 1/integration_period. + * The integration period depends on the number of integration + * samples, measurement resolution and post filters. + * + * 1/(integr_period_usecs/MICRO) = MICRO/integr_period_usecs + */ + *val = MICRO; + *val2 = (int)priv->integr_period_usecs; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } +} + +static int pac1921_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_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = pac1921_int_num_samples; + *length = ARRAY_SIZE(pac1921_int_num_samples); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +/* + * Perform configuration update sequence: set the device into read state, then + * write the config register and set the device back into integration state. + * Also reset integration start time and mark first integration to be yet + * completed. + * + * Must be called with lock held. + */ +static int pac1921_update_cfg_reg(struct pac1921_priv *priv, unsigned int reg, + unsigned int mask, unsigned int val) +{ + /* Enter READ state before configuration */ + int ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + /* Update configuration value */ + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + return ret; + + /* Re-enable integration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN); + if (ret) + return ret; + + /* + * Reset integration started time and mark this integration period as + * the first one so that new measurements will be considered as valid + * only at the end of this integration period. + */ + priv->integr_started_time_jiffies = jiffies; + priv->first_integr_done = false; + + return 0; +} + +/* + * Retrieve the index of the given scale (represented by scale_val and + * scale_val2) from scales_tbl. The returned index (if found) is the log2 of + * the gain corresponding to the given scale. + * + * Must be called with lock held if the scales_tbl can change runtime (e.g. for + * the current scales table) + */ +static int pac1921_lookup_scale(const int (*const scales_tbl)[2], size_t size, + int scale_val, int scale_val2) +{ + for (unsigned int i = 0; i < size; i++) + if (scales_tbl[i][0] == scale_val && + scales_tbl[i][1] == scale_val2) + return (int)i; + + return -EINVAL; +} + +/* + * Configure device with the given gain (only if changed) + * + * Must be called with lock held. + */ +static int pac1921_update_gain(struct pac1921_priv *priv, u8 *priv_val, u8 gain, + unsigned int mask) +{ + unsigned int reg_val; + int ret; + + if (*priv_val == gain) + return 0; + + reg_val = (gain << __ffs(mask)) & mask; + ret = pac1921_update_cfg_reg(priv, PAC1921_REG_GAIN_CFG, mask, reg_val); + if (ret) + return ret; + + *priv_val = gain; + + return 0; +} + +/* + * Given a scale factor represented by scale_val and scale_val2 with format + * IIO_VAL_INT_PLUS_NANO, find the corresponding gain value and write it to the + * device. + * + * Must be called with lock held. + */ +static int pac1921_update_gain_from_scale(struct pac1921_priv *priv, + struct iio_chan_spec const *chan, + int scale_val, int scale_val2) +{ + int ret; + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + ret = pac1921_lookup_scale(pac1921_vbus_scales, + ARRAY_SIZE(pac1921_vbus_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->dv_gain, (u8)ret, + PAC1921_GAIN_DV_GAIN_MASK); + case PAC1921_CHAN_VSENSE: + ret = pac1921_lookup_scale(pac1921_vsense_scales, + ARRAY_SIZE(pac1921_vsense_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->di_gain, (u8)ret, + PAC1921_GAIN_DI_GAIN_MASK); + case PAC1921_CHAN_CURRENT: + ret = pac1921_lookup_scale(priv->current_scales, + ARRAY_SIZE(priv->current_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->di_gain, (u8)ret, + PAC1921_GAIN_DI_GAIN_MASK); + default: + return -EINVAL; + } +} + +/* + * Retrieve the index of the given number of samples from the constant table. + * The returned index (if found) is the log2 of the given num_samples. + */ +static int pac1921_lookup_int_num_samples(int num_samples) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(pac1921_int_num_samples); i++) + if (pac1921_int_num_samples[i] == num_samples) + return (int)i; + + return -EINVAL; +} + +/* + * Update the device with the given number of integration samples. + * + * Must be called with lock held. + */ +static int pac1921_update_int_num_samples(struct pac1921_priv *priv, + int num_samples) +{ + unsigned int reg_val; + u8 n_samples; + int ret; + + ret = pac1921_lookup_int_num_samples(num_samples); + if (ret < 0) + return ret; + + n_samples = (u8)ret; + + if (priv->n_samples == n_samples) + return 0; + + reg_val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, n_samples); + + ret = pac1921_update_cfg_reg(priv, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_SMPL_MASK, reg_val); + if (ret) + return ret; + + priv->n_samples = n_samples; + + priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples]; + + return 0; +} + +static int pac1921_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pac1921_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return pac1921_update_gain_from_scale(priv, chan, val, val2); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return pac1921_update_int_num_samples(priv, val); + default: + return -EINVAL; + } +} + +static int pac1921_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + return sprintf(label, "vbus\n"); + case PAC1921_CHAN_VSENSE: + return sprintf(label, "vsense\n"); + case PAC1921_CHAN_CURRENT: + return sprintf(label, "current\n"); + case PAC1921_CHAN_POWER: + return sprintf(label, "power\n"); + default: + return -EINVAL; + } +} + +static int pac1921_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV); + case PAC1921_CHAN_VSENSE: + case PAC1921_CHAN_CURRENT: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV); + case PAC1921_CHAN_POWER: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV); + default: + return -EINVAL; + } +} + +static int pac1921_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + u8 ovf_bit; + + guard(mutex)(&priv->lock); + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + ovf_bit = PAC1921_OVERFLOW_VBOV; + break; + case PAC1921_CHAN_VSENSE: + case PAC1921_CHAN_CURRENT: + ovf_bit = PAC1921_OVERFLOW_VSOV; + break; + case PAC1921_CHAN_POWER: + ovf_bit = PAC1921_OVERFLOW_VPOV; + break; + default: + return -EINVAL; + } + + if (state) + priv->ovf_enabled_events |= ovf_bit; + else + priv->ovf_enabled_events &= ~ovf_bit; + + return 0; +} + +static int pac1921_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + *val = PAC1921_RES_RESOLUTION; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info pac1921_iio = { + .read_raw = pac1921_read_raw, + .read_avail = pac1921_read_avail, + .write_raw = pac1921_write_raw, + .write_raw_get_fmt = pac1921_write_raw_get_fmt, + .read_label = pac1921_read_label, + .read_event_config = pac1921_read_event_config, + .write_event_config = pac1921_write_event_config, + .read_event_value = pac1921_read_event_value, +}; + +static ssize_t pac1921_read_shunt_resistor(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + int vals[2]; + + if (chan->channel != PAC1921_CHAN_CURRENT) + return -EINVAL; + + guard(mutex)(&priv->lock); + + vals[0] = (int)priv->rshunt_uohm; + vals[1] = MICRO; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + u64 rshunt_uohm; + int val, val_fract; + int ret; + + if (chan->channel != PAC1921_CHAN_CURRENT) + return -EINVAL; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + return ret; + + rshunt_uohm = (u32)val * MICRO + (u32)val_fract; + if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX) + return -EINVAL; + + guard(mutex)(&priv->lock); + + priv->rshunt_uohm = (u32)rshunt_uohm; + + pac1921_calc_current_scales(priv); + + return len; +} + +/* + * Emit on sysfs the list of available scales contained in scales_tbl + * + * TODO:: this function can be replaced with iio_format_avail_list() if the + * latter will ever be exported. + * + * Must be called with lock held if the scales_tbl can change runtime (e.g. for + * the current scales table) + */ +static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2], + size_t size, char *buf) +{ + ssize_t len = 0; + + for (unsigned int i = 0; i < size; i++) { + if (i != 0) { + len += sysfs_emit_at(buf, len, " "); + if (len >= PAGE_SIZE) + return -EFBIG; + } + len += sysfs_emit_at(buf, len, "%d.%09d", scales_tbl[i][0], + scales_tbl[i][1]); + if (len >= PAGE_SIZE) + return -EFBIG; + } + + len += sysfs_emit_at(buf, len, "\n"); + return len; +} + +/* + * Read available scales for a specific channel + * + * NOTE: using extended info insted of iio.read_avail() because access to + * current scales must be locked as they depend on shunt resistor which may + * change runtime. Caller of iio.read_avail() would access the table unlocked + * instead. + */ +static ssize_t pac1921_read_scale_avail(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + const int (*scales_tbl)[2]; + size_t size; + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + scales_tbl = pac1921_vbus_scales; + size = ARRAY_SIZE(pac1921_vbus_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + + case PAC1921_CHAN_VSENSE: + scales_tbl = pac1921_vsense_scales; + size = ARRAY_SIZE(pac1921_vsense_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + + case PAC1921_CHAN_CURRENT: { + guard(mutex)(&priv->lock); + scales_tbl = priv->current_scales; + size = ARRAY_SIZE(priv->current_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + } + default: + return -EINVAL; + } +} + +#define PAC1921_EXT_INFO_SCALE_AVAIL { \ + .name = "scale_available", \ + .read = pac1921_read_scale_avail, \ + .shared = IIO_SEPARATE, \ +} + +static const struct iio_chan_spec_ext_info pac1921_ext_info_voltage[] = { + PAC1921_EXT_INFO_SCALE_AVAIL, + {} +}; + +static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = { + PAC1921_EXT_INFO_SCALE_AVAIL, + { + .name = "shunt_resistor", + .read = pac1921_read_shunt_resistor, + .write = pac1921_write_shunt_resistor, + .shared = IIO_SEPARATE, + }, + {} +}; + +static const struct iio_event_spec pac1921_overflow_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE), + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec pac1921_channels[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_VBUS, + .address = PAC1921_REG_VBUS, + .scan_index = PAC1921_CHAN_VBUS, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .indexed = 1, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_voltage, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_VSENSE, + .address = PAC1921_REG_VSENSE, + .scan_index = PAC1921_CHAN_VSENSE, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .indexed = 1, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_voltage, + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_CURRENT, + .address = PAC1921_REG_VSENSE, + .scan_index = PAC1921_CHAN_CURRENT, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_current, + }, + { + .type = IIO_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_POWER, + .address = PAC1921_REG_VPOWER, + .scan_index = PAC1921_CHAN_POWER, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + }, + IIO_CHAN_SOFT_TIMESTAMP(PAC1921_NUM_MEAS_CHANS), +}; + +static irqreturn_t pac1921_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct pac1921_priv *priv = iio_priv(idev); + int ret; + int bit; + int ch = 0; + + guard(mutex)(&priv->lock); + + if (!pac1921_data_ready(priv)) + goto done; + + ret = pac1921_check_push_overflow(idev, pf->timestamp); + if (ret) + goto done; + + iio_for_each_active_channel(idev, bit) { + u16 val; + + ret = pac1921_read_res(priv, idev->channels[ch].address, &val); + if (ret) + goto done; + + priv->scan.chan[ch++] = val; + } + + iio_push_to_buffers_with_timestamp(idev, &priv->scan, pf->timestamp); + +done: + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +/* + * Initialize device by writing initial configuration and putting it into + * integration state. + * + * Must be called with lock held when called after first initialization + * (e.g. from pm resume) + */ +static int pac1921_init(struct pac1921_priv *priv) +{ + unsigned int val; + int ret; + + /* Enter READ state before configuration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + /* Configure gains, use 14-bits measurement resolution (HW default) */ + val = FIELD_PREP(PAC1921_GAIN_DI_GAIN_MASK, priv->di_gain) | + FIELD_PREP(PAC1921_GAIN_DV_GAIN_MASK, priv->dv_gain); + ret = regmap_write(priv->regmap, PAC1921_REG_GAIN_CFG, val); + if (ret) + return ret; + + /* + * Configure integration: + * - num of integration samples + * - filters enabled (HW default) + * - set READ/INT pin override (RIOV) to control operation mode via + * register instead of pin + */ + val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, priv->n_samples) | + PAC1921_INT_CFG_VSFEN | PAC1921_INT_CFG_VBFEN | + PAC1921_INT_CFG_RIOV; + ret = regmap_write(priv->regmap, PAC1921_REG_INT_CFG, val); + if (ret) + return ret; + + /* + * Init control register: + * - VPower free run integration mode + * - OUT pin full scale range: 3V (HW detault) + * - no timeout, no sleep, no sleep override, no recalc (HW defaults) + */ + val = FIELD_PREP(PAC1921_CONTROL_MXSL_MASK, + PAC1921_MXSL_VPOWER_FREE_RUN); + ret = regmap_write(priv->regmap, PAC1921_REG_CONTROL, val); + if (ret) + return ret; + + /* Enable integration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN); + if (ret) + return ret; + + priv->first_integr_started = true; + priv->integr_started_time_jiffies = jiffies; + priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples]; + + return 0; +} + +static int pac1921_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct pac1921_priv *priv = iio_priv(indio_dev); + int ret; + + guard(mutex)(&priv->lock); + + priv->first_integr_started = false; + priv->first_integr_done = false; + + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, PAC1921_REG_CONTROL, + PAC1921_CONTROL_SLEEP, PAC1921_CONTROL_SLEEP); + if (ret) + return ret; + + return regulator_disable(priv->vdd); + +} + +static int pac1921_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct pac1921_priv *priv = iio_priv(indio_dev); + int ret; + + guard(mutex)(&priv->lock); + + ret = regulator_enable(priv->vdd); + if (ret) + return ret; + + msleep(PAC1921_POWERUP_TIME_MS); + + return pac1921_init(priv); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pac1921_pm_ops, pac1921_suspend, + pac1921_resume); + +static void pac1921_regulator_disable(void *data) +{ + struct regulator *regulator = data; + + regulator_disable(regulator); +} + +static int pac1921_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pac1921_priv *priv; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->client = client; + i2c_set_clientdata(client, indio_dev); + + priv->regmap = devm_regmap_init_i2c(client, &pac1921_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, (int)PTR_ERR(priv->regmap), + "Cannot initialize register map\n"); + + devm_mutex_init(dev, &priv->lock); + + priv->dv_gain = PAC1921_DEFAULT_DV_GAIN; + priv->di_gain = PAC1921_DEFAULT_DI_GAIN; + priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &priv->rshunt_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Cannot read shunt resistor property\n"); + if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX) + return dev_err_probe(dev, -EINVAL, + "Invalid shunt resistor: %u\n", + priv->rshunt_uohm); + + pac1921_calc_current_scales(priv); + + priv->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(priv->vdd)) + return dev_err_probe(dev, (int)PTR_ERR(priv->vdd), + "Cannot get vdd regulator\n"); + + ret = regulator_enable(priv->vdd); + if (ret) + return dev_err_probe(dev, ret, "Cannot enable vdd regulator\n"); + + ret = devm_add_action_or_reset(dev, pac1921_regulator_disable, + priv->vdd); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add action for vdd regulator disposal\n"); + + msleep(PAC1921_POWERUP_TIME_MS); + + ret = pac1921_init(priv); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize device\n"); + + priv->iio_info = pac1921_iio; + + indio_dev->name = "pac1921"; + indio_dev->info = &priv->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pac1921_channels; + indio_dev->num_channels = ARRAY_SIZE(pac1921_channels); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &pac1921_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Cannot setup IIO triggered buffer\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Cannot register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id pac1921_id[] = { + { .name = "pac1921", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pac1921_id); + +static const struct of_device_id pac1921_of_match[] = { + { .compatible = "microchip,pac1921" }, + { } +}; +MODULE_DEVICE_TABLE(of, pac1921_of_match); + +static struct i2c_driver pac1921_driver = { + .driver = { + .name = "pac1921", + .pm = pm_sleep_ptr(&pac1921_pm_ops), + .of_match_table = pac1921_of_match, + }, + .probe = pac1921_probe, + .id_table = pac1921_id, +}; + +module_i2c_driver(pac1921_driver); + +MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>"); +MODULE_DESCRIPTION("IIO driver for PAC1921 High-Side Power/Current Monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index ae24a27805ab..8210728034d0 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1571,7 +1571,7 @@ static const struct i2c_device_id pac1934_id[] = { { .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] }, { .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] }, { .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, pac1934_id); @@ -1592,7 +1592,7 @@ static const struct of_device_id pac1934_of_match[] = { .compatible = "microchip,pac1934", .data = &pac1934_chip_config[PAC1934] }, - {} + { } }; MODULE_DEVICE_TABLE(of, pac1934_of_match); @@ -1602,7 +1602,7 @@ MODULE_DEVICE_TABLE(of, pac1934_of_match); */ static const struct acpi_device_id pac1934_acpi_match[] = { { "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match); diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index c9d2c66434e4..9e1112f5acc6 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -1006,7 +1006,7 @@ static const struct of_device_id pm8xxx_xoadc_id_table[] = { .compatible = "qcom,pm8921-adc", .data = &pm8921_variant, }, - { }, + { } }; MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table); diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c index 1402df68dd52..6aa70b4629a7 100644 --- a/drivers/iio/adc/qcom-spmi-rradc.c +++ b/drivers/iio/adc/qcom-spmi-rradc.c @@ -1002,7 +1002,7 @@ static int rradc_probe(struct platform_device *pdev) static const struct of_device_id rradc_match_table[] = { { .compatible = "qcom,pm660-rradc" }, { .compatible = "qcom,pmi8998-rradc" }, - {} + { } }; MODULE_DEVICE_TABLE(of, rradc_match_table); diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index bbe954a738c7..240cfa391674 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -331,7 +331,7 @@ static const struct of_device_id rockchip_saradc_match[] = { .compatible = "rockchip,rk3588-saradc", .data = &rk3588_saradc_data, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rockchip_saradc_match); @@ -370,7 +370,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) mutex_lock(&info->lock); - for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) { + iio_for_each_active_channel(i_dev, i) { const struct iio_chan_spec *chan = &i_dev->channels[i]; ret = rockchip_saradc_conversion(info, chan); diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index bcb129840908..56ed948a8ae1 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -643,7 +643,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) pm_runtime_get_sync(dev); - for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { unsigned int addr = rtq6056_channels[bit].address; ret = regmap_read(priv->regmap, addr, &raw); @@ -865,7 +865,7 @@ static const struct richtek_dev_data rtq6059_devdata = { static const struct of_device_id rtq6056_device_match[] = { { .compatible = "richtek,rtq6056", .data = &rtq6056_devdata }, { .compatible = "richtek,rtq6059", .data = &rtq6059_devdata }, - {} + { } }; MODULE_DEVICE_TABLE(of, rtq6056_device_match); diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c index 327cc2097f6c..654b6a38b650 100644 --- a/drivers/iio/adc/sd_adc_modulator.c +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -6,11 +6,14 @@ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>. */ +#include <linux/iio/backend.h> #include <linux/iio/iio.h> #include <linux/iio/triggered_buffer.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> static const struct iio_info iio_sd_mod_iio_info; @@ -24,7 +27,59 @@ static const struct iio_chan_spec iio_sd_mod_ch = { }, }; -static int iio_sd_mod_probe(struct platform_device *pdev) +struct iio_sd_backend_priv { + struct regulator *vref; + int vref_mv; +}; + +static int iio_sd_mod_enable(struct iio_backend *backend) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + if (priv->vref) + return regulator_enable(priv->vref); + + return 0; +}; + +static void iio_sd_mod_disable(struct iio_backend *backend) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + if (priv->vref) + regulator_disable(priv->vref); +}; + +static int iio_sd_mod_read(struct iio_backend *backend, struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *val = priv->vref_mv; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OFFSET: + *val = 0; + return IIO_VAL_INT; + } + + return -EOPNOTSUPP; +}; + +static const struct iio_backend_ops sd_backend_ops = { + .enable = iio_sd_mod_enable, + .disable = iio_sd_mod_disable, + .read_raw = iio_sd_mod_read, +}; + +static const struct iio_backend_info sd_backend_info = { + .name = "sd-modulator", + .ops = &sd_backend_ops, +}; + +static int iio_sd_mod_register(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct iio_dev *iio; @@ -45,6 +100,45 @@ static int iio_sd_mod_probe(struct platform_device *pdev) return devm_iio_device_register(&pdev->dev, iio); } +static int iio_sd_mod_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regulator *vref; + struct iio_sd_backend_priv *priv; + int ret; + + /* If sd modulator is not defined as an IIO backend device, fallback to legacy */ + if (!device_property_present(dev, "#io-backend-cells")) + return iio_sd_mod_register(pdev); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Get regulator reference if any, but don't enable regulator right now. + * Rely on enable and disable callbacks to manage regulator power. + */ + vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref)) { + if (PTR_ERR(vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref), "Failed to get vref\n"); + } else { + /* + * Retrieve voltage right now, as regulator_get_voltage() provides it whatever + * the state of the regulator. + */ + ret = regulator_get_voltage(vref); + if (ret < 0) + return ret; + + priv->vref = vref; + priv->vref_mv = ret / 1000; + } + + return devm_iio_backend_register(&pdev->dev, &sd_backend_info, priv); +}; + static const struct of_device_id sd_adc_of_match[] = { { .compatible = "sd-modulator" }, { .compatible = "ads1201" }, @@ -65,3 +159,4 @@ module_platform_driver(iio_sd_mod_adc); MODULE_DESCRIPTION("Basic sigma delta modulator"); MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/adc/sophgo-cv1800b-adc.c b/drivers/iio/adc/sophgo-cv1800b-adc.c new file mode 100644 index 000000000000..0951deb7b111 --- /dev/null +++ b/drivers/iio/adc/sophgo-cv1800b-adc.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sophgo CV1800B SARADC Driver + * + * Copyright (C) Bootlin 2024 + * Author: Thomas Bonnefille <thomas.bonnefille@bootlin.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +#define CV1800B_ADC_CTRL_REG 0x04 +#define CV1800B_ADC_EN BIT(0) +#define CV1800B_ADC_SEL(x) BIT((x) + 5) +#define CV1800B_ADC_STATUS_REG 0x08 +#define CV1800B_ADC_BUSY BIT(0) +#define CV1800B_ADC_CYC_SET_REG 0x0C +#define CV1800B_MASK_STARTUP_CYCLE GENMASK(4, 0) +#define CV1800B_MASK_SAMPLE_WINDOW GENMASK(11, 8) +#define CV1800B_MASK_CLKDIV GENMASK(15, 12) +#define CV1800B_MASK_COMPARE_CYCLE GENMASK(19, 16) +#define CV1800B_ADC_CH_RESULT_REG(x) (0x14 + 4 * (x)) +#define CV1800B_ADC_CH_RESULT GENMASK(11, 0) +#define CV1800B_ADC_CH_VALID BIT(15) +#define CV1800B_ADC_INTR_EN_REG 0x20 +#define CV1800B_ADC_INTR_CLR_REG 0x24 +#define CV1800B_ADC_INTR_CLR_BIT BIT(0) +#define CV1800B_ADC_INTR_STA_REG 0x28 +#define CV1800B_ADC_INTR_STA_BIT BIT(0) +#define CV1800B_READ_TIMEOUT_MS 1000 +#define CV1800B_READ_TIMEOUT_US (CV1800B_READ_TIMEOUT_MS * 1000) + +#define CV1800B_ADC_CHANNEL(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), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .scan_index = index, \ + } + +struct cv1800b_adc { + struct completion completion; + void __iomem *regs; + struct mutex lock; /* ADC Control and Result register */ + struct clk *clk; + int irq; +}; + +static const struct iio_chan_spec sophgo_channels[] = { + CV1800B_ADC_CHANNEL(0), + CV1800B_ADC_CHANNEL(1), + CV1800B_ADC_CHANNEL(2), +}; + +static void cv1800b_adc_start_measurement(struct cv1800b_adc *saradc, + int channel) +{ + writel(0, saradc->regs + CV1800B_ADC_CTRL_REG); + writel(CV1800B_ADC_SEL(channel) | CV1800B_ADC_EN, + saradc->regs + CV1800B_ADC_CTRL_REG); +} + +static int cv1800b_adc_wait(struct cv1800b_adc *saradc) +{ + if (saradc->irq < 0) { + u32 reg; + + return readl_poll_timeout(saradc->regs + CV1800B_ADC_STATUS_REG, + reg, !(reg & CV1800B_ADC_BUSY), + 500, CV1800B_READ_TIMEOUT_US); + } + + return wait_for_completion_timeout(&saradc->completion, + msecs_to_jiffies(CV1800B_READ_TIMEOUT_MS)) > 0 ? + 0 : -ETIMEDOUT; +} + +static int cv1800b_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cv1800b_adc *saradc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u32 sample; + + scoped_guard(mutex, &saradc->lock) { + int ret; + + cv1800b_adc_start_measurement(saradc, chan->scan_index); + ret = cv1800b_adc_wait(saradc); + if (ret < 0) + return ret; + + sample = readl(saradc->regs + CV1800B_ADC_CH_RESULT_REG(chan->scan_index)); + } + if (!(sample & CV1800B_ADC_CH_VALID)) + return -ENODATA; + + *val = sample & CV1800B_ADC_CH_RESULT; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 3300; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: { + u32 status_reg = readl(saradc->regs + CV1800B_ADC_CYC_SET_REG); + unsigned int clk_div = (1 + FIELD_GET(CV1800B_MASK_CLKDIV, status_reg)); + unsigned int freq = clk_get_rate(saradc->clk) / clk_div; + unsigned int nb_startup_cycle = 1 + FIELD_GET(CV1800B_MASK_STARTUP_CYCLE, status_reg); + unsigned int nb_sample_cycle = 1 + FIELD_GET(CV1800B_MASK_SAMPLE_WINDOW, status_reg); + unsigned int nb_compare_cycle = 1 + FIELD_GET(CV1800B_MASK_COMPARE_CYCLE, status_reg); + + *val = freq / (nb_startup_cycle + nb_sample_cycle + nb_compare_cycle); + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static irqreturn_t cv1800b_adc_interrupt_handler(int irq, void *private) +{ + struct cv1800b_adc *saradc = private; + u32 reg = readl(saradc->regs + CV1800B_ADC_INTR_STA_REG); + + if (!(FIELD_GET(CV1800B_ADC_INTR_STA_BIT, reg))) + return IRQ_NONE; + + writel(CV1800B_ADC_INTR_CLR_BIT, saradc->regs + CV1800B_ADC_INTR_CLR_REG); + complete(&saradc->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info cv1800b_adc_info = { + .read_raw = &cv1800b_adc_read_raw, +}; + +static int cv1800b_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cv1800b_adc *saradc; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*saradc)); + if (!indio_dev) + return -ENOMEM; + + saradc = iio_priv(indio_dev); + indio_dev->name = "sophgo-cv1800b-adc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &cv1800b_adc_info; + indio_dev->num_channels = ARRAY_SIZE(sophgo_channels); + indio_dev->channels = sophgo_channels; + + saradc->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(saradc->clk)) + return PTR_ERR(saradc->clk); + + saradc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(saradc->regs)) + return PTR_ERR(saradc->regs); + + saradc->irq = platform_get_irq_optional(pdev, 0); + if (saradc->irq > 0) { + init_completion(&saradc->completion); + ret = devm_request_irq(dev, saradc->irq, + cv1800b_adc_interrupt_handler, 0, + dev_name(dev), saradc); + if (ret) + return ret; + + writel(1, saradc->regs + CV1800B_ADC_INTR_EN_REG); + } + + ret = devm_mutex_init(dev, &saradc->lock); + if (ret) + return ret; + + writel(FIELD_PREP(CV1800B_MASK_STARTUP_CYCLE, 15) | + FIELD_PREP(CV1800B_MASK_SAMPLE_WINDOW, 15) | + FIELD_PREP(CV1800B_MASK_CLKDIV, 1) | + FIELD_PREP(CV1800B_MASK_COMPARE_CYCLE, 15), + saradc->regs + CV1800B_ADC_CYC_SET_REG); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id cv1800b_adc_match[] = { + { .compatible = "sophgo,cv1800b-saradc", }, + { } +}; +MODULE_DEVICE_TABLE(of, cv1800b_adc_match); + +static struct platform_driver cv1800b_adc_driver = { + .driver = { + .name = "sophgo-cv1800b-saradc", + .of_match_table = cv1800b_adc_match, + }, + .probe = cv1800b_adc_probe, +}; +module_platform_driver(cv1800b_adc_driver); + +MODULE_AUTHOR("Thomas Bonnefille <thomas.bonnefille@bootlin.com>"); +MODULE_DESCRIPTION("Sophgo CV1800B SARADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 375aa7720f80..32ca26ed59f7 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1261,7 +1261,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]); stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]); - for_each_set_bit(bit, scan_mask, indio_dev->masklength) { + for_each_set_bit(bit, scan_mask, iio_get_masklength(indio_dev)) { chan = indio_dev->channels + bit; /* * Assign one channel per SQ entry in regular @@ -1619,7 +1619,7 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, if (ret < 0) return ret; - adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength); + adc->num_conv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev)); ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask); pm_runtime_mark_last_busy(dev); @@ -2638,7 +2638,7 @@ static const struct of_device_id stm32_adc_of_match[] = { { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg }, { .compatible = "st,stm32mp13-adc", .data = (void *)&stm32mp13_adc_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(of, stm32_adc_of_match); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index fabd654245f5..2037f73426d4 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -9,6 +9,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/iio/adc/stm32-dfsdm-adc.h> +#include <linux/iio/backend.h> #include <linux/iio/buffer.h> #include <linux/iio/hw-consumer.h> #include <linux/iio/sysfs.h> @@ -78,6 +79,7 @@ struct stm32_dfsdm_adc { /* ADC specific */ unsigned int oversamp; struct iio_hw_consumer *hwc; + struct iio_backend **backend; struct completion completion; u32 *buffer; @@ -666,6 +668,74 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, return 0; } +static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm, + struct iio_dev *indio_dev, + struct iio_chan_spec *ch, + struct fwnode_handle *node) +{ + struct stm32_dfsdm_channel *df_ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct iio_backend *backend; + const char *of_str; + int ret, val; + + ret = fwnode_property_read_u32(node, "reg", &ch->channel); + if (ret < 0) { + dev_err(&indio_dev->dev, "Missing channel index %d\n", ret); + return ret; + } + + if (ch->channel >= dfsdm->num_chs) { + dev_err(&indio_dev->dev, " Error bad channel number %d (max = %d)\n", + ch->channel, dfsdm->num_chs); + return -EINVAL; + } + + ret = fwnode_property_read_string(node, "label", &ch->datasheet_name); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'label' for idx %d\n", ch->channel); + return ret; + } + + df_ch = &dfsdm->ch_list[ch->channel]; + df_ch->id = ch->channel; + + ret = fwnode_property_read_string(node, "st,adc-channel-type", &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->type = val; + + ret = fwnode_property_read_string(node, "st,adc-channel-clk-src", &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->src = val; + + ret = fwnode_property_read_u32(node, "st,adc-alt-channel", &df_ch->alt_si); + if (ret != -EINVAL) + df_ch->alt_si = 0; + + if (adc->dev_data->type == DFSDM_IIO) { + backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node); + if (IS_ERR(backend)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(backend), + "Failed to get backend\n"); + adc->backend[ch->scan_index] = backend; + } + + return 0; +} + static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, @@ -987,7 +1057,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev, { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength); + adc->nconv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev)); adc->smask = *scan_mask; dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask); @@ -998,6 +1068,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev, static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int i = 0; int ret; /* Reset adc buffer index */ @@ -1009,6 +1080,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) return ret; } + if (adc->backend) { + while (adc->backend[i]) { + ret = iio_backend_enable(adc->backend[i]); + if (ret < 0) + return ret; + i++; + } + } + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); if (ret < 0) goto err_stop_hwc; @@ -1041,6 +1121,7 @@ err_stop_hwc: static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int i = 0; stm32_dfsdm_stop_conv(indio_dev); @@ -1048,6 +1129,13 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) stm32_dfsdm_stop_dfsdm(adc->dfsdm); + if (adc->backend) { + while (adc->backend[i]) { + iio_backend_disable(adc->backend[i]); + i++; + } + } + if (adc->hwc) iio_hw_consumer_disable(adc->hwc); @@ -1220,14 +1308,25 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; + u32 max = flo->max << (flo->lshift - chan->scan_type.shift); + int idx = chan->scan_index; int ret; + if (flo->lshift < chan->scan_type.shift) + max = flo->max >> (chan->scan_type.shift - flo->lshift); + switch (mask) { case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - ret = iio_hw_consumer_enable(adc->hwc); + if (adc->hwc) + ret = iio_hw_consumer_enable(adc->hwc); + if (adc->backend) + ret = iio_backend_enable(adc->backend[idx]); if (ret < 0) { dev_err(&indio_dev->dev, "%s: IIO enable failed (channel %d)\n", @@ -1236,7 +1335,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, return ret; } ret = stm32_dfsdm_single_conv(indio_dev, chan, val); - iio_hw_consumer_disable(adc->hwc); + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + if (adc->backend) + iio_backend_disable(adc->backend[idx]); if (ret < 0) { dev_err(&indio_dev->dev, "%s: Conversion failed (channel %d)\n", @@ -1256,6 +1358,50 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, *val = adc->sample_freq; return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Scale is expressed in mV. + * When fast mode is disabled, actual resolution may be lower + * than 2^n, where n = realbits - 1. + * This leads to underestimating the input voltage. + * To compensate this deviation, the voltage reference can be + * corrected with a factor = realbits resolution / actual max + */ + if (adc->backend) { + ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL); + if (ret < 0) + return ret; + + *val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max); + *val2 = chan->scan_type.realbits; + if (chan->differential) + *val *= 2; + } + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + /* + * DFSDM output data are in the range [-2^n, 2^n], + * with n = realbits - 1. + * - Differential modulator: + * Offset correspond to SD modulator offset. + * - Single ended modulator: + * Input is in [0V, Vref] range, + * where 0V corresponds to -2^n, and Vref to 2^n. + * Add 2^n to offset. (i.e. middle of input range) + * offset = offset(sd) * vref / res(sd) * max / vref. + */ + if (adc->backend) { + ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL); + if (ret < 0) + return ret; + + *val = div_u64((u64)max * *val, BIT(*val2 - 1)); + if (!chan->differential) + *val += max; + } + return IIO_VAL_INT; } return -EINVAL; @@ -1320,7 +1466,7 @@ static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = { .read = dfsdm_adc_audio_get_spiclk, .write = dfsdm_adc_audio_set_spiclk, }, - {}, + { } }; static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev) @@ -1362,15 +1508,18 @@ static int stm32_dfsdm_dma_request(struct device *dev, return 0; } -static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, - struct iio_chan_spec *ch) +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *ch, + struct fwnode_handle *child) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); int ret; - ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch); + if (child) + ret = stm32_dfsdm_generic_channel_parse_of(adc->dfsdm, indio_dev, ch, child); + else /* Legacy binding */ + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch); if (ret < 0) - return ret; + return dev_err_probe(&indio_dev->dev, ret, "Failed to parse channel\n"); ch->type = IIO_VOLTAGE; ch->indexed = 1; @@ -1379,12 +1528,21 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, * IIO_CHAN_INFO_RAW: used to compute regular conversion * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling */ - ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + if (child) { + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET); + } else { + /* Legacy. Scaling not supported */ + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + } + ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | BIT(IIO_CHAN_INFO_SAMP_FREQ); if (adc->dev_data->type == DFSDM_AUDIO) { ch->ext_info = dfsdm_adc_audio_ext_info; + ch->scan_index = 0; } else { ch->scan_type.shift = 8; } @@ -1396,20 +1554,67 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, &adc->dfsdm->ch_list[ch->channel]); } +static int stm32_dfsdm_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels) +{ + int num_ch = indio_dev->num_channels; + int chan_idx = 0; + int ret; + + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { + channels[chan_idx].scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], NULL); + if (ret < 0) + return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n"); + } + + return 0; +} + +static int stm32_dfsdm_generic_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels) +{ + int chan_idx = 0, ret; + + device_for_each_child_node_scoped(&indio_dev->dev, child) { + /* Skip DAI node in DFSDM audio nodes */ + if (fwnode_property_present(child, "compatible")) + continue; + + channels[chan_idx].scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], child); + if (ret < 0) + return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n"); + + chan_idx++; + } + + return chan_idx; +} + static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev) { struct iio_chan_spec *ch; struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_channel *d_ch; - int ret; + bool legacy = false; + int num_ch, ret; + + /* If st,adc-channels is defined legacy binding is used. Else assume generic binding. */ + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels"); + if (num_ch == 1) + legacy = true; ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL); if (!ch) return -ENOMEM; - ch->scan_index = 0; + indio_dev->num_channels = 1; + indio_dev->channels = ch; + + if (legacy) + ret = stm32_dfsdm_chan_init(indio_dev, ch); + else + ret = stm32_dfsdm_generic_chan_init(indio_dev, ch); - ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch); if (ret < 0) { dev_err(&indio_dev->dev, "Channels init failed\n"); return ret; @@ -1420,9 +1625,6 @@ static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev) if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) adc->spi_freq = adc->dfsdm->spi_master_freq; - indio_dev->num_channels = 1; - indio_dev->channels = ch; - return stm32_dfsdm_dma_request(dev, indio_dev); } @@ -1430,43 +1632,61 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev) { struct iio_chan_spec *ch; struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - int num_ch; - int ret, chan_idx; + int num_ch, ret; + bool legacy = false; adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp); if (ret < 0) return ret; - num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, - "st,adc-channels"); - if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) { - dev_err(&indio_dev->dev, "Bad st,adc-channels\n"); - return num_ch < 0 ? num_ch : -EINVAL; + num_ch = device_get_child_node_count(&indio_dev->dev); + if (!num_ch) { + /* No channels nodes found. Assume legacy binding */ + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels"); + if (num_ch < 0) { + dev_err(&indio_dev->dev, "Bad st,adc-channels\n"); + return num_ch; + } + + legacy = true; } - /* Bind to SD modulator IIO device */ - adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev); - if (IS_ERR(adc->hwc)) - return -EPROBE_DEFER; + if (num_ch > adc->dfsdm->num_chs) { + dev_err(&indio_dev->dev, "Number of channel [%d] exceeds [%d]\n", + num_ch, adc->dfsdm->num_chs); + return -EINVAL; + } + indio_dev->num_channels = num_ch; - ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), - GFP_KERNEL); - if (!ch) - return -ENOMEM; + if (legacy) { + /* Bind to SD modulator IIO device. */ + adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev); + if (IS_ERR(adc->hwc)) + return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER, + "waiting for SD modulator\n"); + } else { + /* Generic binding. SD modulator IIO device not used. Use SD modulator backend. */ + adc->hwc = NULL; - for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { - ch[chan_idx].scan_index = chan_idx; - ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]); - if (ret < 0) { - dev_err(&indio_dev->dev, "Channels init failed\n"); - return ret; - } + adc->backend = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*adc->backend), + GFP_KERNEL); + if (!adc->backend) + return -ENOMEM; } - indio_dev->num_channels = num_ch; + ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; indio_dev->channels = ch; + if (legacy) + ret = stm32_dfsdm_chan_init(indio_dev, ch); + else + ret = stm32_dfsdm_generic_chan_init(indio_dev, ch); + if (ret < 0) + return ret; + init_completion(&adc->completion); /* Optionally request DMA */ @@ -1677,3 +1897,4 @@ module_platform_driver(stm32_dfsdm_adc_driver); MODULE_DESCRIPTION("STM32 sigma delta ADC"); MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index a05d978b8cb8..bef59fcc0d80 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -299,7 +299,7 @@ static const struct of_device_id stm32_dfsdm_of_match[] = { .compatible = "st,stm32mp1-dfsdm", .data = &stm32mp1_dfsdm_data, }, - {} + { } }; MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c index 8e56def1c9e5..b0add5a2eab5 100644 --- a/drivers/iio/adc/stmpe-adc.c +++ b/drivers/iio/adc/stmpe-adc.c @@ -347,7 +347,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume); static const struct of_device_id stmpe_adc_ids[] = { { .compatible = "st,stmpe-adc", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, stmpe_adc_ids); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index b11ce555ba3b..e2dbd070c7c4 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -211,8 +211,7 @@ static irqreturn_t adc0832_trigger_handler(int irq, void *p) mutex_lock(&adc->lock); - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; int ret = adc0832_adc_conversion(adc, scan_chan->channel, @@ -310,7 +309,7 @@ static const struct of_device_id adc0832_dt_ids[] = { { .compatible = "ti,adc0832", }, { .compatible = "ti,adc0834", }, { .compatible = "ti,adc0838", }, - {} + { } }; MODULE_DEVICE_TABLE(of, adc0832_dt_ids); @@ -319,7 +318,7 @@ static const struct spi_device_id adc0832_id[] = { { "adc0832", adc0832 }, { "adc0834", adc0834 }, { "adc0838", adc0838 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adc0832_id); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index 1f6e53832e06..bf98f9bf942a 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -166,8 +166,7 @@ static int adc084s021_buffer_preenable(struct iio_dev *indio_dev) int scan_index; int i = 0; - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *channel = &indio_dev->channels[scan_index]; adc->tx_buf[i++] = channel->channel << 3; @@ -243,13 +242,13 @@ static int adc084s021_probe(struct spi_device *spi) static const struct of_device_id adc084s021_of_match[] = { { .compatible = "ti,adc084s021", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, adc084s021_of_match); static const struct spi_device_id adc084s021_id[] = { { ADC084S021_DRIVER_NAME, 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adc084s021_id); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index c0a72d72f3a9..7f065f457b36 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -344,8 +344,7 @@ static irqreturn_t adc12138_trigger_handler(int irq, void *p) mutex_lock(&adc->lock); - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; @@ -520,7 +519,7 @@ static const struct of_device_id adc12138_dt_ids[] = { { .compatible = "ti,adc12130", }, { .compatible = "ti,adc12132", }, { .compatible = "ti,adc12138", }, - {} + { } }; MODULE_DEVICE_TABLE(of, adc12138_dt_ids); @@ -528,7 +527,7 @@ static const struct spi_device_id adc12138_id[] = { { "adc12130", adc12130 }, { "adc12132", adc12132 }, { "adc12138", adc12138 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adc12138_id); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index f7c78d0dd449..474e733fb8e0 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -226,14 +226,14 @@ static int ti_adc_probe(struct spi_device *spi) static const struct of_device_id ti_adc_dt_ids[] = { { .compatible = "ti,adc141s626", }, { .compatible = "ti,adc161s626", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static const struct spi_device_id ti_adc_id[] = { - {"adc141s626", TI_ADC141S626}, - {"adc161s626", TI_ADC161S626}, - {}, + { "adc141s626", TI_ADC141S626 }, + { "adc161s626", TI_ADC161S626 }, + { } }; MODULE_DEVICE_TABLE(spi, ti_adc_id); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index d3363d02f292..6d1bc9659946 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -456,7 +456,7 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) mutex_lock(&data->lock); chan = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ads1015_get_adc_result(data, chan, &res); if (ret < 0) { mutex_unlock(&data->lock); @@ -1173,7 +1173,7 @@ static const struct i2c_device_id ads1015_id[] = { { "ads1015", (kernel_ulong_t)&ads1015_data }, { "ads1115", (kernel_ulong_t)&ads1115_data }, { "tla2024", (kernel_ulong_t)&tla2024_data }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ads1015_id); @@ -1181,7 +1181,7 @@ static const struct of_device_id ads1015_of_match[] = { { .compatible = "ti,ads1015", .data = &ads1015_data }, { .compatible = "ti,ads1115", .data = &ads1115_data }, { .compatible = "ti,tla2024", .data = &tla2024_data }, - {} + { } }; MODULE_DEVICE_TABLE(of, ads1015_of_match); diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index d649980479e4..1c7606375149 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -435,7 +435,7 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev) int ret; index = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ads1119_set_conv_mode(st, true); if (ret) @@ -508,7 +508,7 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private) if (!iio_trigger_using_own(indio_dev)) { index = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ads1119_poll_data_ready(st, &indio_dev->channels[index]); if (ret) { diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 4ca62121f0d1..14941f384dad 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -279,8 +279,7 @@ static irqreturn_t ads124s_trigger_handler(int irq, void *p) int scan_index, j = 0; int ret; - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, scan_index); if (ret) @@ -358,7 +357,7 @@ MODULE_DEVICE_TABLE(spi, ads124s_id); static const struct of_device_id ads124s_of_table[] = { { .compatible = "ti,ads124s06" }, { .compatible = "ti,ads124s08" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ads124s_of_table); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 1d1eaba3d6d1..13cb32125eef 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -502,8 +502,7 @@ static void ads1298_rdata_complete(void *context) } /* Demux the channel data into our bounce buffer */ - for_each_set_bit(scan_index, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, scan_index) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; const u8 *data = priv->rx_buffer + scan_chan->address; diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 96dd9366f8ff..91a427eb0882 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -637,7 +637,7 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private) if (ret) goto out; - for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, chn) { src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; @@ -918,7 +918,7 @@ static const struct of_device_id ads131e08_of_match[] = { .data = &ads131e08_info_tbl[ads131e06], }, { .compatible = "ti,ads131e08", .data = &ads131e08_info_tbl[ads131e08], }, - {} + { } }; MODULE_DEVICE_TABLE(of, ads131e08_of_match); @@ -926,7 +926,7 @@ static const struct spi_device_id ads131e08_ids[] = { { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ads131e08_ids); diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c index 4da78302359b..66b54c0d75aa 100644 --- a/drivers/iio/adc/ti-ads7924.c +++ b/drivers/iio/adc/ti-ads7924.c @@ -448,13 +448,13 @@ static int ads7924_probe(struct i2c_client *client) static const struct i2c_device_id ads7924_id[] = { { "ads7924" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ads7924_id); static const struct of_device_id ads7924_of_match[] = { { .compatible = "ti,ads7924", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ads7924_of_match); diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 263fc3a1b87e..af28672aa803 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -705,7 +705,7 @@ static const struct of_device_id ads7950_of_table[] = { { .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] }, { .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] }, { .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c index bbd85cb47f81..3bec8a2e61ab 100644 --- a/drivers/iio/adc/ti-ads8344.c +++ b/drivers/iio/adc/ti-ads8344.c @@ -175,7 +175,7 @@ static int ads8344_probe(struct spi_device *spi) static const struct of_device_id ads8344_of_match[] = { { .compatible = "ti,ads8344", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ads8344_of_match); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index 7a79f0cebfbf..9b1814f1965a 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -384,9 +384,7 @@ static irqreturn_t ads8688_trigger_handler(int irq, void *p) u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8); int i, j = 0; - for (i = 0; i < indio_dev->masklength; i++) { - if (!test_bit(i, indio_dev->active_scan_mask)) - continue; + iio_for_each_active_channel(indio_dev, i) { buffer[j] = ads8688_read(indio_dev, i); j++; } @@ -454,9 +452,9 @@ static int ads8688_probe(struct spi_device *spi) } static const struct spi_device_id ads8688_id[] = { - {"ads8684", ID_ADS8684}, - {"ads8688", ID_ADS8688}, - {} + { "ads8684", ID_ADS8684 }, + { "ads8688", ID_ADS8688 }, + { } }; MODULE_DEVICE_TABLE(spi, ads8688_id); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 84ba5c4a0eea..169e3591320b 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -360,7 +360,7 @@ static int lmp92064_adc_probe(struct spi_device *spi) static const struct spi_device_id lmp92064_id_table[] = { { "lmp92064" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, lmp92064_id_table); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index 30f629a553a1..08de997584fd 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -237,14 +237,14 @@ static void tlc4541_remove(struct spi_device *spi) static const struct of_device_id tlc4541_dt_ids[] = { { .compatible = "ti,tlc3541", }, { .compatible = "ti,tlc4541", }, - {} + { } }; MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); static const struct spi_device_id tlc4541_id[] = { - {"tlc3541", TLC3541}, - {"tlc4541", TLC4541}, - {} + { "tlc3541", TLC3541 }, + { "tlc4541", TLC4541 }, + { } }; MODULE_DEVICE_TABLE(spi, tlc4541_id); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index edcef8f11522..311d97001249 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -6,6 +6,7 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/regulator/consumer.h> @@ -141,7 +142,7 @@ enum tsc2046_state { struct tsc2046_adc_priv { struct spi_device *spi; const struct tsc2046_adc_dcfg *dcfg; - struct regulator *vref_reg; + bool internal_vref; struct iio_trigger *trig; struct hrtimer trig_timer; @@ -257,7 +258,7 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx, case TI_TSC2046_ADDR_VBAT: case TI_TSC2046_ADDR_TEMP0: pd |= TI_TSC2046_SER; - if (!priv->vref_reg) + if (priv->internal_vref) pd |= TI_TSC2046_PD1_VREF_ON; } @@ -273,7 +274,6 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, u32 *effective_speed_hz) { struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; - struct tsc2046_adc_atom *rx_buf, *tx_buf; unsigned int val, val_normalized = 0; int ret, i, count_skip = 0, max_count; struct spi_transfer xfer; @@ -287,18 +287,20 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, max_count = 1; } - if (sizeof(*tx_buf) * max_count > PAGE_SIZE) + if (sizeof(struct tsc2046_adc_atom) * max_count > PAGE_SIZE) return -ENOSPC; - tx_buf = kcalloc(max_count, sizeof(*tx_buf), GFP_KERNEL); + struct tsc2046_adc_atom *tx_buf __free(kfree) = kcalloc(max_count, + sizeof(*tx_buf), + GFP_KERNEL); if (!tx_buf) return -ENOMEM; - rx_buf = kcalloc(max_count, sizeof(*rx_buf), GFP_KERNEL); - if (!rx_buf) { - ret = -ENOMEM; - goto free_tx; - } + struct tsc2046_adc_atom *rx_buf __free(kfree) = kcalloc(max_count, + sizeof(*rx_buf), + GFP_KERNEL); + if (!rx_buf) + return -ENOMEM; /* * Do not enable automatic power down on working samples. Otherwise the @@ -326,7 +328,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, if (ret) { dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n", ERR_PTR(ret)); - goto free_bufs; + return ret; } if (effective_speed_hz) @@ -337,14 +339,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, val_normalized += val; } - ret = DIV_ROUND_UP(val_normalized, max_count - count_skip); - -free_bufs: - kfree(rx_buf); -free_tx: - kfree(tx_buf); - - return ret; + return DIV_ROUND_UP(val_normalized, max_count - count_skip); } static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv, @@ -746,49 +741,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) } } -static void tsc2046_adc_regulator_disable(void *data) -{ - struct tsc2046_adc_priv *priv = data; - - regulator_disable(priv->vref_reg); -} - -static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv) -{ - struct device *dev = &priv->spi->dev; - int ret; - - priv->vref_reg = devm_regulator_get_optional(dev, "vref"); - if (IS_ERR(priv->vref_reg)) { - /* If regulator exists but can't be get, return an error */ - if (PTR_ERR(priv->vref_reg) != -ENODEV) - return PTR_ERR(priv->vref_reg); - priv->vref_reg = NULL; - } - if (!priv->vref_reg) { - /* Use internal reference */ - priv->vref_mv = TI_TSC2046_INT_VREF; - return 0; - } - - ret = regulator_enable(priv->vref_reg); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable, - priv); - if (ret) - return ret; - - ret = regulator_get_voltage(priv->vref_reg); - if (ret < 0) - return ret; - - priv->vref_mv = ret / MILLI; - - return 0; -} - static int tsc2046_adc_probe(struct spi_device *spi) { const struct tsc2046_adc_dcfg *dcfg; @@ -830,10 +782,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) indio_dev->num_channels = dcfg->num_channels; indio_dev->info = &tsc2046_adc_info; - ret = tsc2046_adc_configure_regulator(priv); - if (ret) + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) return ret; + priv->internal_vref = ret == -ENODEV; + priv->vref_mv = priv->internal_vref ? TI_TSC2046_INT_VREF : ret / MILLI; + tsc2046_adc_parse_fwnode(priv); ret = tsc2046_adc_setup_spi_msg(priv); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index e4548df3f8fb..5afd2feb8c3d 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -752,7 +752,7 @@ static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev) writel(val, info->regs + VF610_REG_ADC_GC); channel = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); val = VF610_ADC_ADCHC(channel); val |= VF610_ADC_AIEN; diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index f051358d6b50..ebc583b07e0c 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -1275,7 +1275,6 @@ static int ams_parse_firmware(struct iio_dev *indio_dev) struct ams *ams = iio_priv(indio_dev); struct iio_chan_spec *ams_channels, *dev_channels; struct device *dev = indio_dev->dev.parent; - struct fwnode_handle *child = NULL; struct fwnode_handle *fwnode = dev_fwnode(dev); size_t ams_size; int ret, ch_cnt = 0, i, rising_off, falling_off; @@ -1297,16 +1296,12 @@ static int ams_parse_firmware(struct iio_dev *indio_dev) num_channels += ret; } - fwnode_for_each_child_node(fwnode, child) { - if (fwnode_device_is_available(child)) { - ret = ams_init_module(indio_dev, child, ams_channels + num_channels); - if (ret < 0) { - fwnode_handle_put(child); - return ret; - } + device_for_each_child_node_scoped(dev, child) { + ret = ams_init_module(indio_dev, child, ams_channels + num_channels); + if (ret < 0) + return ret; - num_channels += ret; - } + num_channels += ret; } for (i = 0; i < num_channels; i++) { diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 564c0cad0fc7..cfbfcaefec0f 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -628,7 +628,7 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev, size_t n; void *data; - n = bitmap_weight(mask, indio_dev->masklength); + n = bitmap_weight(mask, iio_get_masklength(indio_dev)); data = devm_krealloc_array(indio_dev->dev.parent, xadc->data, n, sizeof(*xadc->data), GFP_KERNEL); @@ -681,8 +681,7 @@ static irqreturn_t xadc_trigger_handler(int irq, void *p) goto out; j = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { chan = xadc_scan_index_to_channel(i); xadc_read_adc_reg(xadc, chan, &xadc->data[j]); j++; diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 4c12b7a94af5..4befc9f55201 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -77,7 +77,7 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, } cb_buff->indio_dev = cb_buff->channels[0].indio_dev; - cb_buff->buffer.scan_mask = bitmap_zalloc(cb_buff->indio_dev->masklength, + cb_buff->buffer.scan_mask = bitmap_zalloc(iio_get_masklength(cb_buff->indio_dev), GFP_KERNEL); if (cb_buff->buffer.scan_mask == NULL) { ret = -ENOMEM; diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 647f417a045e..dbde1443d6ed 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -248,7 +248,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) iio_dma_buffer_queue_wake(queue); dma_fence_end_signalling(cookie); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_done, IIO_DMA_BUFFER); /** * iio_dma_buffer_block_list_abort() - Indicate that a list block has been @@ -287,7 +287,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, iio_dma_buffer_queue_wake(queue); dma_fence_end_signalling(cookie); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_list_abort, IIO_DMA_BUFFER); static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block) { @@ -420,7 +420,7 @@ out_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_request_update, IIO_DMA_BUFFER); static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue) { @@ -506,7 +506,7 @@ int iio_dma_buffer_enable(struct iio_buffer *buffer, return 0; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_enable); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enable, IIO_DMA_BUFFER); /** * iio_dma_buffer_disable() - Disable DMA buffer @@ -530,7 +530,7 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer, return 0; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_disable); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_disable, IIO_DMA_BUFFER); static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) @@ -636,7 +636,7 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, { return iio_dma_buffer_io(buffer, n, user_buffer, false); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_read); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_read, IIO_DMA_BUFFER); /** * iio_dma_buffer_write() - DMA buffer write callback @@ -653,7 +653,7 @@ int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n, return iio_dma_buffer_io(buffer, n, (__force __user char *)user_buffer, true); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_write); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_write, IIO_DMA_BUFFER); /** * iio_dma_buffer_usage() - DMA buffer data_available and @@ -696,7 +696,7 @@ size_t iio_dma_buffer_usage(struct iio_buffer *buf) return data_available; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_usage); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_usage, IIO_DMA_BUFFER); struct iio_dma_buffer_block * iio_dma_buffer_attach_dmabuf(struct iio_buffer *buffer, @@ -723,7 +723,7 @@ iio_dma_buffer_attach_dmabuf(struct iio_buffer *buffer, return block; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_attach_dmabuf); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_attach_dmabuf, IIO_DMA_BUFFER); void iio_dma_buffer_detach_dmabuf(struct iio_buffer *buffer, struct iio_dma_buffer_block *block) @@ -731,7 +731,7 @@ void iio_dma_buffer_detach_dmabuf(struct iio_buffer *buffer, block->state = IIO_BLOCK_STATE_DEAD; iio_buffer_block_put_atomic(block); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_detach_dmabuf); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_detach_dmabuf, IIO_DMA_BUFFER); static int iio_dma_can_enqueue_block(struct iio_dma_buffer_block *block) { @@ -784,7 +784,7 @@ out_end_signalling: return ret; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_enqueue_dmabuf); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enqueue_dmabuf, IIO_DMA_BUFFER); void iio_dma_buffer_lock_queue(struct iio_buffer *buffer) { @@ -792,7 +792,7 @@ void iio_dma_buffer_lock_queue(struct iio_buffer *buffer) mutex_lock(&queue->lock); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_lock_queue); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_lock_queue, IIO_DMA_BUFFER); void iio_dma_buffer_unlock_queue(struct iio_buffer *buffer) { @@ -800,7 +800,7 @@ void iio_dma_buffer_unlock_queue(struct iio_buffer *buffer) mutex_unlock(&queue->lock); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_unlock_queue); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_unlock_queue, IIO_DMA_BUFFER); /** * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback @@ -816,7 +816,7 @@ int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd) return 0; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_set_bytes_per_datum); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_bytes_per_datum, IIO_DMA_BUFFER); /** * iio_dma_buffer_set_length - DMA buffer set_length callback @@ -836,7 +836,7 @@ int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length) return 0; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_set_length); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_length, IIO_DMA_BUFFER); /** * iio_dma_buffer_init() - Initialize DMA buffer queue @@ -864,7 +864,7 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, return 0; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_init); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, IIO_DMA_BUFFER); /** * iio_dma_buffer_exit() - Cleanup DMA buffer queue @@ -882,7 +882,7 @@ void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue) mutex_unlock(&queue->lock); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_exit); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_exit, IIO_DMA_BUFFER); /** * iio_dma_buffer_release() - Release final buffer resources @@ -896,7 +896,7 @@ void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue) { mutex_destroy(&queue->lock); } -EXPORT_SYMBOL_GPL(iio_dma_buffer_release); +EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_release, IIO_DMA_BUFFER); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 426cc614587a..19af1caf14cd 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -350,3 +350,4 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_DMA_BUFFER); diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index fb58f599a80b..526b2a8d725d 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -52,6 +52,7 @@ static const struct iio_buffer_access_funcs iio_hw_buf_access = { static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) { + unsigned int mask_longs = BITS_TO_LONGS(iio_get_masklength(indio_dev)); struct hw_consumer_buffer *buf; list_for_each_entry(buf, &hwc->buffers, head) { @@ -59,8 +60,7 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( return buf; } - buf = kzalloc(struct_size(buf, scan_mask, BITS_TO_LONGS(indio_dev->masklength)), - GFP_KERNEL); + buf = kzalloc(struct_size(buf, scan_mask, mask_longs), GFP_KERNEL); if (!buf) return NULL; diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index f959252a4fe6..b2c547ac8d34 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -12,7 +12,7 @@ #define BME680_REG_TEMP_MSB 0x22 #define BME680_REG_PRESS_MSB 0x1F -#define BM6880_REG_HUMIDITY_MSB 0x25 +#define BME680_REG_HUMIDITY_MSB 0x25 #define BME680_REG_GAS_MSB 0x2A #define BME680_REG_GAS_R_LSB 0x2B #define BME680_GAS_STAB_BIT BIT(4) @@ -39,14 +39,12 @@ #define BME680_HUM_REG_SHIFT_VAL 4 #define BME680_BIT_H1_DATA_MASK GENMASK(3, 0) -#define BME680_REG_RES_HEAT_RANGE 0x02 #define BME680_RHRANGE_MASK GENMASK(5, 4) #define BME680_REG_RES_HEAT_VAL 0x00 -#define BME680_REG_RANGE_SW_ERR 0x04 #define BME680_RSERROR_MASK GENMASK(7, 4) #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 -#define BME680_ADC_GAS_RES_SHIFT 6 +#define BME680_ADC_GAS_RES GENMASK(15, 6) #define BME680_AMB_TEMP 25 #define BME680_REG_CTRL_GAS_1 0x71 @@ -58,33 +56,24 @@ #define BME680_GAS_MEAS_BIT BIT(6) #define BME680_MEAS_BIT BIT(5) +#define BME680_TEMP_NUM_BYTES 3 +#define BME680_PRESS_NUM_BYTES 3 +#define BME680_HUMID_NUM_BYTES 2 +#define BME680_GAS_NUM_BYTES 2 + +#define BME680_MEAS_TRIM_MASK GENMASK(24, 4) + +#define BME680_STARTUP_TIME_US 5000 + /* Calibration Parameters */ #define BME680_T2_LSB_REG 0x8A -#define BME680_T3_REG 0x8C -#define BME680_P1_LSB_REG 0x8E -#define BME680_P2_LSB_REG 0x90 -#define BME680_P3_REG 0x92 -#define BME680_P4_LSB_REG 0x94 -#define BME680_P5_LSB_REG 0x96 -#define BME680_P7_REG 0x98 -#define BME680_P6_REG 0x99 -#define BME680_P8_LSB_REG 0x9C -#define BME680_P9_LSB_REG 0x9E -#define BME680_P10_REG 0xA0 -#define BME680_H2_LSB_REG 0xE2 #define BME680_H2_MSB_REG 0xE1 -#define BME680_H1_MSB_REG 0xE3 -#define BME680_H1_LSB_REG 0xE2 -#define BME680_H3_REG 0xE4 -#define BME680_H4_REG 0xE5 -#define BME680_H5_REG 0xE6 -#define BME680_H6_REG 0xE7 -#define BME680_H7_REG 0xE8 -#define BME680_T1_LSB_REG 0xE9 -#define BME680_GH2_LSB_REG 0xEB -#define BME680_GH1_REG 0xED #define BME680_GH3_REG 0xEE +#define BME680_CALIB_RANGE_1_LEN 23 +#define BME680_CALIB_RANGE_2_LEN 14 +#define BME680_CALIB_RANGE_3_LEN 5 + extern const struct regmap_config bme680_regmap_config; int bme680_core_probe(struct device *dev, struct regmap *regmap, diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 500f56834b01..5d2e750ca2b9 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -8,18 +8,64 @@ * Datasheet: * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf */ -#include <linux/acpi.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> -#include <linux/module.h> #include <linux/log2.h> +#include <linux/module.h> #include <linux/regmap.h> + #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <asm/unaligned.h> + #include "bme680.h" +/* 1st set of calibration data */ +enum { + /* Temperature calib indexes */ + T2_LSB = 0, + T3 = 2, + /* Pressure calib indexes */ + P1_LSB = 4, + P2_LSB = 6, + P3 = 8, + P4_LSB = 10, + P5_LSB = 12, + P7 = 14, + P6 = 15, + P8_LSB = 18, + P9_LSB = 20, + P10 = 22, +}; + +/* 2nd set of calibration data */ +enum { + /* Humidity calib indexes */ + H2_MSB = 0, + H1_LSB = 1, + H3 = 3, + H4 = 4, + H5 = 5, + H6 = 6, + H7 = 7, + /* Stray T1 calib index */ + T1_LSB = 8, + /* Gas heater calib indexes */ + GH2_LSB = 10, + GH1 = 12, + GH3 = 13, +}; + +/* 3rd set of calibration data */ +enum { + RES_HEAT_VAL = 0, + RES_HEAT_RANGE = 2, + RANGE_SW_ERR = 4, +}; + struct bme680_calib { u16 par_t1; s16 par_t2; @@ -52,16 +98,21 @@ struct bme680_calib { struct bme680_data { struct regmap *regmap; struct bme680_calib bme680; + struct mutex lock; /* Protect multiple serial R/W ops to device. */ u8 oversampling_temp; u8 oversampling_press; u8 oversampling_humid; u16 heater_dur; u16 heater_temp; - /* - * Carryover value from temperature conversion, used in pressure - * and humidity compensation calculations. - */ - s32 t_fine; + + union { + u8 buf[3]; + unsigned int check; + __be16 be16; + u8 bme680_cal_buf_1[BME680_CALIB_RANGE_1_LEN]; + u8 bme680_cal_buf_2[BME680_CALIB_RANGE_2_LEN]; + u8 bme680_cal_buf_3[BME680_CALIB_RANGE_3_LEN]; + }; }; static const struct regmap_range bme680_volatile_ranges[] = { @@ -110,217 +161,98 @@ static int bme680_read_calib(struct bme680_data *data, struct bme680_calib *calib) { struct device *dev = regmap_get_device(data->regmap); - unsigned int tmp, tmp_msb, tmp_lsb; + unsigned int tmp_msb, tmp_lsb; int ret; - __le16 buf; - - /* Temperature related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_T1_LSB_REG\n"); - return ret; - } - calib->par_t1 = le16_to_cpu(buf); ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, - &buf, sizeof(buf)); + data->bme680_cal_buf_1, + sizeof(data->bme680_cal_buf_1)); if (ret < 0) { - dev_err(dev, "failed to read BME680_T2_LSB_REG\n"); + dev_err(dev, "failed to read 1st set of calib data;\n"); return ret; } - calib->par_t2 = le16_to_cpu(buf); - ret = regmap_read(data->regmap, BME680_T3_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_T3_REG\n"); - return ret; - } - calib->par_t3 = tmp; - - /* Pressure related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P1_LSB_REG\n"); - return ret; - } - calib->par_p1 = le16_to_cpu(buf); - - ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P2_LSB_REG\n"); - return ret; - } - calib->par_p2 = le16_to_cpu(buf); - - ret = regmap_read(data->regmap, BME680_P3_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P3_REG\n"); - return ret; - } - calib->par_p3 = tmp; - - ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P4_LSB_REG\n"); - return ret; - } - calib->par_p4 = le16_to_cpu(buf); + calib->par_t2 = get_unaligned_le16(&data->bme680_cal_buf_1[T2_LSB]); + calib->par_t3 = data->bme680_cal_buf_1[T3]; + calib->par_p1 = get_unaligned_le16(&data->bme680_cal_buf_1[P1_LSB]); + calib->par_p2 = get_unaligned_le16(&data->bme680_cal_buf_1[P2_LSB]); + calib->par_p3 = data->bme680_cal_buf_1[P3]; + calib->par_p4 = get_unaligned_le16(&data->bme680_cal_buf_1[P4_LSB]); + calib->par_p5 = get_unaligned_le16(&data->bme680_cal_buf_1[P5_LSB]); + calib->par_p7 = data->bme680_cal_buf_1[P7]; + calib->par_p6 = data->bme680_cal_buf_1[P6]; + calib->par_p8 = get_unaligned_le16(&data->bme680_cal_buf_1[P8_LSB]); + calib->par_p9 = get_unaligned_le16(&data->bme680_cal_buf_1[P9_LSB]); + calib->par_p10 = data->bme680_cal_buf_1[P10]; - ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, - &buf, sizeof(buf)); + ret = regmap_bulk_read(data->regmap, BME680_H2_MSB_REG, + data->bme680_cal_buf_2, + sizeof(data->bme680_cal_buf_2)); if (ret < 0) { - dev_err(dev, "failed to read BME680_P5_LSB_REG\n"); + dev_err(dev, "failed to read 2nd set of calib data;\n"); return ret; } - calib->par_p5 = le16_to_cpu(buf); - ret = regmap_read(data->regmap, BME680_P6_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P6_REG\n"); - return ret; - } - calib->par_p6 = tmp; - - ret = regmap_read(data->regmap, BME680_P7_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P7_REG\n"); - return ret; - } - calib->par_p7 = tmp; - - ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P8_LSB_REG\n"); - return ret; - } - calib->par_p8 = le16_to_cpu(buf); - - ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P9_LSB_REG\n"); - return ret; - } - calib->par_p9 = le16_to_cpu(buf); - - ret = regmap_read(data->regmap, BME680_P10_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_P10_REG\n"); - return ret; - } - calib->par_p10 = tmp; - - /* Humidity related coefficients */ - ret = regmap_read(data->regmap, BME680_H1_MSB_REG, &tmp_msb); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H1_MSB_REG\n"); - return ret; - } - ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H1_LSB_REG\n"); - return ret; - } + tmp_lsb = data->bme680_cal_buf_2[H1_LSB]; + tmp_msb = data->bme680_cal_buf_2[H1_LSB + 1]; calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | (tmp_lsb & BME680_BIT_H1_DATA_MASK); - ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H2_MSB_REG\n"); - return ret; - } - ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H2_LSB_REG\n"); - return ret; - } + tmp_msb = data->bme680_cal_buf_2[H2_MSB]; + tmp_lsb = data->bme680_cal_buf_2[H2_MSB + 1]; calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL); - ret = regmap_read(data->regmap, BME680_H3_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H3_REG\n"); - return ret; - } - calib->par_h3 = tmp; - - ret = regmap_read(data->regmap, BME680_H4_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H4_REG\n"); - return ret; - } - calib->par_h4 = tmp; + calib->par_h3 = data->bme680_cal_buf_2[H3]; + calib->par_h4 = data->bme680_cal_buf_2[H4]; + calib->par_h5 = data->bme680_cal_buf_2[H5]; + calib->par_h6 = data->bme680_cal_buf_2[H6]; + calib->par_h7 = data->bme680_cal_buf_2[H7]; + calib->par_t1 = get_unaligned_le16(&data->bme680_cal_buf_2[T1_LSB]); + calib->par_gh2 = get_unaligned_le16(&data->bme680_cal_buf_2[GH2_LSB]); + calib->par_gh1 = data->bme680_cal_buf_2[GH1]; + calib->par_gh3 = data->bme680_cal_buf_2[GH3]; - ret = regmap_read(data->regmap, BME680_H5_REG, &tmp); + ret = regmap_bulk_read(data->regmap, BME680_REG_RES_HEAT_VAL, + data->bme680_cal_buf_3, + sizeof(data->bme680_cal_buf_3)); if (ret < 0) { - dev_err(dev, "failed to read BME680_H5_REG\n"); + dev_err(dev, "failed to read 3rd set of calib data;\n"); return ret; } - calib->par_h5 = tmp; - ret = regmap_read(data->regmap, BME680_H6_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H6_REG\n"); - return ret; - } - calib->par_h6 = tmp; + calib->res_heat_val = data->bme680_cal_buf_3[RES_HEAT_VAL]; - ret = regmap_read(data->regmap, BME680_H7_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_H7_REG\n"); - return ret; - } - calib->par_h7 = tmp; + calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, + data->bme680_cal_buf_3[RES_HEAT_RANGE]); - /* Gas heater related coefficients */ - ret = regmap_read(data->regmap, BME680_GH1_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_GH1_REG\n"); - return ret; - } - calib->par_gh1 = tmp; + calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, + data->bme680_cal_buf_3[RANGE_SW_ERR]); - ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, - &buf, sizeof(buf)); - if (ret < 0) { - dev_err(dev, "failed to read BME680_GH2_LSB_REG\n"); - return ret; - } - calib->par_gh2 = le16_to_cpu(buf); - - ret = regmap_read(data->regmap, BME680_GH3_REG, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read BME680_GH3_REG\n"); - return ret; - } - calib->par_gh3 = tmp; + return 0; +} - /* Other coefficients */ - ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_RANGE, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read resistance heat range\n"); - return ret; - } - calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp); +static int bme680_read_temp_adc(struct bme680_data *data, u32 *adc_temp) +{ + struct device *dev = regmap_get_device(data->regmap); + u32 value_temp; + int ret; - ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp); + ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB, + data->buf, BME680_TEMP_NUM_BYTES); if (ret < 0) { - dev_err(dev, "failed to read resistance heat value\n"); + dev_err(dev, "failed to read temperature\n"); return ret; } - calib->res_heat_val = tmp; - ret = regmap_read(data->regmap, BME680_REG_RANGE_SW_ERR, &tmp); - if (ret < 0) { - dev_err(dev, "failed to read range software error\n"); - return ret; + value_temp = FIELD_GET(BME680_MEAS_TRIM_MASK, + get_unaligned_be24(data->buf)); + if (value_temp == BME680_MEAS_SKIPPED) { + /* reading was skipped */ + dev_err(dev, "reading temperature skipped\n"); + return -EINVAL; } - calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp); + *adc_temp = value_temp; return 0; } @@ -332,25 +264,65 @@ static int bme680_read_calib(struct bme680_data *data, * Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore, * output value of "3233" represents 32.33 DegC. */ -static s16 bme680_compensate_temp(struct bme680_data *data, - s32 adc_temp) +static s32 bme680_calc_t_fine(struct bme680_data *data, u32 adc_temp) { struct bme680_calib *calib = &data->bme680; s64 var1, var2, var3; - s16 calc_temp; /* If the calibration is invalid, attempt to reload it */ if (!calib->par_t2) bme680_read_calib(data, calib); - var1 = (adc_temp >> 3) - ((s32)calib->par_t1 << 1); + var1 = ((s32)adc_temp >> 3) - ((s32)calib->par_t1 << 1); var2 = (var1 * calib->par_t2) >> 11; var3 = ((var1 >> 1) * (var1 >> 1)) >> 12; var3 = (var3 * ((s32)calib->par_t3 << 4)) >> 14; - data->t_fine = var2 + var3; - calc_temp = (data->t_fine * 5 + 128) >> 8; + return var2 + var3; /* t_fine = var2 + var3 */ +} - return calc_temp; +static int bme680_get_t_fine(struct bme680_data *data, s32 *t_fine) +{ + u32 adc_temp; + int ret; + + ret = bme680_read_temp_adc(data, &adc_temp); + if (ret) + return ret; + + *t_fine = bme680_calc_t_fine(data, adc_temp); + + return 0; +} + +static s16 bme680_compensate_temp(struct bme680_data *data, + u32 adc_temp) +{ + return (bme680_calc_t_fine(data, adc_temp) * 5 + 128) / 256; +} + +static int bme680_read_press_adc(struct bme680_data *data, u32 *adc_press) +{ + struct device *dev = regmap_get_device(data->regmap); + u32 value_press; + int ret; + + ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB, + data->buf, BME680_PRESS_NUM_BYTES); + if (ret < 0) { + dev_err(dev, "failed to read pressure\n"); + return ret; + } + + value_press = FIELD_GET(BME680_MEAS_TRIM_MASK, + get_unaligned_be24(data->buf)); + if (value_press == BME680_MEAS_SKIPPED) { + /* reading was skipped */ + dev_err(dev, "reading pressure skipped\n"); + return -EINVAL; + } + *adc_press = value_press; + + return 0; } /* @@ -361,12 +333,12 @@ static s16 bme680_compensate_temp(struct bme680_data *data, * 97356 Pa = 973.56 hPa. */ static u32 bme680_compensate_press(struct bme680_data *data, - u32 adc_press) + u32 adc_press, s32 t_fine) { struct bme680_calib *calib = &data->bme680; s32 var1, var2, var3, press_comp; - var1 = (data->t_fine >> 1) - 64000; + var1 = (t_fine >> 1) - 64000; var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2; var2 = var2 + (var1 * calib->par_p5 << 1); var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16); @@ -394,6 +366,30 @@ static u32 bme680_compensate_press(struct bme680_data *data, return press_comp; } +static int bme680_read_humid_adc(struct bme680_data *data, u32 *adc_humidity) +{ + struct device *dev = regmap_get_device(data->regmap); + u32 value_humidity; + int ret; + + ret = regmap_bulk_read(data->regmap, BME680_REG_HUMIDITY_MSB, + &data->be16, BME680_HUMID_NUM_BYTES); + if (ret < 0) { + dev_err(dev, "failed to read humidity\n"); + return ret; + } + + value_humidity = be16_to_cpu(data->be16); + if (value_humidity == BME680_MEAS_SKIPPED) { + /* reading was skipped */ + dev_err(dev, "reading humidity skipped\n"); + return -EINVAL; + } + *adc_humidity = value_humidity; + + return 0; +} + /* * Taken from Bosch BME680 API: * https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937 @@ -402,15 +398,15 @@ static u32 bme680_compensate_press(struct bme680_data *data, * value of "43215" represents 43.215 %rH. */ static u32 bme680_compensate_humid(struct bme680_data *data, - u16 adc_humid) + u16 adc_humid, s32 t_fine) { struct bme680_calib *calib = &data->bme680; s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum; - temp_scaled = (data->t_fine * 5 + 128) >> 8; - var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) - - (((temp_scaled * (s32) calib->par_h3) / 100) >> 1); - var2 = ((s32) calib->par_h2 * + temp_scaled = (t_fine * 5 + 128) >> 8; + var1 = (adc_humid - (((s32)calib->par_h1 * 16))) - + (((temp_scaled * calib->par_h3) / 100) >> 1); + var2 = (calib->par_h2 * (((temp_scaled * calib->par_h4) / 100) + (((temp_scaled * ((temp_scaled * calib->par_h5) / 100)) >> 6) / 100) + (1 << 14))) >> 10; @@ -442,7 +438,7 @@ static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc, u32 calc_gas_res; /* Look up table for the possible gas range values */ - const u32 lookupTable[16] = {2147483647u, 2147483647u, + static const u32 lookupTable[16] = {2147483647u, 2147483647u, 2147483647u, 2147483647u, 2147483647u, 2126008810u, 2147483647u, 2130303777u, 2147483647u, 2147483647u, 2143188679u, @@ -540,7 +536,6 @@ static u8 bme680_oversampling_to_reg(u8 val) static int bme680_wait_for_eoc(struct bme680_data *data) { struct device *dev = regmap_get_device(data->regmap); - unsigned int check; int ret; /* * (Sum of oversampling ratios * time per oversampling) + @@ -553,16 +548,16 @@ static int bme680_wait_for_eoc(struct bme680_data *data) usleep_range(wait_eoc_us, wait_eoc_us + 100); - ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check); + ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &data->check); if (ret) { dev_err(dev, "failed to read measurement status register.\n"); return ret; } - if (check & BME680_MEAS_BIT) { + if (data->check & BME680_MEAS_BIT) { dev_err(dev, "Device measurement cycle incomplete.\n"); return -EBUSY; } - if (!(check & BME680_NEW_DATA_BIT)) { + if (!(data->check & BME680_NEW_DATA_BIT)) { dev_err(dev, "No new data available from the device.\n"); return -ENODATA; } @@ -606,10 +601,12 @@ static int bme680_chip_config(struct bme680_data *data) ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK, osrs); - if (ret < 0) + if (ret < 0) { dev_err(dev, "failed to write ctrl_meas register\n"); + return ret; + } - return ret; + return 0; } static int bme680_gas_config(struct bme680_data *data) @@ -618,6 +615,11 @@ static int bme680_gas_config(struct bme680_data *data) int ret; u8 heatr_res, heatr_dur; + /* Go to sleep */ + ret = bme680_set_mode(data, false); + if (ret < 0) + return ret; + heatr_res = bme680_calc_heater_res(data, data->heater_temp); /* set target heater temperature */ @@ -649,77 +651,35 @@ static int bme680_gas_config(struct bme680_data *data) static int bme680_read_temp(struct bme680_data *data, int *val) { - struct device *dev = regmap_get_device(data->regmap); int ret; - __be32 tmp = 0; - s32 adc_temp; + u32 adc_temp; s16 comp_temp; - /* set forced mode to trigger measurement */ - ret = bme680_set_mode(data, true); - if (ret < 0) - return ret; - - ret = bme680_wait_for_eoc(data); + ret = bme680_read_temp_adc(data, &adc_temp); if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB, - &tmp, 3); - if (ret < 0) { - dev_err(dev, "failed to read temperature\n"); - return ret; - } - - adc_temp = be32_to_cpu(tmp) >> 12; - if (adc_temp == BME680_MEAS_SKIPPED) { - /* reading was skipped */ - dev_err(dev, "reading temperature skipped\n"); - return -EINVAL; - } comp_temp = bme680_compensate_temp(data, adc_temp); - /* - * val might be NULL if we're called by the read_press/read_humid - * routine which is called to get t_fine value used in - * compensate_press/compensate_humid to get compensated - * pressure/humidity readings. - */ - if (val) { - *val = comp_temp * 10; /* Centidegrees to millidegrees */ - return IIO_VAL_INT; - } - - return ret; + *val = comp_temp * 10; /* Centidegrees to millidegrees */ + return IIO_VAL_INT; } static int bme680_read_press(struct bme680_data *data, int *val, int *val2) { - struct device *dev = regmap_get_device(data->regmap); int ret; - __be32 tmp = 0; - s32 adc_press; + u32 adc_press; + s32 t_fine; - /* Read and compensate temperature to get a reading of t_fine */ - ret = bme680_read_temp(data, NULL); - if (ret < 0) + ret = bme680_get_t_fine(data, &t_fine); + if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB, - &tmp, 3); - if (ret < 0) { - dev_err(dev, "failed to read pressure\n"); + ret = bme680_read_press_adc(data, &adc_press); + if (ret) return ret; - } - - adc_press = be32_to_cpu(tmp) >> 12; - if (adc_press == BME680_MEAS_SKIPPED) { - /* reading was skipped */ - dev_err(dev, "reading pressure skipped\n"); - return -EINVAL; - } - *val = bme680_compensate_press(data, adc_press); + *val = bme680_compensate_press(data, adc_press, t_fine); *val2 = 1000; return IIO_VAL_FRACTIONAL; } @@ -727,31 +687,19 @@ static int bme680_read_press(struct bme680_data *data, static int bme680_read_humid(struct bme680_data *data, int *val, int *val2) { - struct device *dev = regmap_get_device(data->regmap); int ret; - __be16 tmp = 0; - s32 adc_humidity; - u32 comp_humidity; + u32 adc_humidity, comp_humidity; + s32 t_fine; - /* Read and compensate temperature to get a reading of t_fine */ - ret = bme680_read_temp(data, NULL); - if (ret < 0) + ret = bme680_get_t_fine(data, &t_fine); + if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB, - &tmp, sizeof(tmp)); - if (ret < 0) { - dev_err(dev, "failed to read humidity\n"); + ret = bme680_read_humid_adc(data, &adc_humidity); + if (ret) return ret; - } - adc_humidity = be16_to_cpu(tmp); - if (adc_humidity == BME680_MEAS_SKIPPED) { - /* reading was skipped */ - dev_err(dev, "reading humidity skipped\n"); - return -EINVAL; - } - comp_humidity = bme680_compensate_humid(data, adc_humidity); + comp_humidity = bme680_compensate_humid(data, adc_humidity, t_fine); *val = comp_humidity; *val2 = 1000; @@ -763,59 +711,37 @@ static int bme680_read_gas(struct bme680_data *data, { struct device *dev = regmap_get_device(data->regmap); int ret; - __be16 tmp = 0; - unsigned int check; - u16 adc_gas_res; + u16 adc_gas_res, gas_regs_val; u8 gas_range; - /* Set heater settings */ - ret = bme680_gas_config(data); - if (ret < 0) { - dev_err(dev, "failed to set gas config\n"); - return ret; - } - - /* set forced mode to trigger measurement */ - ret = bme680_set_mode(data, true); - if (ret < 0) - return ret; - - ret = bme680_wait_for_eoc(data); - if (ret) - return ret; - - ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check); - if (check & BME680_GAS_MEAS_BIT) { + ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &data->check); + if (data->check & BME680_GAS_MEAS_BIT) { dev_err(dev, "gas measurement incomplete\n"); return -EBUSY; } - ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check); + ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB, + &data->be16, BME680_GAS_NUM_BYTES); if (ret < 0) { - dev_err(dev, "failed to read gas_r_lsb register\n"); + dev_err(dev, "failed to read gas resistance\n"); return ret; } + gas_regs_val = be16_to_cpu(data->be16); + adc_gas_res = FIELD_GET(BME680_ADC_GAS_RES, gas_regs_val); + /* * occurs if either the gas heating duration was insuffient * to reach the target heater temperature or the target * heater temperature was too high for the heater sink to * reach. */ - if ((check & BME680_GAS_STAB_BIT) == 0) { + if ((gas_regs_val & BME680_GAS_STAB_BIT) == 0) { dev_err(dev, "heater failed to reach the target temperature\n"); return -EINVAL; } - ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB, - &tmp, sizeof(tmp)); - if (ret < 0) { - dev_err(dev, "failed to read gas resistance\n"); - return ret; - } - - gas_range = check & BME680_GAS_RANGE_MASK; - adc_gas_res = be16_to_cpu(tmp) >> BME680_ADC_GAS_RES_SHIFT; + gas_range = FIELD_GET(BME680_GAS_RANGE_MASK, gas_regs_val); *val = bme680_compensate_gas(data, adc_gas_res, gas_range); return IIO_VAL_INT; @@ -826,6 +752,18 @@ static int bme680_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); + int ret; + + guard(mutex)(&data->lock); + + /* set forced mode to trigger measurement */ + ret = bme680_set_mode(data, true); + if (ret < 0) + return ret; + + ret = bme680_wait_for_eoc(data); + if (ret) + return ret; switch (mask) { case IIO_CHAN_INFO_PROCESSED: @@ -871,6 +809,8 @@ static int bme680_write_raw(struct iio_dev *indio_dev, { struct bme680_data *data = iio_priv(indio_dev); + guard(mutex)(&data->lock); + if (val2 != 0) return -EINVAL; @@ -921,52 +861,19 @@ static const struct iio_info bme680_info = { .attrs = &bme680_attribute_group, }; -static const char *bme680_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name) { struct iio_dev *indio_dev; struct bme680_data *data; - unsigned int val; int ret; - ret = regmap_write(regmap, BME680_REG_SOFT_RESET, - BME680_CMD_SOFTRESET); - if (ret < 0) { - dev_err(dev, "Failed to reset chip\n"); - return ret; - } - - ret = regmap_read(regmap, BME680_REG_CHIP_ID, &val); - if (ret < 0) { - dev_err(dev, "Error reading chip ID\n"); - return ret; - } - - if (val != BME680_CHIP_ID_VAL) { - dev_err(dev, "Wrong chip ID, got %x expected %x\n", - val, BME680_CHIP_ID_VAL); - return -ENODEV; - } - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; - if (!name && ACPI_HANDLE(dev)) - name = bme680_match_acpi_device(dev); - data = iio_priv(indio_dev); + mutex_init(&data->lock); dev_set_drvdata(dev, indio_dev); data->regmap = regmap; indio_dev->name = name; @@ -982,25 +889,39 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, data->heater_temp = 320; /* degree Celsius */ data->heater_dur = 150; /* milliseconds */ - ret = bme680_chip_config(data); - if (ret < 0) { - dev_err(dev, "failed to set chip_config data\n"); - return ret; - } + ret = regmap_write(regmap, BME680_REG_SOFT_RESET, + BME680_CMD_SOFTRESET); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to reset chip\n"); - ret = bme680_gas_config(data); - if (ret < 0) { - dev_err(dev, "failed to set gas config data\n"); - return ret; + usleep_range(BME680_STARTUP_TIME_US, BME680_STARTUP_TIME_US + 1000); + + ret = regmap_read(regmap, BME680_REG_CHIP_ID, &data->check); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading chip ID\n"); + + if (data->check != BME680_CHIP_ID_VAL) { + dev_err(dev, "Wrong chip ID, got %x expected %x\n", + data->check, BME680_CHIP_ID_VAL); + return -ENODEV; } ret = bme680_read_calib(data, &data->bme680); if (ret < 0) { - dev_err(dev, + return dev_err_probe(dev, ret, "failed to read calibration coefficients at probe\n"); - return ret; } + ret = bme680_chip_config(data); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set chip_config data\n"); + + ret = bme680_gas_config(data); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set gas config data\n"); + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(bme680_core_probe, IIO_BME680); diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 4404d42ae5ec..7c54bd17d4b0 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -100,7 +100,7 @@ static int bme680_regmap_spi_read(void *context, const void *reg, return spi_write_then_read(spi, &addr, 1, val, val_size); } -static struct regmap_bus bme680_regmap_bus = { +static const struct regmap_bus bme680_regmap_bus = { .write = bme680_regmap_spi_write, .read = bme680_regmap_spi_read, .reg_format_endian_default = REGMAP_ENDIAN_BIG, diff --git a/drivers/iio/chemical/sgp40.c b/drivers/iio/chemical/sgp40.c index 7f0de14a1956..07d8ab830211 100644 --- a/drivers/iio/chemical/sgp40.c +++ b/drivers/iio/chemical/sgp40.c @@ -14,11 +14,16 @@ * 1) read raw logarithmic resistance value from sensor * --> useful to pass it to the algorithm of the sensor vendor for * measuring deteriorations and improvements of air quality. + * It can be read from the attribute in_resistance_raw. * - * 2) calculate an estimated absolute voc index (0 - 500 index points) for - * measuring the air quality. + * 2) calculate an estimated absolute voc index (in_concentration_input) + * with 0 - 500 index points) for measuring the air quality. * For this purpose the value of the resistance for which the voc index - * will be 250 can be set up using calibbias. + * will be 250 can be set up using in_resistance_calibbias (default 30000). + * + * The voc index is calculated as: + * x = (in_resistance_raw - in_resistance_calibbias) * 0.65 + * in_concentration_input = 500 / (1 + e^x) * * Compensation values of relative humidity and temperature can be set up * by writing to the out values of temp and humidityrelative. 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 6bfe5d6847e7..9fc71a73caa1 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 @@ -198,9 +198,7 @@ int cros_ec_sensors_push_data(struct iio_dev *indio_dev, return 0; out = (s16 *)st->samples; - for_each_set_bit(i, - indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { *out = data[i]; out++; } @@ -587,7 +585,7 @@ static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev, int ret; /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */ - for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) { ret = cros_ec_sensors_cmd_read_u16(ec, cros_ec_sensors_idx_to_reg(st, i), data); @@ -683,7 +681,7 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, return ret; } - for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) { *data = st->resp->data.data[i]; data++; } diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 7190eaede7fb..ed15dcbf4cf6 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -158,7 +158,7 @@ static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) * To calculate the multiplier,we convert the sf into char string and * count the number of characters */ - sf = (u64)uHz * 0xFFFF; + sf = uHz * 0xFFFF; do_div(sf, MICROHZ_PER_HZ); mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index a2596c2d3de3..1cfd7e2a622f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -371,6 +371,17 @@ config LTC2632 To compile this driver as a module, choose M here: the module will be called ltc2632. +config LTC2664 + tristate "Analog Devices LTC2664 and LTC2672 DAC SPI driver" + depends on SPI + select REGMAP + help + Say yes here to build support for Analog Devices + LTC2664 and LTC2672 converters (DAC). + + To compile this driver as a module, choose M here: the + module will be called ltc2664. + config M62332 tristate "Mitsubishi M62332 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 8432a81a19dc..2cf148f16306 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_DS4424) += ds4424.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_LTC1660) += ltc1660.o obj-$(CONFIG_LTC2632) += ltc2632.o +obj-$(CONFIG_LTC2664) += ltc2664.o obj-$(CONFIG_LTC2688) += ltc2688.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index 4572d6f49275..953fcfa2110b 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -20,8 +20,6 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/platform_data/ad5449.h> - #define AD5449_MAX_CHANNELS 2 #define AD5449_MAX_VREFS 2 @@ -268,7 +266,6 @@ static const char *ad5449_vref_name(struct ad5449 *st, int n) static int ad5449_spi_probe(struct spi_device *spi) { - struct ad5449_platform_data *pdata = spi->dev.platform_data; const struct spi_device_id *id = spi_get_device_id(spi); struct iio_dev *indio_dev; struct ad5449 *st; @@ -306,16 +303,8 @@ static int ad5449_spi_probe(struct spi_device *spi) mutex_init(&st->lock); if (st->chip_info->has_ctrl) { - unsigned int ctrl = 0x00; - if (pdata) { - if (pdata->hardware_clear_to_midscale) - ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE; - ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET; - st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED; - } else { - st->has_sdo = true; - } - ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl); + st->has_sdo = true; + ad5449_write(indio_dev, AD5449_CMD_CTRL, 0x0); } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c index f56eabe53723..615d1a196db3 100644 --- a/drivers/iio/dac/ad9739a.c +++ b/drivers/iio/dac/ad9739a.c @@ -145,7 +145,7 @@ static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev) struct ad9739a_state *st = iio_priv(indio_dev); return iio_backend_data_source_set(st->back, 0, - IIO_BACKEND_INTERNAL_CONTINUOS_WAVE); + IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE); } static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg) @@ -413,8 +413,7 @@ static int ad9739a_probe(struct spi_device *spi) if (ret) return ret; - ret = iio_backend_extend_chan_spec(indio_dev, st->back, - &ad9739a_channels[0]); + ret = iio_backend_extend_chan_spec(st->back, &ad9739a_channels[0]); if (ret) return ret; @@ -432,7 +431,13 @@ static int ad9739a_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels); indio_dev->setup_ops = &ad9739a_buffer_setup_ops; - return devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + + iio_backend_debugfs_add(st->back, indio_dev); + + return 0; } static const struct of_device_id ad9739a_of_match[] = { diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 6d56428e623d..0cb00f3bec04 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -452,7 +452,7 @@ static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan, struct axi_dac_state *st = iio_backend_get_priv(back); switch (data) { - case IIO_BACKEND_INTERNAL_CONTINUOS_WAVE: + case IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE: return regmap_update_bits(st->regmap, AXI_DAC_REG_CHAN_CNTRL_7(chan), AXI_DAC_DATA_SEL, @@ -507,7 +507,18 @@ static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan, return 0; } -static const struct iio_backend_ops axi_dac_generic = { +static int axi_dac_reg_access(struct iio_backend *back, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_backend_ops axi_dac_generic_ops = { .enable = axi_dac_enable, .disable = axi_dac_disable, .request_buffer = axi_dac_request_buffer, @@ -517,6 +528,12 @@ static const struct iio_backend_ops axi_dac_generic = { .ext_info_get = axi_dac_ext_info_get, .data_source_set = axi_dac_data_source_set, .set_sample_rate = axi_dac_set_sample_rate, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_dac_reg_access), +}; + +static const struct iio_backend_info axi_dac_generic = { + .name = "axi-dac", + .ops = &axi_dac_generic_ops, }; static const struct regmap_config axi_dac_regmap_config = { diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c new file mode 100644 index 000000000000..5be5345ac5c8 --- /dev/null +++ b/drivers/iio/dac/ltc2664.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LTC2664 4 channel, 12-/16-Bit Voltage Output SoftSpan DAC driver + * LTC2672 5 channel, 12-/16-Bit Current Output Softspan DAC driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#define LTC2664_CMD_WRITE_N(n) (0x00 + (n)) +#define LTC2664_CMD_UPDATE_N(n) (0x10 + (n)) +#define LTC2664_CMD_WRITE_N_UPDATE_ALL 0x20 +#define LTC2664_CMD_WRITE_N_UPDATE_N(n) (0x30 + (n)) +#define LTC2664_CMD_POWER_DOWN_N(n) (0x40 + (n)) +#define LTC2664_CMD_POWER_DOWN_ALL 0x50 +#define LTC2664_CMD_SPAN_N(n) (0x60 + (n)) +#define LTC2664_CMD_CONFIG 0x70 +#define LTC2664_CMD_MUX 0xB0 +#define LTC2664_CMD_TOGGLE_SEL 0xC0 +#define LTC2664_CMD_GLOBAL_TOGGLE 0xD0 +#define LTC2664_CMD_NO_OPERATION 0xF0 +#define LTC2664_REF_DISABLE 0x0001 +#define LTC2664_MSPAN_SOFTSPAN 7 + +#define LTC2672_MAX_CHANNEL 5 +#define LTC2672_MAX_SPAN 7 +#define LTC2672_SCALE_MULTIPLIER(n) (50 * BIT(n)) + +enum { + LTC2664_SPAN_RANGE_0V_5V, + LTC2664_SPAN_RANGE_0V_10V, + LTC2664_SPAN_RANGE_M5V_5V, + LTC2664_SPAN_RANGE_M10V_10V, + LTC2664_SPAN_RANGE_M2V5_2V5, +}; + +enum { + LTC2664_INPUT_A, + LTC2664_INPUT_B, + LTC2664_INPUT_B_AVAIL, + LTC2664_POWERDOWN, + LTC2664_POWERDOWN_MODE, + LTC2664_TOGGLE_EN, + LTC2664_GLOBAL_TOGGLE, +}; + +static const u16 ltc2664_mspan_lut[8][2] = { + { LTC2664_SPAN_RANGE_M10V_10V, 32768 }, /* MPS2=0, MPS1=0, MSP0=0 (0)*/ + { LTC2664_SPAN_RANGE_M5V_5V, 32768 }, /* MPS2=0, MPS1=0, MSP0=1 (1)*/ + { LTC2664_SPAN_RANGE_M2V5_2V5, 32768 }, /* MPS2=0, MPS1=1, MSP0=0 (2)*/ + { LTC2664_SPAN_RANGE_0V_10V, 0 }, /* MPS2=0, MPS1=1, MSP0=1 (3)*/ + { LTC2664_SPAN_RANGE_0V_10V, 32768 }, /* MPS2=1, MPS1=0, MSP0=0 (4)*/ + { LTC2664_SPAN_RANGE_0V_5V, 0 }, /* MPS2=1, MPS1=0, MSP0=1 (5)*/ + { LTC2664_SPAN_RANGE_0V_5V, 32768 }, /* MPS2=1, MPS1=1, MSP0=0 (6)*/ + { LTC2664_SPAN_RANGE_0V_5V, 0 } /* MPS2=1, MPS1=1, MSP0=1 (7)*/ +}; + +struct ltc2664_state; + +struct ltc2664_chip_info { + const char *name; + int (*scale_get)(const struct ltc2664_state *st, int c); + int (*offset_get)(const struct ltc2664_state *st, int c); + int measurement_type; + unsigned int num_channels; + const int (*span_helper)[2]; + unsigned int num_span; + unsigned int internal_vref_mv; + bool manual_span_support; + bool rfsadj_support; +}; + +struct ltc2664_chan { + /* indicates if the channel should be toggled */ + bool toggle_chan; + /* indicates if the channel is in powered down state */ + bool powerdown; + /* span code of the channel */ + u8 span; + /* raw data of the current state of the chip registers (A/B) */ + u16 raw[2]; +}; + +struct ltc2664_state { + struct spi_device *spi; + struct regmap *regmap; + struct ltc2664_chan channels[LTC2672_MAX_CHANNEL]; + /* lock to protect against multiple access to the device and shared data */ + struct mutex lock; + const struct ltc2664_chip_info *chip_info; + struct iio_chan_spec *iio_channels; + int vref_mv; + u32 rfsadj_ohms; + u32 toggle_sel; + bool global_toggle; +}; + +static const int ltc2664_span_helper[][2] = { + { 0, 5000 }, + { 0, 10000 }, + { -5000, 5000 }, + { -10000, 10000 }, + { -2500, 2500 }, +}; + +static const int ltc2672_span_helper[][2] = { + { 0, 0 }, + { 0, 3125 }, + { 0, 6250 }, + { 0, 12500 }, + { 0, 25000 }, + { 0, 50000 }, + { 0, 100000 }, + { 0, 200000 }, + { 0, 300000 }, +}; + +static int ltc2664_scale_get(const struct ltc2664_state *st, int c) +{ + const struct ltc2664_chan *chan = &st->channels[c]; + const int (*span_helper)[2] = st->chip_info->span_helper; + int span, fs; + + span = chan->span; + if (span < 0) + return span; + + fs = span_helper[span][1] - span_helper[span][0]; + + return fs * st->vref_mv / 2500; +} + +static int ltc2672_scale_get(const struct ltc2664_state *st, int c) +{ + const struct ltc2664_chan *chan = &st->channels[c]; + int span, fs; + + span = chan->span - 1; + if (span < 0) + return span; + + fs = 1000 * st->vref_mv; + + if (span == LTC2672_MAX_SPAN) + return mul_u64_u32_div(4800, fs, st->rfsadj_ohms); + + return mul_u64_u32_div(LTC2672_SCALE_MULTIPLIER(span), fs, st->rfsadj_ohms); +} + +static int ltc2664_offset_get(const struct ltc2664_state *st, int c) +{ + const struct ltc2664_chan *chan = &st->channels[c]; + int span; + + span = chan->span; + if (span < 0) + return span; + + if (st->chip_info->span_helper[span][0] < 0) + return -32768; + + return 0; +} + +static int ltc2664_dac_code_write(struct ltc2664_state *st, u32 chan, u32 input, + u16 code) +{ + struct ltc2664_chan *c = &st->channels[chan]; + int ret, reg; + + guard(mutex)(&st->lock); + /* select the correct input register to write to */ + if (c->toggle_chan) { + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, + input << chan); + if (ret) + return ret; + } + /* + * If in toggle mode the dac should be updated by an + * external signal (or sw toggle) and not here. + */ + if (st->toggle_sel & BIT(chan)) + reg = LTC2664_CMD_WRITE_N(chan); + else + reg = LTC2664_CMD_WRITE_N_UPDATE_N(chan); + + ret = regmap_write(st->regmap, reg, code); + if (ret) + return ret; + + c->raw[input] = code; + + if (c->toggle_chan) { + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, + st->toggle_sel); + if (ret) + return ret; + } + + return 0; +} + +static void ltc2664_dac_code_read(struct ltc2664_state *st, u32 chan, u32 input, + u32 *code) +{ + guard(mutex)(&st->lock); + *code = st->channels[chan].raw[input]; +} + +static const int ltc2664_raw_range[] = { 0, 1, U16_MAX }; + +static int ltc2664_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + *vals = ltc2664_raw_range; + *type = IIO_VAL_INT; + + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int ltc2664_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + ltc2664_dac_code_read(st, chan->channel, LTC2664_INPUT_A, val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = st->chip_info->offset_get(st, chan->channel); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->chip_info->scale_get(st, chan->channel); + + *val2 = 16; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ltc2664_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val > U16_MAX || val < 0) + return -EINVAL; + + return ltc2664_dac_code_write(st, chan->channel, + LTC2664_INPUT_A, val); + default: + return -EINVAL; + } +} + +static ssize_t ltc2664_reg_bool_get(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + u32 val; + + guard(mutex)(&st->lock); + switch (private) { + case LTC2664_POWERDOWN: + val = st->channels[chan->channel].powerdown; + + return sysfs_emit(buf, "%u\n", val); + case LTC2664_POWERDOWN_MODE: + return sysfs_emit(buf, "42kohm_to_gnd\n"); + case LTC2664_TOGGLE_EN: + val = !!(st->toggle_sel & BIT(chan->channel)); + + return sysfs_emit(buf, "%u\n", val); + case LTC2664_GLOBAL_TOGGLE: + val = st->global_toggle; + + return sysfs_emit(buf, "%u\n", val); + default: + return -EINVAL; + } +} + +static ssize_t ltc2664_reg_bool_set(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + int ret; + bool en; + + ret = kstrtobool(buf, &en); + if (ret) + return ret; + + guard(mutex)(&st->lock); + switch (private) { + case LTC2664_POWERDOWN: + ret = regmap_write(st->regmap, + en ? LTC2664_CMD_POWER_DOWN_N(chan->channel) : + LTC2664_CMD_UPDATE_N(chan->channel), en); + if (ret) + return ret; + + st->channels[chan->channel].powerdown = en; + + return len; + case LTC2664_TOGGLE_EN: + if (en) + st->toggle_sel |= BIT(chan->channel); + else + st->toggle_sel &= ~BIT(chan->channel); + + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, + st->toggle_sel); + if (ret) + return ret; + + return len; + case LTC2664_GLOBAL_TOGGLE: + ret = regmap_write(st->regmap, LTC2664_CMD_GLOBAL_TOGGLE, en); + if (ret) + return ret; + + st->global_toggle = en; + + return len; + default: + return -EINVAL; + } +} + +static ssize_t ltc2664_dac_input_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + u32 val; + + if (private == LTC2664_INPUT_B_AVAIL) + return sysfs_emit(buf, "[%u %u %u]\n", ltc2664_raw_range[0], + ltc2664_raw_range[1], + ltc2664_raw_range[2] / 4); + + ltc2664_dac_code_read(st, chan->channel, private, &val); + + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t ltc2664_dac_input_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + int ret; + u16 val; + + if (private == LTC2664_INPUT_B_AVAIL) + return -EINVAL; + + ret = kstrtou16(buf, 10, &val); + if (ret) + return ret; + + ret = ltc2664_dac_code_write(st, chan->channel, private, val); + if (ret) + return ret; + + return len; +} + +static int ltc2664_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ltc2664_state *st = iio_priv(indio_dev); + + if (readval) + return -EOPNOTSUPP; + + return regmap_write(st->regmap, reg, writeval); +} + +#define LTC2664_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \ + .name = _name, \ + .read = (_read), \ + .write = (_write), \ + .private = (_what), \ + .shared = (_shared), \ +} + +/* + * For toggle mode we only expose the symbol attr (sw_toggle) in case a TGPx is + * not provided in dts. + */ +static const struct iio_chan_spec_ext_info ltc2664_toggle_sym_ext_info[] = { + LTC2664_CHAN_EXT_INFO("raw0", LTC2664_INPUT_A, IIO_SEPARATE, + ltc2664_dac_input_read, ltc2664_dac_input_write), + LTC2664_CHAN_EXT_INFO("raw1", LTC2664_INPUT_B, IIO_SEPARATE, + ltc2664_dac_input_read, ltc2664_dac_input_write), + LTC2664_CHAN_EXT_INFO("powerdown", LTC2664_POWERDOWN, IIO_SEPARATE, + ltc2664_reg_bool_get, ltc2664_reg_bool_set), + LTC2664_CHAN_EXT_INFO("powerdown_mode", LTC2664_POWERDOWN_MODE, + IIO_SEPARATE, ltc2664_reg_bool_get, NULL), + LTC2664_CHAN_EXT_INFO("symbol", LTC2664_GLOBAL_TOGGLE, IIO_SEPARATE, + ltc2664_reg_bool_get, ltc2664_reg_bool_set), + LTC2664_CHAN_EXT_INFO("toggle_en", LTC2664_TOGGLE_EN, + IIO_SEPARATE, ltc2664_reg_bool_get, + ltc2664_reg_bool_set), + { } +}; + +static const struct iio_chan_spec_ext_info ltc2664_ext_info[] = { + LTC2664_CHAN_EXT_INFO("powerdown", LTC2664_POWERDOWN, IIO_SEPARATE, + ltc2664_reg_bool_get, ltc2664_reg_bool_set), + LTC2664_CHAN_EXT_INFO("powerdown_mode", LTC2664_POWERDOWN_MODE, + IIO_SEPARATE, ltc2664_reg_bool_get, NULL), + { } +}; + +static const struct iio_chan_spec ltc2664_channel_template = { + .indexed = 1, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .ext_info = ltc2664_ext_info, +}; + +static const struct ltc2664_chip_info ltc2664_chip = { + .name = "ltc2664", + .scale_get = ltc2664_scale_get, + .offset_get = ltc2664_offset_get, + .measurement_type = IIO_VOLTAGE, + .num_channels = 4, + .span_helper = ltc2664_span_helper, + .num_span = ARRAY_SIZE(ltc2664_span_helper), + .internal_vref_mv = 2500, + .manual_span_support = true, + .rfsadj_support = false, +}; + +static const struct ltc2664_chip_info ltc2672_chip = { + .name = "ltc2672", + .scale_get = ltc2672_scale_get, + .offset_get = ltc2664_offset_get, + .measurement_type = IIO_CURRENT, + .num_channels = 5, + .span_helper = ltc2672_span_helper, + .num_span = ARRAY_SIZE(ltc2672_span_helper), + .internal_vref_mv = 1250, + .manual_span_support = false, + .rfsadj_support = true, +}; + +static int ltc2664_set_span(const struct ltc2664_state *st, int min, int max, + int chan) +{ + const struct ltc2664_chip_info *chip_info = st->chip_info; + const int (*span_helper)[2] = chip_info->span_helper; + int span, ret; + + for (span = 0; span < chip_info->num_span; span++) { + if (min == span_helper[span][0] && max == span_helper[span][1]) + break; + } + + if (span == chip_info->num_span) + return -EINVAL; + + ret = regmap_write(st->regmap, LTC2664_CMD_SPAN_N(chan), span); + if (ret) + return ret; + + return span; +} + +static int ltc2664_channel_config(struct ltc2664_state *st) +{ + const struct ltc2664_chip_info *chip_info = st->chip_info; + struct device *dev = &st->spi->dev; + u32 reg, tmp[2], mspan; + int ret, span = 0; + + mspan = LTC2664_MSPAN_SOFTSPAN; + ret = device_property_read_u32(dev, "adi,manual-span-operation-config", + &mspan); + if (!ret) { + if (!chip_info->manual_span_support) + return dev_err_probe(dev, -EINVAL, + "adi,manual-span-operation-config not supported\n"); + + if (mspan >= ARRAY_SIZE(ltc2664_mspan_lut)) + return dev_err_probe(dev, -EINVAL, + "adi,manual-span-operation-config not in range\n"); + } + + st->rfsadj_ohms = 20000; + ret = device_property_read_u32(dev, "adi,rfsadj-ohms", &st->rfsadj_ohms); + if (!ret) { + if (!chip_info->rfsadj_support) + return dev_err_probe(dev, -EINVAL, + "adi,rfsadj-ohms not supported\n"); + + if (st->rfsadj_ohms < 19000 || st->rfsadj_ohms > 41000) + return dev_err_probe(dev, -EINVAL, + "adi,rfsadj-ohms not in range\n"); + } + + device_for_each_child_node_scoped(dev, child) { + struct ltc2664_chan *chan; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get reg property\n"); + + if (reg >= chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, + "reg bigger than: %d\n", + chip_info->num_channels); + + chan = &st->channels[reg]; + + if (fwnode_property_read_bool(child, "adi,toggle-mode")) { + chan->toggle_chan = true; + /* assume sw toggle ABI */ + st->iio_channels[reg].ext_info = ltc2664_toggle_sym_ext_info; + + /* + * Clear IIO_CHAN_INFO_RAW bit as toggle channels expose + * out_voltage/current_raw{0|1} files. + */ + __clear_bit(IIO_CHAN_INFO_RAW, + &st->iio_channels[reg].info_mask_separate); + } + + chan->raw[0] = ltc2664_mspan_lut[mspan][1]; + chan->raw[1] = ltc2664_mspan_lut[mspan][1]; + + chan->span = ltc2664_mspan_lut[mspan][0]; + + ret = fwnode_property_read_u32_array(child, "output-range-microvolt", + tmp, ARRAY_SIZE(tmp)); + if (!ret && mspan == LTC2664_MSPAN_SOFTSPAN) { + chan->span = ltc2664_set_span(st, tmp[0] / 1000, + tmp[1] / 1000, reg); + if (span < 0) + return dev_err_probe(dev, span, + "Failed to set span\n"); + } + + ret = fwnode_property_read_u32_array(child, "output-range-microamp", + tmp, ARRAY_SIZE(tmp)); + if (!ret) { + chan->span = ltc2664_set_span(st, 0, tmp[1] / 1000, reg); + if (span < 0) + return dev_err_probe(dev, span, + "Failed to set span\n"); + } + } + + return 0; +} + +static int ltc2664_setup(struct ltc2664_state *st) +{ + const struct ltc2664_chip_info *chip_info = st->chip_info; + struct gpio_desc *gpio; + int ret, i; + + /* If we have a clr/reset pin, use that to reset the chip. */ + gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return dev_err_probe(&st->spi->dev, PTR_ERR(gpio), + "Failed to get reset gpio"); + if (gpio) { + fsleep(1000); + gpiod_set_value_cansleep(gpio, 0); + } + + /* + * Duplicate the default channel configuration as it can change during + * @ltc2664_channel_config() + */ + st->iio_channels = devm_kcalloc(&st->spi->dev, + chip_info->num_channels, + sizeof(struct iio_chan_spec), + GFP_KERNEL); + if (!st->iio_channels) + return -ENOMEM; + + for (i = 0; i < chip_info->num_channels; i++) { + st->iio_channels[i] = ltc2664_channel_template; + st->iio_channels[i].type = chip_info->measurement_type; + st->iio_channels[i].channel = i; + } + + ret = ltc2664_channel_config(st); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, LTC2664_CMD_CONFIG, LTC2664_REF_DISABLE); +} + +static const struct regmap_config ltc2664_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = LTC2664_CMD_NO_OPERATION, +}; + +static const struct iio_info ltc2664_info = { + .write_raw = ltc2664_write_raw, + .read_raw = ltc2664_read_raw, + .read_avail = ltc2664_read_avail, + .debugfs_reg_access = ltc2664_reg_access, +}; + +static int ltc2664_probe(struct spi_device *spi) +{ + static const char * const regulators[] = { "vcc", "iovcc", "v-neg" }; + const struct ltc2664_chip_info *chip_info; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ltc2664_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; + + chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return -ENODEV; + + st->chip_info = chip_info; + + mutex_init(&st->lock); + + st->regmap = devm_regmap_init_spi(spi, <c2664_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to init regmap"); + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), + regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + st->vref_mv = ret > 0 ? ret / 1000 : chip_info->internal_vref_mv; + + ret = ltc2664_setup(st); + if (ret) + return ret; + + indio_dev->name = chip_info->name; + indio_dev->info = <c2664_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->iio_channels; + indio_dev->num_channels = chip_info->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ltc2664_id[] = { + { "ltc2664", (kernel_ulong_t)<c2664_chip }, + { "ltc2672", (kernel_ulong_t)<c2672_chip }, + { } +}; +MODULE_DEVICE_TABLE(spi, ltc2664_id); + +static const struct of_device_id ltc2664_of_id[] = { + { .compatible = "adi,ltc2664", .data = <c2664_chip }, + { .compatible = "adi,ltc2672", .data = <c2672_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2664_of_id); + +static struct spi_driver ltc2664_driver = { + .driver = { + .name = "ltc2664", + .of_match_table = ltc2664_of_id, + }, + .probe = ltc2664_probe, + .id_table = ltc2664_id, +}; +module_spi_driver(ltc2664_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>"); +MODULE_DESCRIPTION("Analog Devices LTC2664 and LTC2672 DAC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index af50d2a95898..376dca163c91 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -918,7 +918,7 @@ static bool ltc2688_reg_writable(struct device *dev, unsigned int reg) return false; } -static struct regmap_bus ltc2688_regmap_bus = { +static const struct regmap_bus ltc2688_regmap_bus = { .read = ltc2688_spi_read, .write = ltc2688_spi_write, .read_flag_mask = LTC2688_READ_OPERATION, diff --git a/drivers/iio/dac/mcp4728.c b/drivers/iio/dac/mcp4728.c index c449ca949465..192175dc6419 100644 --- a/drivers/iio/dac/mcp4728.c +++ b/drivers/iio/dac/mcp4728.c @@ -84,7 +84,6 @@ enum mcp4728_scale { struct mcp4728_data { struct i2c_client *client; - struct regulator *vdd_reg; bool powerdown; int scales_avail[MCP4728_N_SCALES * 2]; struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS]; @@ -415,15 +414,9 @@ static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv, data->scales_avail[scale * 2 + 1] = value_micro; } -static int mcp4728_init_scales_avail(struct mcp4728_data *data) +static int mcp4728_init_scales_avail(struct mcp4728_data *data, int vdd_mv) { - int ret; - - ret = regulator_get_voltage(data->vdd_reg); - if (ret < 0) - return ret; - - mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data); + mcp4728_init_scale_avail(MCP4728_SCALE_VDD, vdd_mv, data); mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data); mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data); @@ -530,17 +523,12 @@ static int mcp4728_init_channels_data(struct mcp4728_data *data) return 0; } -static void mcp4728_reg_disable(void *reg) -{ - regulator_disable(reg); -} - static int mcp4728_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); struct mcp4728_data *data; struct iio_dev *indio_dev; - int err; + int ret, vdd_mv; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -550,18 +538,11 @@ static int mcp4728_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(data->vdd_reg)) - return PTR_ERR(data->vdd_reg); - - err = regulator_enable(data->vdd_reg); - if (err) - return err; + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vdd"); + if (ret < 0) + return ret; - err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable, - data->vdd_reg); - if (err) - return err; + vdd_mv = ret / 1000; /* * MCP4728 has internal EEPROM that save each channel boot @@ -569,15 +550,15 @@ static int mcp4728_probe(struct i2c_client *client) * driver at kernel boot. mcp4728_init_channels_data() reads back DAC * settings and stores them in data structure. */ - err = mcp4728_init_channels_data(data); - if (err) { - return dev_err_probe(&client->dev, err, + ret = mcp4728_init_channels_data(data); + if (ret) { + return dev_err_probe(&client->dev, ret, "failed to read mcp4728 current configuration\n"); } - err = mcp4728_init_scales_avail(data); - if (err) { - return dev_err_probe(&client->dev, err, + ret = mcp4728_init_scales_avail(data, vdd_mv); + if (ret) { + return dev_err_probe(&client->dev, ret, "failed to init scales\n"); } diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index da4327624d45..26aa99059813 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -30,7 +30,6 @@ struct mcp4922_state { struct spi_device *spi; unsigned int value[MCP4922_NUM_CHANNELS]; unsigned int vref_mv; - struct regulator *vref_reg; u8 mosi[2] __aligned(IIO_DMA_MINALIGN); }; @@ -132,27 +131,13 @@ static int mcp4922_probe(struct spi_device *spi) state = iio_priv(indio_dev); state->spi = spi; - state->vref_reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(state->vref_reg)) - return dev_err_probe(&spi->dev, PTR_ERR(state->vref_reg), - "Vref regulator not specified\n"); - - ret = regulator_enable(state->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vref regulator: %d\n", - ret); - return ret; - } - ret = regulator_get_voltage(state->vref_reg); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read vref regulator: %d\n", - ret); - goto error_disable_reg; - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, "Failed to get vref voltage\n"); + state->vref_mv = ret / 1000; - spi_set_drvdata(spi, indio_dev); id = spi_get_device_id(spi); indio_dev->info = &mcp4922_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -163,30 +148,13 @@ static int mcp4922_probe(struct spi_device *spi) indio_dev->num_channels = MCP4922_NUM_CHANNELS; indio_dev->name = id->name; - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", - ret); - goto error_disable_reg; - } + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to register iio device\n"); return 0; - -error_disable_reg: - regulator_disable(state->vref_reg); - - return ret; } -static void mcp4922_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct mcp4922_state *state; - - iio_device_unregister(indio_dev); - state = iio_priv(indio_dev); - regulator_disable(state->vref_reg); -} static const struct spi_device_id mcp4922_id[] = { {"mcp4902", ID_MCP4902}, @@ -202,7 +170,6 @@ static struct spi_driver mcp4922_driver = { .name = "mcp4922", }, .probe = mcp4922_probe, - .remove = mcp4922_remove, .id_table = mcp4922_id, }; module_spi_driver(mcp4922_driver); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 7f89d2a52f49..6f4aa4794a0c 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -249,7 +249,9 @@ static int ti_dac_probe(struct spi_device *spi) spi->mode = SPI_MODE_1; spi->bits_per_word = 16; - spi_setup(spi); + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "spi_setup failed\n"); indio_dev->info = &ti_dac_info; indio_dev->name = spi_get_device_id(spi)->name; diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 9b2f99449a82..4ca3f1aaff99 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -68,7 +68,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) * Here let's pretend we have random access. And the values are in the * constant table fakedata. */ - for_each_set_bit(j, indio_dev->active_scan_mask, indio_dev->masklength) + iio_for_each_active_channel(indio_dev, j) data[i++] = fakedata[j]; iio_push_to_buffers_with_timestamp(indio_dev, data, diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index 9284c13f1abb..25fbc2cef1f7 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -400,7 +400,13 @@ enum muxout_select_mode { ADF4377_MUXOUT_HIGH = 0x8, }; +struct adf4377_chip_info { + const char *name; + bool has_gpio_enclk2; +}; + struct adf4377_state { + const struct adf4377_chip_info *chip_info; struct spi_device *spi; struct regmap *regmap; struct clk *clkin; @@ -889,11 +895,13 @@ static int adf4377_properties_parse(struct adf4377_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk1), "failed to get the CE GPIO\n"); - st->gpio_enclk2 = devm_gpiod_get_optional(&st->spi->dev, "clk2-enable", - GPIOD_OUT_LOW); - if (IS_ERR(st->gpio_enclk2)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2), - "failed to get the CE GPIO\n"); + if (st->chip_info->has_gpio_enclk2) { + st->gpio_enclk2 = devm_gpiod_get_optional(&st->spi->dev, "clk2-enable", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_enclk2)) + return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2), + "failed to get the CE GPIO\n"); + } ret = device_property_match_property_string(&spi->dev, "adi,muxout-select", adf4377_muxout_modes, @@ -921,6 +929,16 @@ static int adf4377_freq_change(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } +static const struct adf4377_chip_info adf4377_chip_info = { + .name = "adf4377", + .has_gpio_enclk2 = true, +}; + +static const struct adf4377_chip_info adf4378_chip_info = { + .name = "adf4378", + .has_gpio_enclk2 = false, +}; + static int adf4377_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -945,6 +963,7 @@ static int adf4377_probe(struct spi_device *spi) st->regmap = regmap; st->spi = spi; + st->chip_info = spi_get_device_match_data(spi); mutex_init(&st->lock); ret = adf4377_properties_parse(st); @@ -964,13 +983,15 @@ static int adf4377_probe(struct spi_device *spi) } static const struct spi_device_id adf4377_id[] = { - { "adf4377", 0 }, + { "adf4377", (kernel_ulong_t)&adf4377_chip_info }, + { "adf4378", (kernel_ulong_t)&adf4378_chip_info }, {} }; MODULE_DEVICE_TABLE(spi, adf4377_id); static const struct of_device_id adf4377_of_match[] = { - { .compatible = "adi,adf4377" }, + { .compatible = "adi,adf4377", .data = &adf4377_chip_info }, + { .compatible = "adi,adf4378", .data = &adf4378_chip_info }, {} }; MODULE_DEVICE_TABLE(of, adf4377_of_match); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 52326dc521ac..85637e8ac45f 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -321,8 +321,7 @@ static irqreturn_t afe4403_trigger_handler(int irq, void *private) if (ret) goto err; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = spi_write_then_read(afe->spi, &afe4403_channel_values[bit], 1, rx, sizeof(rx)); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 7f69baa1ed53..d49e1572a439 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -333,8 +333,7 @@ static irqreturn_t afe4404_trigger_handler(int irq, void *private) struct afe4404_data *afe = iio_priv(indio_dev); int ret, bit, i = 0; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = regmap_read(afe->regmap, afe4404_channel_values[bit], &afe->buffer[i++]); if (ret) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 07a343e35a81..1d074eb6a8c5 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -293,7 +293,7 @@ static irqreturn_t max30102_interrupt_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct max30102_data *data = iio_priv(indio_dev); unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); int ret, cnt = 0; mutex_lock(&data->lock); diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index b15b7a3b66d5..54f11f000b6f 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -25,6 +25,17 @@ config DHT11 Other sensors should work as well as long as they speak the same protocol. +config ENS210 + tristate "ENS210 temperature and humidity sensor" + depends on I2C + select CRC7 + help + Say yes here to get support for the ScioSense ENS210 family of + humidity and temperature sensors. + + This driver can also be built as a module. If so, the module will be + called ens210. + config HDC100X tristate "TI HDC100x relative humidity and temperature sensor" depends on I2C diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile index 5fbeef299f61..34b3dc749466 100644 --- a/drivers/iio/humidity/Makefile +++ b/drivers/iio/humidity/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_AM2315) += am2315.o obj-$(CONFIG_DHT11) += dht11.o +obj-$(CONFIG_ENS210) += ens210.o obj-$(CONFIG_HDC100X) += hdc100x.o obj-$(CONFIG_HDC2010) += hdc2010.o obj-$(CONFIG_HDC3020) += hdc3020.o diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index a56474be5dd2..6b0aa3a3f025 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -174,8 +174,7 @@ static irqreturn_t am2315_trigger_handler(int irq, void *p) data->scan.chans[1] = sensor_data.temp_data; } else { i = 0; - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { data->scan.chans[i] = (bit ? sensor_data.temp_data : sensor_data.hum_data); i++; diff --git a/drivers/iio/humidity/ens210.c b/drivers/iio/humidity/ens210.c new file mode 100644 index 000000000000..e9167574203a --- /dev/null +++ b/drivers/iio/humidity/ens210.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ens210.c - Support for ScioSense ens210 temperature & humidity sensor family + * + * (7-bit I2C slave address 0x43 ENS210) + * (7-bit I2C slave address 0x43 ENS210A) + * (7-bit I2C slave address 0x44 ENS211) + * (7-bit I2C slave address 0x45 ENS212) + * (7-bit I2C slave address 0x46 ENS213A) + * (7-bit I2C slave address 0x47 ENS215) + * + * Datasheet: + * https://www.sciosense.com/wp-content/uploads/2024/04/ENS21x-Datasheet.pdf + * https://www.sciosense.com/wp-content/uploads/2023/12/ENS210-Datasheet.pdf + */ + +#include <linux/crc7.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/types.h> + +#include <asm/unaligned.h> + +/* register definitions */ +#define ENS210_REG_PART_ID 0x00 +#define ENS210_REG_DIE_REV 0x02 +#define ENS210_REG_UID 0x04 +#define ENS210_REG_SYS_CTRL 0x10 +#define ENS210_REG_SYS_STAT 0x11 +#define ENS210_REG_SENS_RUN 0x21 +#define ENS210_REG_SENS_START 0x22 +#define ENS210_REG_SENS_STOP 0x23 +#define ENS210_REG_SENS_STAT 0x24 +#define ENS210_REG_T_VAL 0x30 +#define ENS210_REG_H_VAL 0x33 + +/* value definitions */ +#define ENS210_SENS_START_T_START BIT(0) +#define ENS210_SENS_START_H_START BIT(1) + +#define ENS210_SENS_STAT_T_ACTIVE BIT(0) +#define ENS210_SENS_STAT_H_ACTIVE BIT(1) + +#define ENS210_SYS_CTRL_LOW_POWER_ENABLE BIT(0) +#define ENS210_SYS_CTRL_SYS_RESET BIT(7) + +#define ENS210_SYS_STAT_SYS_ACTIVE BIT(0) + +enum ens210_partnumber { + ENS210 = 0x0210, + ENS210A = 0xa210, + ENS211 = 0x0211, + ENS212 = 0x0212, + ENS213A = 0xa213, + ENS215 = 0x0215, +}; + +/** + * struct ens210_chip_info - Humidity/Temperature chip specific information + * @name: name of device + * @part_id: chip identifier + * @conv_time_msec: time for conversion calculation in m/s + */ +struct ens210_chip_info { + const char *name; + enum ens210_partnumber part_id; + unsigned int conv_time_msec; +}; + +/** + * struct ens210_data - Humidity/Temperature sensor device structure + * @client: i2c client + * @chip_info: chip specific information + * @lock: lock protecting against simultaneous callers of get_measurement + * since multiple uninterrupted transactions are required + */ +struct ens210_data { + struct i2c_client *client; + const struct ens210_chip_info *chip_info; + struct mutex lock; +}; + +/* calculate 17-bit crc7 */ +static u8 ens210_crc7(u32 val) +{ + unsigned int val_be = (val & 0x1ffff) >> 0x8; + + return crc7_be(0xde, (u8 *)&val_be, 3) >> 1; +} + +static int ens210_get_measurement(struct iio_dev *indio_dev, bool temp, int *val) +{ + struct ens210_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + u32 regval; + u8 regval_le[3]; + int ret; + + /* assert read */ + ret = i2c_smbus_write_byte_data(data->client, ENS210_REG_SENS_START, + temp ? ENS210_SENS_START_T_START : + ENS210_SENS_START_H_START); + if (ret) + return ret; + + /* wait for conversion to be ready */ + msleep(data->chip_info->conv_time_msec); + + ret = i2c_smbus_read_byte_data(data->client, ENS210_REG_SENS_STAT); + if (ret < 0) + return ret; + + /* perform read */ + ret = i2c_smbus_read_i2c_block_data( + data->client, temp ? ENS210_REG_T_VAL : ENS210_REG_H_VAL, 3, + regval_le); + if (ret < 0) { + dev_err(dev, "failed to read register"); + return -EIO; + } + if (ret != 3) { + dev_err(dev, "expected 3 bytes, received %d\n", ret); + return -EIO; + } + + regval = get_unaligned_le24(regval_le); + if (ens210_crc7(regval) != ((regval >> 17) & 0x7f)) { + dev_err(dev, "invalid crc\n"); + return -EIO; + } + + if (!((regval >> 16) & 0x1)) { + dev_err(dev, "data is not valid"); + return -EIO; + } + + *val = regval & GENMASK(15, 0); + return IIO_VAL_INT; +} + +static int ens210_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct ens210_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + scoped_guard(mutex, &data->lock) { + ret = ens210_get_measurement( + indio_dev, channel->type == IIO_TEMP, val); + if (ret) + return ret; + return IIO_VAL_INT; + } + return -EINVAL; /* compiler warning workaround */ + case IIO_CHAN_INFO_SCALE: + if (channel->type == IIO_TEMP) { + *val = 15; + *val2 = 625000; + } else { + *val = 1; + *val2 = 953125; + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = -17481; + *val2 = 600000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec ens210_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + } +}; + +static const struct iio_info ens210_info = { + .read_raw = ens210_read_raw, +}; + +static int ens210_probe(struct i2c_client *client) +{ + struct ens210_data *data; + struct iio_dev *indio_dev; + uint16_t part_id; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + return dev_err_probe(&client->dev, -EOPNOTSUPP, + "adapter does not support some i2c transactions\n"); + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + data->chip_info = i2c_get_match_data(client); + + ret = devm_regulator_get_enable(&client->dev, "vdd"); + if (ret) + return ret; + + /* reset device */ + ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL, + ENS210_SYS_CTRL_SYS_RESET); + if (ret) + return ret; + + /* wait for device to become active */ + usleep_range(4000, 5000); + + /* disable low power mode */ + ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL, 0x00); + if (ret) + return ret; + + /* wait for device to finish */ + usleep_range(4000, 5000); + + /* get part_id */ + ret = i2c_smbus_read_word_data(client, ENS210_REG_PART_ID); + if (ret < 0) + return ret; + part_id = ret; + + if (part_id != data->chip_info->part_id) { + dev_info(&client->dev, + "Part ID does not match (0x%04x != 0x%04x)\n", part_id, + data->chip_info->part_id); + } + + /* reenable low power */ + ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL, + ENS210_SYS_CTRL_LOW_POWER_ENABLE); + if (ret) + return ret; + + indio_dev->name = data->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ens210_channels; + indio_dev->num_channels = ARRAY_SIZE(ens210_channels); + indio_dev->info = &ens210_info; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct ens210_chip_info ens210_chip_info_data = { + .name = "ens210", + .part_id = ENS210, + .conv_time_msec = 130, +}; + +static const struct ens210_chip_info ens210a_chip_info_data = { + .name = "ens210a", + .part_id = ENS210A, + .conv_time_msec = 130, +}; + +static const struct ens210_chip_info ens211_chip_info_data = { + .name = "ens211", + .part_id = ENS211, + .conv_time_msec = 32, +}; + +static const struct ens210_chip_info ens212_chip_info_data = { + .name = "ens212", + .part_id = ENS212, + .conv_time_msec = 32, +}; + +static const struct ens210_chip_info ens213a_chip_info_data = { + .name = "ens213a", + .part_id = ENS213A, + .conv_time_msec = 130, +}; + +static const struct ens210_chip_info ens215_chip_info_data = { + .name = "ens215", + .part_id = ENS215, + .conv_time_msec = 130, +}; + +static const struct of_device_id ens210_of_match[] = { + { .compatible = "sciosense,ens210", .data = &ens210_chip_info_data }, + { .compatible = "sciosense,ens210a", .data = &ens210a_chip_info_data }, + { .compatible = "sciosense,ens211", .data = &ens211_chip_info_data }, + { .compatible = "sciosense,ens212", .data = &ens212_chip_info_data }, + { .compatible = "sciosense,ens213a", .data = &ens213a_chip_info_data }, + { .compatible = "sciosense,ens215", .data = &ens215_chip_info_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ens210_of_match); + +static const struct i2c_device_id ens210_id_table[] = { + { "ens210", (kernel_ulong_t)&ens210_chip_info_data }, + { "ens210a", (kernel_ulong_t)&ens210a_chip_info_data }, + { "ens211", (kernel_ulong_t)&ens211_chip_info_data }, + { "ens212", (kernel_ulong_t)&ens212_chip_info_data }, + { "ens213a", (kernel_ulong_t)&ens213a_chip_info_data }, + { "ens215", (kernel_ulong_t)&ens215_chip_info_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ens210_id_table); + +static struct i2c_driver ens210_driver = { + .probe = ens210_probe, + .id_table = ens210_id_table, + .driver = { + .name = "ens210", + .of_match_table = ens210_of_match, + }, +}; +module_i2c_driver(ens210_driver); + +MODULE_DESCRIPTION("ScioSense ENS210 temperature and humidity sensor driver"); +MODULE_AUTHOR("Joshua Felmeden <jfelmeden@thegoodpenguin.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 0bfd6205f5f6..6484ab8aff55 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -202,8 +202,6 @@ enum { ADIS16400_SCAN_TIMESTAMP, }; -#ifdef CONFIG_DEBUG_FS - static ssize_t adis16400_show_serial_number(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -273,11 +271,14 @@ static int adis16400_show_flash_count(void *arg, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(adis16400_flash_count_fops, adis16400_show_flash_count, NULL, "%lld\n"); -static int adis16400_debugfs_init(struct iio_dev *indio_dev) +static void adis16400_debugfs_init(struct iio_dev *indio_dev) { struct adis16400_state *st = iio_priv(indio_dev); struct dentry *d = iio_get_debugfs_dentry(indio_dev); + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER) debugfs_create_file_unsafe("serial_number", 0400, d, st, &adis16400_serial_number_fops); @@ -286,19 +287,8 @@ static int adis16400_debugfs_init(struct iio_dev *indio_dev) d, st, &adis16400_product_id_fops); debugfs_create_file_unsafe("flash_count", 0400, d, st, &adis16400_flash_count_fops); - - return 0; } -#else - -static int adis16400_debugfs_init(struct iio_dev *indio_dev) -{ - return 0; -} - -#endif - enum adis16400_chip_variant { ADIS16300, ADIS16334, diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 69facd72bd7d..eaa38dd6201f 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -69,8 +69,6 @@ struct adis16460 { struct adis adis; }; -#ifdef CONFIG_DEBUG_FS - static int adis16460_show_serial_number(void *arg, u64 *val) { struct adis16460 *adis16460 = arg; @@ -125,30 +123,22 @@ static int adis16460_show_flash_count(void *arg, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(adis16460_flash_count_fops, adis16460_show_flash_count, NULL, "%lld\n"); -static int adis16460_debugfs_init(struct iio_dev *indio_dev) +static void adis16460_debugfs_init(struct iio_dev *indio_dev) { struct adis16460 *adis16460 = iio_priv(indio_dev); struct dentry *d = iio_get_debugfs_dentry(indio_dev); + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + debugfs_create_file_unsafe("serial_number", 0400, d, adis16460, &adis16460_serial_number_fops); debugfs_create_file_unsafe("product_id", 0400, d, adis16460, &adis16460_product_id_fops); debugfs_create_file_unsafe("flash_count", 0400, d, adis16460, &adis16460_flash_count_fops); - - return 0; -} - -#else - -static int adis16460_debugfs_init(struct iio_dev *indio_dev) -{ - return 0; } -#endif - static int adis16460_set_freq(struct iio_dev *indio_dev, int val, int val2) { struct adis16460 *st = iio_priv(indio_dev); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 482258ed5a3c..88efe728b61b 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -164,7 +164,6 @@ module_param(low_rate_allow, bool, 0444); MODULE_PARM_DESC(low_rate_allow, "Allow IMU rates below the minimum advisable when external clk is used in SCALED mode (default: N)"); -#ifdef CONFIG_DEBUG_FS static ssize_t adis16475_show_firmware_revision(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) @@ -279,6 +278,9 @@ static void adis16475_debugfs_init(struct iio_dev *indio_dev) struct adis16475 *st = iio_priv(indio_dev); struct dentry *d = iio_get_debugfs_dentry(indio_dev); + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + debugfs_create_file_unsafe("serial_number", 0400, d, st, &adis16475_serial_number_fops); debugfs_create_file_unsafe("product_id", 0400, @@ -290,11 +292,6 @@ static void adis16475_debugfs_init(struct iio_dev *indio_dev) debugfs_create_file("firmware_date", 0400, d, st, &adis16475_firmware_date_fops); } -#else -static void adis16475_debugfs_init(struct iio_dev *indio_dev) -{ -} -#endif static int adis16475_get_freq(struct adis16475 *st, u32 *freq) { @@ -1593,8 +1590,7 @@ static int adis16475_push_single_sample(struct iio_poll_func *pf) return -EINVAL; } - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { /* * When burst mode is used, system flags is the first data * channel in the sequence, but the scan index is 7. diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index c59ef6f7cfd4..294181f2fcb3 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -193,8 +193,6 @@ module_param(low_rate_allow, bool, 0444); MODULE_PARM_DESC(low_rate_allow, "Allow IMU rates below the minimum advisable when external clk is used in PPS mode (default: N)"); -#ifdef CONFIG_DEBUG_FS - static ssize_t adis16480_show_firmware_revision(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -304,11 +302,14 @@ static int adis16480_show_flash_count(void *arg, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(adis16480_flash_count_fops, adis16480_show_flash_count, NULL, "%lld\n"); -static int adis16480_debugfs_init(struct iio_dev *indio_dev) +static void adis16480_debugfs_init(struct iio_dev *indio_dev) { struct adis16480 *adis16480 = iio_priv(indio_dev); struct dentry *d = iio_get_debugfs_dentry(indio_dev); + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + debugfs_create_file_unsafe("firmware_revision", 0400, d, adis16480, &adis16480_firmware_revision_fops); debugfs_create_file_unsafe("firmware_date", 0400, @@ -319,19 +320,8 @@ static int adis16480_debugfs_init(struct iio_dev *indio_dev) d, adis16480, &adis16480_product_id_fops); debugfs_create_file_unsafe("flash_count", 0400, d, adis16480, &adis16480_flash_count_fops); - - return 0; -} - -#else - -static int adis16480_debugfs_init(struct iio_dev *indio_dev) -{ - return 0; } -#endif - static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) { struct adis16480 *st = iio_priv(indio_dev); @@ -1395,7 +1385,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p) goto irq_done; } - for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { /* * When burst mode is used, temperature is the first data * channel in the sequence, but the temperature scan index diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 90aa04d94da5..495e8a74ac67 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -435,8 +435,7 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L; __le16 sample; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample), &sample, sizeof(sample)); if (ret) diff --git a/drivers/iio/imu/bmi323/bmi323.h b/drivers/iio/imu/bmi323/bmi323.h index dff126d41658..209bccb1f335 100644 --- a/drivers/iio/imu/bmi323/bmi323.h +++ b/drivers/iio/imu/bmi323/bmi323.h @@ -205,5 +205,6 @@ struct device; int bmi323_core_probe(struct device *dev); extern const struct regmap_config bmi323_regmap_config; +extern const struct dev_pm_ops bmi323_core_pm_ops; #endif diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index d708d1fe3e42..671401ce80dc 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -118,6 +118,38 @@ static const struct bmi323_hw bmi323_hw[2] = { }, }; +static const unsigned int bmi323_reg_savestate[] = { + BMI323_INT_MAP1_REG, + BMI323_INT_MAP2_REG, + BMI323_IO_INT_CTR_REG, + BMI323_IO_INT_CONF_REG, + BMI323_ACC_CONF_REG, + BMI323_GYRO_CONF_REG, + BMI323_FEAT_IO0_REG, + BMI323_FIFO_WTRMRK_REG, + BMI323_FIFO_CONF_REG +}; + +static const unsigned int bmi323_ext_reg_savestate[] = { + BMI323_GEN_SET1_REG, + BMI323_TAP1_REG, + BMI323_TAP2_REG, + BMI323_TAP3_REG, + BMI323_FEAT_IO0_S_TAP_MSK, + BMI323_STEP_SC1_REG, + BMI323_ANYMO1_REG, + BMI323_NOMO1_REG, + BMI323_ANYMO1_REG + BMI323_MO2_OFFSET, + BMI323_NOMO1_REG + BMI323_MO2_OFFSET, + BMI323_ANYMO1_REG + BMI323_MO3_OFFSET, + BMI323_NOMO1_REG + BMI323_MO3_OFFSET +}; + +struct bmi323_regs_runtime_pm { + unsigned int reg_settings[ARRAY_SIZE(bmi323_reg_savestate)]; + unsigned int ext_reg_settings[ARRAY_SIZE(bmi323_ext_reg_savestate)]; +}; + struct bmi323_data { struct device *dev; struct regmap *regmap; @@ -130,6 +162,7 @@ struct bmi323_data { u32 odrns[BMI323_SENSORS_CNT]; u32 odrhz[BMI323_SENSORS_CNT]; unsigned int feature_events; + struct bmi323_regs_runtime_pm runtime_pm_status; /* * Lock to protect the members of device's private data from concurrent @@ -1972,6 +2005,11 @@ static void bmi323_disable(void *data_ptr) bmi323_set_mode(data, BMI323_ACCEL, ACC_GYRO_MODE_DISABLE); bmi323_set_mode(data, BMI323_GYRO, ACC_GYRO_MODE_DISABLE); + + /* + * Place the peripheral in its lowest power consuming state. + */ + regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); } static int bmi323_set_bw(struct bmi323_data *data, @@ -2030,6 +2068,13 @@ static int bmi323_init(struct bmi323_data *data) return dev_err_probe(data->dev, -EINVAL, "Sensor power error = 0x%x\n", val); + return 0; +} + +static int bmi323_init_reset(struct bmi323_data *data) +{ + int ret; + /* * Set the Bandwidth coefficient which defines the 3 dB cutoff * frequency in relation to the ODR. @@ -2078,12 +2123,18 @@ int bmi323_core_probe(struct device *dev) data = iio_priv(indio_dev); data->dev = dev; data->regmap = regmap; + data->irq_pin = BMI323_IRQ_DISABLED; + data->state = BMI323_IDLE; mutex_init(&data->mutex); ret = bmi323_init(data); if (ret) return -EINVAL; + ret = bmi323_init_reset(data); + if (ret) + return -EINVAL; + if (!iio_read_acpi_mount_matrix(dev, &data->orientation, "ROTM")) { ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) @@ -2117,10 +2168,139 @@ int bmi323_core_probe(struct device *dev) return dev_err_probe(data->dev, ret, "Unable to register iio device\n"); - return 0; + return bmi323_fifo_disable(data); } EXPORT_SYMBOL_NS_GPL(bmi323_core_probe, IIO_BMI323); +#if defined(CONFIG_PM) +static int bmi323_core_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi323_data *data = iio_priv(indio_dev); + struct bmi323_regs_runtime_pm *savestate = &data->runtime_pm_status; + int ret; + + guard(mutex)(&data->mutex); + + ret = iio_device_suspend_triggering(indio_dev); + if (ret) + return ret; + + /* Save registers meant to be restored by resume pm callback. */ + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_reg_savestate); i++) { + ret = regmap_read(data->regmap, bmi323_reg_savestate[i], + &savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_ext_reg_savestate); i++) { + ret = bmi323_read_ext_reg(data, bmi323_reg_savestate[i], + &savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 external reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + /* Perform soft reset to place the device in its lowest power state. */ + ret = regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); + if (ret) + return ret; + + return 0; +} + +static int bmi323_core_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi323_data *data = iio_priv(indio_dev); + struct bmi323_regs_runtime_pm *savestate = &data->runtime_pm_status; + unsigned int val; + int ret; + + guard(mutex)(&data->mutex); + + /* + * Perform the device power-on and initial setup once again + * after being reset in the lower power state by runtime-pm. + */ + ret = bmi323_init(data); + if (!ret) + return ret; + + /* Register must be cleared before changing an active config */ + ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, 0); + if (ret) { + dev_err(data->dev, "Error stopping feature engine\n"); + return ret; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_ext_reg_savestate); i++) { + ret = bmi323_write_ext_reg(data, bmi323_reg_savestate[i], + savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error writing bmi323 external reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_reg_savestate); i++) { + ret = regmap_write(data->regmap, bmi323_reg_savestate[i], + savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error writing bmi323 reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + /* + * Clear old FIFO samples that might be generated before suspend + * or generated from a peripheral state not equal to the saved one. + */ + if (data->state == BMI323_BUFFER_FIFO) { + ret = regmap_write(data->regmap, BMI323_FIFO_CTRL_REG, + BMI323_FIFO_FLUSH_MSK); + if (ret) { + dev_err(data->dev, "Error flushing FIFO buffer: %d\n", ret); + return ret; + } + } + + ret = regmap_read(data->regmap, BMI323_ERR_REG, &val); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 error register: %d\n", ret); + return ret; + } + + if (val) { + dev_err(data->dev, + "Sensor power error in PM = 0x%x\n", val); + return -EINVAL; + } + + return iio_device_resume_triggering(indio_dev); +} + +#endif + +const struct dev_pm_ops bmi323_core_pm_ops = { + SET_RUNTIME_PM_OPS(bmi323_core_runtime_suspend, + bmi323_core_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(bmi323_core_pm_ops, IIO_BMI323); + MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bmi323/bmi323_i2c.c b/drivers/iio/imu/bmi323/bmi323_i2c.c index 52140bf05765..0ba5d69d8329 100644 --- a/drivers/iio/imu/bmi323/bmi323_i2c.c +++ b/drivers/iio/imu/bmi323/bmi323_i2c.c @@ -61,7 +61,7 @@ static int bmi323_regmap_i2c_write(void *context, const void *data, data + sizeof(u8)); } -static struct regmap_bus bmi323_regmap_bus = { +static const struct regmap_bus bmi323_regmap_bus = { .read = bmi323_regmap_i2c_read, .write = bmi323_regmap_i2c_write, }; @@ -128,6 +128,7 @@ MODULE_DEVICE_TABLE(of, bmi323_of_i2c_match); static struct i2c_driver bmi323_i2c_driver = { .driver = { .name = "bmi323", + .pm = pm_ptr(&bmi323_core_pm_ops), .of_match_table = bmi323_of_i2c_match, .acpi_match_table = bmi323_acpi_match, }, diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c index 7b1e8127d0dd..9de3ade78d71 100644 --- a/drivers/iio/imu/bmi323/bmi323_spi.c +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -36,7 +36,7 @@ static int bmi323_regmap_spi_write(void *context, const void *data, return spi_write(spi, data_buff + 1, count - 1); } -static struct regmap_bus bmi323_regmap_bus = { +static const struct regmap_bus bmi323_regmap_bus = { .read = bmi323_regmap_spi_read, .write = bmi323_regmap_spi_write, }; @@ -79,6 +79,7 @@ MODULE_DEVICE_TABLE(of, bmi323_of_spi_match); static struct spi_driver bmi323_spi_driver = { .driver = { .name = "bmi323", + .pm = pm_ptr(&bmi323_core_pm_ops), .of_match_table = bmi323_of_spi_match, }, .probe = bmi323_spi_probe, diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 52744dd98e65..ea6519b22b2f 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -1458,7 +1458,7 @@ static irqreturn_t bno055_trigger_handler(int irq, void *p) * then we split the transfer, skipping the gap. */ for_each_set_bitrange(start, end, iio_dev->active_scan_mask, - iio_dev->masklength) { + iio_get_masklength(iio_dev)) { /* * First transfer will start from the beginning of the first * ones-field in the bitmap diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c index 694ff14a3aa2..da7873bfd348 100644 --- a/drivers/iio/imu/bno055/bno055_ser_core.c +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -492,7 +492,7 @@ static const struct serdev_device_ops bno055_ser_serdev_ops = { .write_wakeup = serdev_device_write_wakeup, }; -static struct regmap_bus bno055_ser_regmap_bus = { +static const struct regmap_bus bno055_ser_regmap_bus = { .write = bno055_ser_write_reg, .read = bno055_ser_read_reg, }; diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index d37eca5ef761..c61c012e25bb 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1200,8 +1200,7 @@ static irqreturn_t kmx61_trigger_handler(int irq, void *p) base = KMX61_MAG_XOUT_L; mutex_lock(&data->lock); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = kmx61_read_measurement(data, base, bit); if (ret < 0) { mutex_unlock(&data->lock); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 937ff9c5a74c..ed0267929725 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2127,25 +2127,15 @@ static const struct iio_info st_lsm6dsx_gyro_info = { .write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt, }; -static int st_lsm6dsx_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) -{ - struct device *dev = hw->dev; - - if (!dev_fwnode(dev)) - return -EINVAL; - - return device_property_read_u32(dev, "st,drdy-int-pin", drdy_pin); -} - static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, const struct st_lsm6dsx_reg **drdy_reg) { + struct device *dev = hw->dev; int err = 0, drdy_pin; - if (st_lsm6dsx_get_drdy_pin(hw, &drdy_pin) < 0) { + if (device_property_read_u32(dev, "st,drdy-int-pin", &drdy_pin) < 0) { struct st_sensors_platform_data *pdata; - struct device *dev = hw->dev; pdata = (struct st_sensors_platform_data *)dev->platform_data; drdy_pin = pdata ? pdata->drdy_int_pin : 1; @@ -2180,7 +2170,7 @@ static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) hub_settings = &hw->settings->shub_settings; pdata = (struct st_sensors_platform_data *)dev->platform_data; - if ((dev_fwnode(dev) && device_property_read_bool(dev, "st,pullups")) || + if (device_property_read_bool(dev, "st,pullups") || (pdata && pdata->pullups)) { if (hub_settings->pullup_en.sec_page) { err = st_lsm6dsx_set_page(hw, true); @@ -2565,7 +2555,7 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) return err; pdata = (struct st_sensors_platform_data *)dev->platform_data; - if ((dev_fwnode(dev) && device_property_read_bool(dev, "drive-open-drain")) || + if (device_property_read_bool(dev, "drive-open-drain") || (pdata && pdata->open_drain)) { reg = &hw->settings->irq_config.od; err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, @@ -2646,73 +2636,6 @@ static int st_lsm6dsx_init_regulators(struct device *dev) return 0; } -#ifdef CONFIG_ACPI - -static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, - struct iio_mount_matrix *orientation) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_device *adev = ACPI_COMPANION(dev); - union acpi_object *obj, *elements; - acpi_status status; - int i, j, val[3]; - char *str; - - if (!has_acpi_companion(dev)) - return -EINVAL; - - if (!acpi_has_method(adev->handle, "ROTM")) - return -EINVAL; - - status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer); - if (ACPI_FAILURE(status)) { - dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status); - return -EINVAL; - } - - obj = buffer.pointer; - if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) - goto unknown_format; - - elements = obj->package.elements; - for (i = 0; i < 3; i++) { - if (elements[i].type != ACPI_TYPE_STRING) - goto unknown_format; - - str = elements[i].string.pointer; - if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) - goto unknown_format; - - for (j = 0; j < 3; j++) { - switch (val[j]) { - case -1: str = "-1"; break; - case 0: str = "0"; break; - case 1: str = "1"; break; - default: goto unknown_format; - } - orientation->rotation[i * 3 + j] = str; - } - } - - kfree(buffer.pointer); - return 0; - -unknown_format: - dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n"); - kfree(buffer.pointer); - return -EINVAL; -} - -#else - -static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, - struct iio_mount_matrix *orientation) -{ - return -EOPNOTSUPP; -} - -#endif - int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { @@ -2760,8 +2683,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, hub_settings = &hw->settings->shub_settings; if (hub_settings->master_en.addr && - (!dev_fwnode(dev) || - !device_property_read_bool(dev, "st,disable-sensor-hub"))) { + !device_property_read_bool(dev, "st,disable-sensor-hub")) { err = st_lsm6dsx_shub_probe(hw, name); if (err < 0) return err; @@ -2787,8 +2709,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - err = lsm6dsx_get_acpi_mount_matrix(hw->dev, &hw->orientation); - if (err) { + if (!iio_read_acpi_mount_matrix(hw->dev, &hw->orientation, "ROTM")) { err = iio_read_mount_matrix(hw->dev, &hw->orientation); if (err) return err; @@ -2803,7 +2724,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - if ((dev_fwnode(dev) && device_property_read_bool(dev, "wakeup-source")) || + if (device_property_read_bool(dev, "wakeup-source") || (pdata && pdata->wakeup_source)) device_init_wakeup(dev, true); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index efe05be284b6..20b3b5212da7 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -32,6 +32,7 @@ #define dev_fmt(fmt) "iio-backend: " fmt #include <linux/cleanup.h> +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> @@ -40,6 +41,7 @@ #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> +#include <linux/stringify.h> #include <linux/types.h> #include <linux/iio/backend.h> @@ -52,6 +54,14 @@ struct iio_backend { struct device *dev; struct module *owner; void *priv; + const char *name; + unsigned int cached_reg_addr; + /* + * This index is relative to the frontend. Meaning that for + * frontends with multiple backends, this will be the index of this + * backend. Used for the debugfs directory name. + */ + u8 idx; }; /* @@ -111,7 +121,142 @@ static DEFINE_MUTEX(iio_back_lock); __ret = iio_backend_check_op(__back, op); \ if (!__ret) \ __back->ops->op(__back, ##args); \ + else \ + dev_dbg(__back->dev, "Op(%s) not implemented\n",\ + __stringify(op)); \ +} + +static ssize_t iio_backend_debugfs_read_reg(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct iio_backend *back = file->private_data; + char read_buf[20]; + unsigned int val; + int ret, len; + + ret = iio_backend_op_call(back, debugfs_reg_access, + back->cached_reg_addr, 0, &val); + if (ret) + return ret; + + len = scnprintf(read_buf, sizeof(read_buf), "0x%X\n", val); + + return simple_read_from_buffer(userbuf, count, ppos, read_buf, len); +} + +static ssize_t iio_backend_debugfs_write_reg(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct iio_backend *back = file->private_data; + unsigned int val; + char buf[80]; + ssize_t rc; + int ret; + + rc = simple_write_to_buffer(buf, sizeof(buf), ppos, userbuf, count); + if (rc < 0) + return rc; + + ret = sscanf(buf, "%i %i", &back->cached_reg_addr, &val); + + switch (ret) { + case 1: + return count; + case 2: + ret = iio_backend_op_call(back, debugfs_reg_access, + back->cached_reg_addr, val, NULL); + if (ret) + return ret; + return count; + default: + return -EINVAL; + } +} + +static const struct file_operations iio_backend_debugfs_reg_fops = { + .open = simple_open, + .read = iio_backend_debugfs_read_reg, + .write = iio_backend_debugfs_write_reg, +}; + +static ssize_t iio_backend_debugfs_read_name(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct iio_backend *back = file->private_data; + char name[128]; + int len; + + len = scnprintf(name, sizeof(name), "%s\n", back->name); + + return simple_read_from_buffer(userbuf, count, ppos, name, len); +} + +static const struct file_operations iio_backend_debugfs_name_fops = { + .open = simple_open, + .read = iio_backend_debugfs_read_name, +}; + +/** + * iio_backend_debugfs_add - Add debugfs interfaces for Backends + * @back: Backend device + * @indio_dev: IIO device + */ +void iio_backend_debugfs_add(struct iio_backend *back, + struct iio_dev *indio_dev) +{ + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + struct dentry *back_d; + char name[128]; + + if (!IS_ENABLED(CONFIG_DEBUG_FS) || !d) + return; + if (!back->ops->debugfs_reg_access && !back->name) + return; + + snprintf(name, sizeof(name), "backend%d", back->idx); + + back_d = debugfs_create_dir(name, d); + if (IS_ERR(back_d)) + return; + + if (back->ops->debugfs_reg_access) + debugfs_create_file("direct_reg_access", 0600, back_d, back, + &iio_backend_debugfs_reg_fops); + + if (back->name) + debugfs_create_file("name", 0400, back_d, back, + &iio_backend_debugfs_name_fops); } +EXPORT_SYMBOL_NS_GPL(iio_backend_debugfs_add, IIO_BACKEND); + +/** + * iio_backend_debugfs_print_chan_status - Print channel status + * @back: Backend device + * @chan: Channel number + * @buf: Buffer where to print the status + * @len: Available space + * + * One usecase where this is useful is for testing test tones in a digital + * interface and "ask" the backend to dump more details on why a test tone might + * have errors. + * + * RETURNS: + * Number of copied bytes on success, negative error code on failure. + */ +ssize_t iio_backend_debugfs_print_chan_status(struct iio_backend *back, + unsigned int chan, char *buf, + size_t len) +{ + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return -ENODEV; + + return iio_backend_op_call(back, debugfs_print_chan_status, chan, buf, + len); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_debugfs_print_chan_status, IIO_BACKEND); /** * iio_backend_chan_enable - Enable a backend channel @@ -147,6 +292,29 @@ static void __iio_backend_disable(void *back) } /** + * iio_backend_disable - Backend disable + * @back: Backend device + */ +void iio_backend_disable(struct iio_backend *back) +{ + __iio_backend_disable(back); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_disable, IIO_BACKEND); + +/** + * iio_backend_enable - Backend enable + * @back: Backend device + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_enable(struct iio_backend *back) +{ + return iio_backend_op_call(back, enable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_enable, IIO_BACKEND); + +/** * devm_iio_backend_enable - Device managed backend enable * @dev: Consumer device for the backend * @back: Backend device @@ -158,7 +326,7 @@ int devm_iio_backend_enable(struct device *dev, struct iio_backend *back) { int ret; - ret = iio_backend_op_call(back, enable); + ret = iio_backend_enable(back); if (ret) return ret; @@ -357,6 +525,25 @@ int devm_iio_backend_request_buffer(struct device *dev, } EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND); +/** + * iio_backend_read_raw - Read a channel attribute from a backend device. + * @back: Backend device + * @chan: IIO channel reference + * @val: First returned value + * @val2: Second returned value + * @mask: Specify the attribute to return + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_read_raw(struct iio_backend *back, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + return iio_backend_op_call(back, read_raw, chan, val, val2, mask); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_read_raw, IIO_BACKEND); + static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev) { struct iio_backend *back = ERR_PTR(-ENODEV), *iter; @@ -451,7 +638,6 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND); /** * iio_backend_extend_chan_spec - Extend an IIO channel - * @indio_dev: IIO device * @back: Backend device * @chan: IIO channel * @@ -461,8 +647,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND); * RETURNS: * 0 on success, negative error number on failure. */ -int iio_backend_extend_chan_spec(struct iio_dev *indio_dev, - struct iio_backend *back, +int iio_backend_extend_chan_spec(struct iio_backend *back, struct iio_chan_spec *chan) { const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info; @@ -533,19 +718,10 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) return 0; } -/** - * devm_iio_backend_get - Device managed backend device get - * @dev: Consumer device for the backend - * @name: Backend name - * - * Get's the backend associated with @dev. - * - * RETURNS: - * A backend pointer, negative error pointer otherwise. - */ -struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) +static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, + struct fwnode_handle *fwnode) { - struct fwnode_handle *fwnode; + struct fwnode_handle *fwnode_back; struct iio_backend *back; unsigned int index; int ret; @@ -560,30 +736,67 @@ struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) index = 0; } - fwnode = fwnode_find_reference(dev_fwnode(dev), "io-backends", index); + fwnode_back = fwnode_find_reference(fwnode, "io-backends", index); if (IS_ERR(fwnode)) return dev_err_cast_probe(dev, fwnode, "Cannot get Firmware reference\n"); guard(mutex)(&iio_back_lock); list_for_each_entry(back, &iio_back_list, entry) { - if (!device_match_fwnode(back->dev, fwnode)) + if (!device_match_fwnode(back->dev, fwnode_back)) continue; - fwnode_handle_put(fwnode); + fwnode_handle_put(fwnode_back); ret = __devm_iio_backend_get(dev, back); if (ret) return ERR_PTR(ret); + if (name) + back->idx = index; + return back; } - fwnode_handle_put(fwnode); + fwnode_handle_put(fwnode_back); return ERR_PTR(-EPROBE_DEFER); } + +/** + * devm_iio_backend_get - Device managed backend device get + * @dev: Consumer device for the backend + * @name: Backend name + * + * Get's the backend associated with @dev. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) +{ + return __devm_iio_backend_fwnode_get(dev, name, dev_fwnode(dev)); +} EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND); /** + * devm_iio_backend_fwnode_get - Device managed backend firmware node get + * @dev: Consumer device for the backend + * @name: Backend name + * @fwnode: Firmware node of the backend consumer + * + * Get's the backend associated with a firmware node. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, + const char *name, + struct fwnode_handle *fwnode) +{ + return __devm_iio_backend_fwnode_get(dev, name, fwnode); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_fwnode_get, IIO_BACKEND); + +/** * __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get * @dev: Consumer device for the backend * @fwnode: Firmware node of the backend device @@ -639,20 +852,20 @@ static void iio_backend_unregister(void *arg) /** * devm_iio_backend_register - Device managed backend device register * @dev: Backend device being registered - * @ops: Backend ops + * @info: Backend info * @priv: Device private data * - * @ops is mandatory. Not providing it results in -EINVAL. + * @info is mandatory. Not providing it results in -EINVAL. * * RETURNS: * 0 on success, negative error number on failure. */ int devm_iio_backend_register(struct device *dev, - const struct iio_backend_ops *ops, void *priv) + const struct iio_backend_info *info, void *priv) { struct iio_backend *back; - if (!ops) + if (!info || !info->ops) return dev_err_probe(dev, -EINVAL, "No backend ops given\n"); /* @@ -665,7 +878,8 @@ int devm_iio_backend_register(struct device *dev, if (!back) return -ENOMEM; - back->ops = ops; + back->ops = info->ops; + back->name = info->name; back->owner = dev->driver->owner; back->dev = dev; back->priv = priv; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index d6fe105d2f40..8104696cd475 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -508,18 +508,19 @@ static bool iio_validate_scan_mask(struct iio_dev *indio_dev, static int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit) { + unsigned int masklength = iio_get_masklength(indio_dev); const unsigned long *mask; unsigned long *trialmask; - if (!indio_dev->masklength) { + if (!masklength) { WARN(1, "Trying to set scanmask prior to registering buffer\n"); return -EINVAL; } - trialmask = bitmap_alloc(indio_dev->masklength, GFP_KERNEL); + trialmask = bitmap_alloc(masklength, GFP_KERNEL); if (!trialmask) return -ENOMEM; - bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); + bitmap_copy(trialmask, buffer->scan_mask, masklength); set_bit(bit, trialmask); if (!iio_validate_scan_mask(indio_dev, trialmask)) @@ -527,12 +528,11 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev, if (indio_dev->available_scan_masks) { mask = iio_scan_mask_match(indio_dev->available_scan_masks, - indio_dev->masklength, - trialmask, false); + masklength, trialmask, false); if (!mask) goto err_invalid_mask; } - bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength); + bitmap_copy(buffer->scan_mask, trialmask, masklength); bitmap_free(trialmask); @@ -552,7 +552,7 @@ static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit) static int iio_scan_mask_query(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit) { - if (bit > indio_dev->masklength) + if (bit > iio_get_masklength(indio_dev)) return -EINVAL; if (!buffer->scan_mask) @@ -768,8 +768,7 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, int length, i, largest = 0; /* How much space will the demuxed element take? */ - for_each_set_bit(i, mask, - indio_dev->masklength) { + for_each_set_bit(i, mask, iio_get_masklength(indio_dev)) { length = iio_storage_bytes_for_si(indio_dev, i); if (length < 0) return length; @@ -890,6 +889,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, struct iio_device_config *config) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + unsigned int masklength = iio_get_masklength(indio_dev); unsigned long *compound_mask; const unsigned long *scan_mask; bool strict_scanmask = false; @@ -898,7 +898,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, unsigned int modes; if (insert_buffer && - bitmap_empty(insert_buffer->scan_mask, indio_dev->masklength)) { + bitmap_empty(insert_buffer->scan_mask, masklength)) { dev_dbg(&indio_dev->dev, "At least one scan element must be enabled first\n"); return -EINVAL; @@ -952,7 +952,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, } /* What scan mask do we actually have? */ - compound_mask = bitmap_zalloc(indio_dev->masklength, GFP_KERNEL); + compound_mask = bitmap_zalloc(masklength, GFP_KERNEL); if (!compound_mask) return -ENOMEM; @@ -962,20 +962,19 @@ static int iio_verify_update(struct iio_dev *indio_dev, if (buffer == remove_buffer) continue; bitmap_or(compound_mask, compound_mask, buffer->scan_mask, - indio_dev->masklength); + masklength); scan_timestamp |= buffer->scan_timestamp; } if (insert_buffer) { bitmap_or(compound_mask, compound_mask, - insert_buffer->scan_mask, indio_dev->masklength); + insert_buffer->scan_mask, masklength); scan_timestamp |= insert_buffer->scan_timestamp; } if (indio_dev->available_scan_masks) { scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks, - indio_dev->masklength, - compound_mask, + masklength, compound_mask, strict_scanmask); bitmap_free(compound_mask); if (!scan_mask) @@ -1040,6 +1039,7 @@ static int iio_buffer_add_demux(struct iio_buffer *buffer, static int iio_buffer_update_demux(struct iio_dev *indio_dev, struct iio_buffer *buffer) { + unsigned int masklength = iio_get_masklength(indio_dev); int ret, in_ind = -1, out_ind, length; unsigned int in_loc = 0, out_loc = 0; struct iio_demux_table *p = NULL; @@ -1051,17 +1051,13 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, /* First work out which scan mode we will actually have */ if (bitmap_equal(indio_dev->active_scan_mask, - buffer->scan_mask, - indio_dev->masklength)) + buffer->scan_mask, masklength)) return 0; /* Now we have the two masks, work from least sig and build up sizes */ - for_each_set_bit(out_ind, - buffer->scan_mask, - indio_dev->masklength) { + for_each_set_bit(out_ind, buffer->scan_mask, masklength) { in_ind = find_next_bit(indio_dev->active_scan_mask, - indio_dev->masklength, - in_ind + 1); + masklength, in_ind + 1); while (in_ind != out_ind) { ret = iio_storage_bytes_for_si(indio_dev, in_ind); if (ret < 0) @@ -1071,8 +1067,7 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, /* Make sure we are aligned */ in_loc = roundup(in_loc, length) + length; in_ind = find_next_bit(indio_dev->active_scan_mask, - indio_dev->masklength, - in_ind + 1); + masklength, in_ind + 1); } ret = iio_storage_bytes_for_si(indio_dev, in_ind); if (ret < 0) @@ -2104,6 +2099,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, int index) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + unsigned int masklength = iio_get_masklength(indio_dev); struct iio_dev_attr *p; const struct iio_dev_attr *id_attr; struct attribute **attr; @@ -2166,8 +2162,8 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, iio_dev_opaque->scan_index_timestamp = channels[i].scan_index; } - if (indio_dev->masklength && !buffer->scan_mask) { - buffer->scan_mask = bitmap_zalloc(indio_dev->masklength, + if (masklength && !buffer->scan_mask) { + buffer->scan_mask = bitmap_zalloc(masklength, GFP_KERNEL); if (!buffer->scan_mask) { ret = -ENOMEM; @@ -2273,7 +2269,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) for (i = 0; i < indio_dev->num_channels; i++) ml = max(ml, channels[i].scan_index + 1); - indio_dev->masklength = ml; + ACCESS_PRIVATE(indio_dev, masklength) = ml; } if (!iio_dev_opaque->attached_buffers_cnt) @@ -2337,7 +2333,7 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, const unsigned long *mask) { - return bitmap_weight(mask, indio_dev->masklength) == 1; + return bitmap_weight(mask, iio_get_masklength(indio_dev)) == 1; } EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0f6cda7ffe45..6a6568d4a2cb 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -667,7 +667,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, vals[1]); case IIO_VAL_FRACTIONAL: tmp2 = div_s64((s64)vals[0] * 1000000000LL, vals[1]); - tmp1 = vals[1]; tmp0 = (int)div_s64_rem(tmp2, 1000000000, &tmp1); if ((tmp2 < 0) && (tmp0 == 0)) return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1)); @@ -1912,7 +1911,7 @@ static void iio_sanity_check_avail_scan_masks(struct iio_dev *indio_dev) int i; av_masks = indio_dev->available_scan_masks; - masklength = indio_dev->masklength; + masklength = iio_get_masklength(indio_dev); longs_per_mask = BITS_TO_LONGS(masklength); /* @@ -1965,6 +1964,49 @@ static void iio_sanity_check_avail_scan_masks(struct iio_dev *indio_dev) } } +/** + * iio_active_scan_mask_index - Get index of the active scan mask inside the + * available scan masks array + * @indio_dev: the IIO device containing the active and available scan masks + * + * Returns: the index or -EINVAL if active_scan_mask is not set + */ +int iio_active_scan_mask_index(struct iio_dev *indio_dev) + +{ + const unsigned long *av_masks; + unsigned int masklength = iio_get_masklength(indio_dev); + int i = 0; + + if (!indio_dev->active_scan_mask) + return -EINVAL; + + /* + * As in iio_scan_mask_match and iio_sanity_check_avail_scan_masks, + * the condition here do not handle multi-long masks correctly. + * It only checks the first long to be zero, and will use such mask + * as a terminator even if there was bits set after the first long. + * + * This should be fine since the available_scan_mask has already been + * sanity tested using iio_sanity_check_avail_scan_masks. + * + * See iio_scan_mask_match and iio_sanity_check_avail_scan_masks for + * more details + */ + av_masks = indio_dev->available_scan_masks; + while (*av_masks) { + if (indio_dev->active_scan_mask == av_masks) + return i; + av_masks += BITS_TO_LONGS(masklength); + i++; + } + + dev_warn(indio_dev->dev.parent, + "active scan mask is not part of the available scan masks\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(iio_active_scan_mask_index); + int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 2e84776f4fbd..54416a384232 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -347,6 +347,7 @@ int iio_trigger_detach_poll_func(struct iio_trigger *trig, iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); module_put(iio_dev_opaque->driver_module); + pf->irq = 0; return ret; } @@ -770,3 +771,29 @@ void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev) if (indio_dev->trig) iio_trigger_put(indio_dev->trig); } + +int iio_device_suspend_triggering(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + guard(mutex)(&iio_dev_opaque->mlock); + + if ((indio_dev->pollfunc) && (indio_dev->pollfunc->irq > 0)) + disable_irq(indio_dev->pollfunc->irq); + + return 0; +} +EXPORT_SYMBOL(iio_device_suspend_triggering); + +int iio_device_resume_triggering(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + guard(mutex)(&iio_dev_opaque->mlock); + + if ((indio_dev->pollfunc) && (indio_dev->pollfunc->irq > 0)) + enable_irq(indio_dev->pollfunc->irq); + + return 0; +} +EXPORT_SYMBOL(iio_device_resume_triggering); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index b68dcc1fbaca..515ff46b5b82 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -114,6 +114,19 @@ config AS73211 This driver can also be built as a module. If so, the module will be called as73211. +config BH1745 + tristate "ROHM BH1745 colour sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IIO_GTS_HELPER + help + Say Y here to build support for the ROHM bh1745 colour sensor. + + To compile this driver as a module, choose M here: the module will + be called bh1745. + config BH1750 tristate "ROHM BH1750 ambient light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 1a071a8e9f8e..321010fc0b93 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9306) += apds9306.o obj-$(CONFIG_APDS9960) += apds9960.o obj-$(CONFIG_AS73211) += as73211.o +obj-$(CONFIG_BH1745) += bh1745.o obj-$(CONFIG_BH1750) += bh1750.o obj-$(CONFIG_BH1780) += bh1780.o obj-$(CONFIG_CM32181) += cm32181.o diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 5169f12c3eba..c1b43053fbc7 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -125,8 +125,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) if (ret < 0) goto done; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { ret = i2c_smbus_read_word_data(data->client, ADJD_S311_DATA_REG(i)); if (ret < 0) diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index e9e65130b6f9..3c14e4c30805 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -146,6 +146,25 @@ struct apds9960_data { /* gesture buffer */ u8 buffer[4]; /* 4 8-bit channels */ + + /* calibration value buffer */ + int calibbias[5]; +}; + +enum { + APDS9960_CHAN_PROXIMITY, + APDS9960_CHAN_GESTURE_UP, + APDS9960_CHAN_GESTURE_DOWN, + APDS9960_CHAN_GESTURE_LEFT, + APDS9960_CHAN_GESTURE_RIGHT, +}; + +static const unsigned int apds9960_offset_regs[][2] = { + [APDS9960_CHAN_PROXIMITY] = {APDS9960_REG_POFFSET_UR, APDS9960_REG_POFFSET_DL}, + [APDS9960_CHAN_GESTURE_UP] = {APDS9960_REG_GOFFSET_U, 0}, + [APDS9960_CHAN_GESTURE_DOWN] = {APDS9960_REG_GOFFSET_D, 0}, + [APDS9960_CHAN_GESTURE_LEFT] = {APDS9960_REG_GOFFSET_L, 0}, + [APDS9960_CHAN_GESTURE_RIGHT] = {APDS9960_REG_GOFFSET_R, 0}, }; static const struct reg_default apds9960_reg_defaults[] = { @@ -255,6 +274,7 @@ static const struct iio_event_spec apds9960_als_event_spec[] = { #define APDS9960_GESTURE_CHANNEL(_dir, _si) { \ .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS), \ .channel = _si + 1, \ .scan_index = _si, \ .indexed = 1, \ @@ -282,7 +302,8 @@ static const struct iio_chan_spec apds9960_channels[] = { { .type = IIO_PROXIMITY, .address = APDS9960_REG_PDATA, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBBIAS), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .channel = 0, .indexed = 0, @@ -316,6 +337,28 @@ static const struct iio_chan_spec apds9960_channels[] = { APDS9960_INTENSITY_CHANNEL(BLUE), }; +static int apds9960_set_calibbias(struct apds9960_data *data, + struct iio_chan_spec const *chan, int calibbias) +{ + int ret, i; + + if (calibbias < S8_MIN || calibbias > S8_MAX) + return -EINVAL; + + guard(mutex)(&data->lock); + for (i = 0; i < 2; i++) { + if (apds9960_offset_regs[chan->channel][i] == 0) + break; + + ret = regmap_write(data->regmap, apds9960_offset_regs[chan->channel][i], calibbias); + if (ret < 0) + return ret; + } + data->calibbias[chan->channel] = calibbias; + + return 0; +} + /* integration time in us */ static const int apds9960_int_time[][2] = { { 28000, 246}, @@ -531,6 +574,12 @@ static int apds9960_read_raw(struct iio_dev *indio_dev, } mutex_unlock(&data->lock); break; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&data->lock); + *val = data->calibbias[chan->channel]; + ret = IIO_VAL_INT; + mutex_unlock(&data->lock); + break; } return ret; @@ -564,6 +613,10 @@ static int apds9960_write_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_CALIBBIAS: + if (val2 != 0) + return -EINVAL; + return apds9960_set_calibbias(data, chan, val); default: return -EINVAL; } diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c new file mode 100644 index 000000000000..2e458e9d5d85 --- /dev/null +++ b/drivers/iio/light/bh1745.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ROHM BH1745 digital colour sensor driver + * + * Copyright (C) Mudit Sharma <muditsharma.info@gmail.com> + * + * 7-bit I2C slave addresses: + * 0x38 (ADDR pin low) + * 0x39 (ADDR pin high) + */ + +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/util_macros.h> +#include <linux/iio/events.h> +#include <linux/regmap.h> +#include <linux/bits.h> +#include <linux/bitfield.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/iio-gts-helper.h> + +/* BH1745 configuration registers */ + +/* System control */ +#define BH1745_SYS_CTRL 0x40 +#define BH1745_SYS_CTRL_SW_RESET BIT(7) +#define BH1745_SYS_CTRL_INTR_RESET BIT(6) +#define BH1745_SYS_CTRL_PART_ID_MASK GENMASK(5, 0) +#define BH1745_PART_ID 0x0B + +/* Mode control 1 */ +#define BH1745_MODE_CTRL1 0x41 +#define BH1745_CTRL1_MEASUREMENT_TIME_MASK GENMASK(2, 0) + +/* Mode control 2 */ +#define BH1745_MODE_CTRL2 0x42 +#define BH1745_CTRL2_RGBC_EN BIT(4) +#define BH1745_CTRL2_ADC_GAIN_MASK GENMASK(1, 0) + +/* Interrupt */ +#define BH1745_INTR 0x60 +#define BH1745_INTR_STATUS BIT(7) +#define BH1745_INTR_SOURCE_MASK GENMASK(3, 2) +#define BH1745_INTR_ENABLE BIT(0) + +#define BH1745_PERSISTENCE 0x61 + +/* Threshold high */ +#define BH1745_TH_LSB 0x62 +#define BH1745_TH_MSB 0x63 + +/* Threshold low */ +#define BH1745_TL_LSB 0x64 +#define BH1745_TL_MSB 0x65 + +/* BH1745 data output regs */ +#define BH1745_RED_LSB 0x50 +#define BH1745_RED_MSB 0x51 +#define BH1745_GREEN_LSB 0x52 +#define BH1745_GREEN_MSB 0x53 +#define BH1745_BLUE_LSB 0x54 +#define BH1745_BLUE_MSB 0x55 +#define BH1745_CLEAR_LSB 0x56 +#define BH1745_CLEAR_MSB 0x57 + +#define BH1745_MANU_ID_REG 0x92 + +/* From 16x max HW gain and 32x max integration time */ +#define BH1745_MAX_GAIN 512 + +enum bh1745_int_source { + BH1745_INTR_SOURCE_RED, + BH1745_INTR_SOURCE_GREEN, + BH1745_INTR_SOURCE_BLUE, + BH1745_INTR_SOURCE_CLEAR, +}; + +enum bh1745_gain { + BH1745_ADC_GAIN_1X, + BH1745_ADC_GAIN_2X, + BH1745_ADC_GAIN_16X, +}; + +enum bh1745_measurement_time { + BH1745_MEASUREMENT_TIME_160MS, + BH1745_MEASUREMENT_TIME_320MS, + BH1745_MEASUREMENT_TIME_640MS, + BH1745_MEASUREMENT_TIME_1280MS, + BH1745_MEASUREMENT_TIME_2560MS, + BH1745_MEASUREMENT_TIME_5120MS, +}; + +enum bh1745_presistence_value { + BH1745_PRESISTENCE_UPDATE_TOGGLE, + BH1745_PRESISTENCE_UPDATE_EACH_MEASUREMENT, + BH1745_PRESISTENCE_UPDATE_FOUR_MEASUREMENT, + BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT, +}; + +static const struct iio_gain_sel_pair bh1745_gain[] = { + GAIN_SCALE_GAIN(1, BH1745_ADC_GAIN_1X), + GAIN_SCALE_GAIN(2, BH1745_ADC_GAIN_2X), + GAIN_SCALE_GAIN(16, BH1745_ADC_GAIN_16X), +}; + +static const struct iio_itime_sel_mul bh1745_itimes[] = { + GAIN_SCALE_ITIME_US(5120000, BH1745_MEASUREMENT_TIME_5120MS, 32), + GAIN_SCALE_ITIME_US(2560000, BH1745_MEASUREMENT_TIME_2560MS, 16), + GAIN_SCALE_ITIME_US(1280000, BH1745_MEASUREMENT_TIME_1280MS, 8), + GAIN_SCALE_ITIME_US(640000, BH1745_MEASUREMENT_TIME_640MS, 4), + GAIN_SCALE_ITIME_US(320000, BH1745_MEASUREMENT_TIME_320MS, 2), + GAIN_SCALE_ITIME_US(160000, BH1745_MEASUREMENT_TIME_160MS, 1), +}; + +struct bh1745_data { + /* + * Lock to prevent device setting update or read before + * related calculations are completed + */ + struct mutex lock; + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + struct iio_gts gts; +}; + +static const struct regmap_range bh1745_volatile_ranges[] = { + regmap_reg_range(BH1745_MODE_CTRL2, BH1745_MODE_CTRL2), /* VALID */ + regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB), /* Data */ + regmap_reg_range(BH1745_INTR, BH1745_INTR), /* Interrupt */ +}; + +static const struct regmap_access_table bh1745_volatile_regs = { + .yes_ranges = bh1745_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(bh1745_volatile_ranges), +}; + +static const struct regmap_range bh1745_readable_ranges[] = { + regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2), + regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB), + regmap_reg_range(BH1745_INTR, BH1745_INTR), + regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB), + regmap_reg_range(BH1745_MANU_ID_REG, BH1745_MANU_ID_REG), +}; + +static const struct regmap_access_table bh1745_readable_regs = { + .yes_ranges = bh1745_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(bh1745_readable_ranges), +}; + +static const struct regmap_range bh1745_writable_ranges[] = { + regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2), + regmap_reg_range(BH1745_INTR, BH1745_INTR), + regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB), +}; + +static const struct regmap_access_table bh1745_writable_regs = { + .yes_ranges = bh1745_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(bh1745_writable_ranges), +}; + +static const struct regmap_config bh1745_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BH1745_MANU_ID_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bh1745_volatile_regs, + .wr_table = &bh1745_writable_regs, + .rd_table = &bh1745_readable_regs, +}; + +static const struct iio_event_spec bh1745_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_type = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define BH1745_CHANNEL(_colour, _si, _addr) \ + { \ + .type = IIO_INTENSITY, .modified = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .event_spec = bh1745_event_spec, \ + .num_event_specs = ARRAY_SIZE(bh1745_event_spec), \ + .channel2 = IIO_MOD_LIGHT_##_colour, .address = _addr, \ + .scan_index = _si, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + } + +static const struct iio_chan_spec bh1745_channels[] = { + BH1745_CHANNEL(RED, 0, BH1745_RED_LSB), + BH1745_CHANNEL(GREEN, 1, BH1745_GREEN_LSB), + BH1745_CHANNEL(BLUE, 2, BH1745_BLUE_LSB), + BH1745_CHANNEL(CLEAR, 3, BH1745_CLEAR_LSB), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int bh1745_reset(struct bh1745_data *data) +{ + return regmap_set_bits(data->regmap, BH1745_SYS_CTRL, + BH1745_SYS_CTRL_SW_RESET | + BH1745_SYS_CTRL_INTR_RESET); +} + +static int bh1745_power_on(struct bh1745_data *data) +{ + return regmap_set_bits(data->regmap, BH1745_MODE_CTRL2, + BH1745_CTRL2_RGBC_EN); +} + +static void bh1745_power_off(void *data_ptr) +{ + struct bh1745_data *data = data_ptr; + struct device *dev = data->dev; + int ret; + + ret = regmap_clear_bits(data->regmap, BH1745_MODE_CTRL2, + BH1745_CTRL2_RGBC_EN); + if (ret) + dev_err(dev, "Failed to turn off device\n"); +} + +static int bh1745_get_scale(struct bh1745_data *data, int *val, int *val2) +{ + int ret; + int value; + int gain_sel, int_time_sel; + int gain; + const struct iio_itime_sel_mul *int_time; + + ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value); + if (ret) + return ret; + + gain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value); + gain = iio_gts_find_gain_by_sel(&data->gts, gain_sel); + + ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value); + if (ret) + return ret; + + int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value); + int_time = iio_gts_find_itime_by_sel(&data->gts, int_time_sel); + + return iio_gts_get_scale(&data->gts, gain, int_time->time_us, val, + val2); +} + +static int bh1745_set_scale(struct bh1745_data *data, int val) +{ + struct device *dev = data->dev; + int ret; + int value; + int hw_gain_sel, current_int_time_sel, new_int_time_sel; + + ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value); + if (ret) + return ret; + + current_int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, + value); + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, + current_int_time_sel, + val, 0, &hw_gain_sel); + if (ret) { + for (int i = 0; i < data->gts.num_itime; i++) { + new_int_time_sel = data->gts.itime_table[i].sel; + + if (new_int_time_sel == current_int_time_sel) + continue; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, + new_int_time_sel, + val, 0, + &hw_gain_sel); + if (!ret) + break; + } + + if (ret) { + dev_dbg(dev, "Unsupported scale value requested: %d\n", + val); + return -EINVAL; + } + + ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL1, + BH1745_CTRL1_MEASUREMENT_TIME_MASK, + new_int_time_sel); + if (ret) + return ret; + } + + return regmap_write_bits(data->regmap, BH1745_MODE_CTRL2, + BH1745_CTRL2_ADC_GAIN_MASK, hw_gain_sel); +} + +static int bh1745_get_int_time(struct bh1745_data *data, int *val) +{ + int ret; + int value; + int int_time, int_time_sel; + + ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value); + if (ret) + return ret; + + int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value); + int_time = iio_gts_find_int_time_by_sel(&data->gts, int_time_sel); + if (int_time < 0) + return int_time; + + *val = int_time; + + return 0; +} + +static int bh1745_set_int_time(struct bh1745_data *data, int val, int val2) +{ + struct device *dev = data->dev; + int ret; + int value; + int current_int_time, current_hwgain_sel, current_hwgain; + int new_hwgain, new_hwgain_sel, new_int_time_sel; + int req_int_time = (1000000 * val) + val2; + + if (!iio_gts_valid_time(&data->gts, req_int_time)) { + dev_dbg(dev, "Unsupported integration time requested: %d\n", + req_int_time); + return -EINVAL; + } + + ret = bh1745_get_int_time(data, ¤t_int_time); + if (ret) + return ret; + + if (current_int_time == req_int_time) + return 0; + + ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value); + if (ret) + return ret; + + current_hwgain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value); + current_hwgain = iio_gts_find_gain_by_sel(&data->gts, + current_hwgain_sel); + ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, current_hwgain, + current_int_time, + req_int_time, + &new_hwgain); + if (new_hwgain < 0) { + dev_dbg(dev, "No corresponding gain for requested integration time\n"); + return ret; + } + + if (ret) { + bool in_range; + + new_hwgain = iio_find_closest_gain_low(&data->gts, new_hwgain, + &in_range); + if (new_hwgain < 0) { + new_hwgain = iio_gts_get_min_gain(&data->gts); + if (new_hwgain < 0) + return ret; + } + + if (!in_range) + dev_dbg(dev, "Optimal gain out of range\n"); + + dev_dbg(dev, "Scale changed, new hw_gain %d\n", new_hwgain); + } + + new_hwgain_sel = iio_gts_find_sel_by_gain(&data->gts, new_hwgain); + if (new_hwgain_sel < 0) + return new_hwgain_sel; + + ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL2, + BH1745_CTRL2_ADC_GAIN_MASK, + new_hwgain_sel); + if (ret) + return ret; + + new_int_time_sel = iio_gts_find_sel_by_int_time(&data->gts, + req_int_time); + if (new_int_time_sel < 0) + return new_int_time_sel; + + return regmap_write_bits(data->regmap, BH1745_MODE_CTRL1, + BH1745_CTRL1_MEASUREMENT_TIME_MASK, + new_int_time_sel); +} + +static int bh1745_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bh1745_data *data = iio_priv(indio_dev); + int ret; + int value; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = regmap_bulk_read(data->regmap, chan->address, + &value, 2); + if (ret) + return ret; + *val = value; + + return IIO_VAL_INT; + } + unreachable(); + + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->lock); + ret = bh1745_get_scale(data, val, val2); + if (ret) + return ret; + + return IIO_VAL_INT; + } + + case IIO_CHAN_INFO_INT_TIME: { + guard(mutex)(&data->lock); + *val = 0; + ret = bh1745_get_int_time(data, val2); + if (ret) + return 0; + + return IIO_VAL_INT_PLUS_MICRO; + } + + default: + return -EINVAL; + } +} + +static int bh1745_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bh1745_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return bh1745_set_scale(data, val); + + case IIO_CHAN_INFO_INT_TIME: + return bh1745_set_int_time(data, val, val2); + + default: + return -EINVAL; + } +} + +static int bh1745_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; + + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int bh1745_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct bh1745_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_bulk_read(data->regmap, BH1745_TH_LSB, + val, 2); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_EV_DIR_FALLING: + ret = regmap_bulk_read(data->regmap, BH1745_TL_LSB, + val, 2); + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_EV_INFO_PERIOD: + ret = regmap_read(data->regmap, BH1745_PERSISTENCE, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int bh1745_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct bh1745_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0x0 || val > 0xFFFF) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_bulk_write(data->regmap, BH1745_TH_LSB, + &val, 2); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_EV_DIR_FALLING: + ret = regmap_bulk_write(data->regmap, BH1745_TL_LSB, + &val, 2); + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_EV_INFO_PERIOD: + if (val < BH1745_PRESISTENCE_UPDATE_TOGGLE || + val > BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT) + return -EINVAL; + ret = regmap_write(data->regmap, BH1745_PERSISTENCE, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int bh1745_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct bh1745_data *data = iio_priv(indio_dev); + int ret; + int value; + int int_src; + + ret = regmap_read(data->regmap, BH1745_INTR, &value); + if (ret) + return ret; + + if (!FIELD_GET(BH1745_INTR_ENABLE, value)) + return 0; + + int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value); + + switch (chan->channel2) { + case IIO_MOD_LIGHT_RED: + if (int_src == BH1745_INTR_SOURCE_RED) + return 1; + return 0; + + case IIO_MOD_LIGHT_GREEN: + if (int_src == BH1745_INTR_SOURCE_GREEN) + return 1; + return 0; + + case IIO_MOD_LIGHT_BLUE: + if (int_src == BH1745_INTR_SOURCE_BLUE) + return 1; + return 0; + + case IIO_MOD_LIGHT_CLEAR: + if (int_src == BH1745_INTR_SOURCE_CLEAR) + return 1; + return 0; + + default: + return -EINVAL; + } +} + +static int bh1745_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct bh1745_data *data = iio_priv(indio_dev); + int value; + + if (state == 0) + return regmap_clear_bits(data->regmap, + BH1745_INTR, BH1745_INTR_ENABLE); + + if (state == 1) { + /* Latch is always enabled when enabling interrupt */ + value = BH1745_INTR_ENABLE; + + switch (chan->channel2) { + case IIO_MOD_LIGHT_RED: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_RED)); + + case IIO_MOD_LIGHT_GREEN: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_GREEN)); + + case IIO_MOD_LIGHT_BLUE: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_BLUE)); + + case IIO_MOD_LIGHT_CLEAR: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_CLEAR)); + + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int bh1745_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct bh1745_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + + default: + return -EINVAL; + } +} + +static const struct iio_info bh1745_info = { + .read_raw = bh1745_read_raw, + .write_raw = bh1745_write_raw, + .write_raw_get_fmt = bh1745_write_raw_get_fmt, + .read_event_value = bh1745_read_thresh, + .write_event_value = bh1745_write_thresh, + .read_event_config = bh1745_read_event_config, + .write_event_config = bh1745_write_event_config, + .read_avail = bh1745_read_avail, +}; + +static irqreturn_t bh1745_interrupt_handler(int interrupt, void *p) +{ + struct iio_dev *indio_dev = p; + struct bh1745_data *data = iio_priv(indio_dev); + int ret; + int value; + int int_src; + + ret = regmap_read(data->regmap, BH1745_INTR, &value); + if (ret) + return IRQ_NONE; + + int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value); + + if (value & BH1745_INTR_STATUS) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, int_src, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t bh1745_trigger_handler(int interrupt, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bh1745_data *data = iio_priv(indio_dev); + struct { + u16 chans[4]; + s64 timestamp __aligned(8); + } scan; + u16 value; + int ret; + int i; + int j = 0; + + iio_for_each_active_channel(indio_dev, i) { + ret = regmap_bulk_read(data->regmap, BH1745_RED_LSB + 2 * i, + &value, 2); + if (ret) + goto err; + + scan.chans[j++] = value; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, + iio_get_time_ns(indio_dev)); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev, + struct device *parent, + int irq) +{ + struct bh1745_data *data = iio_priv(indio_dev); + struct device *dev = data->dev; + int ret; + + ret = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL, + bh1745_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Triggered buffer setup failed\n"); + + if (irq) { + ret = devm_request_threaded_irq(dev, irq, NULL, + bh1745_interrupt_handler, + IRQF_ONESHOT, + "bh1745_interrupt", indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Request for IRQ failed\n"); + } + + return 0; +} + +static int bh1745_init(struct bh1745_data *data) +{ + int ret; + struct device *dev = data->dev; + + mutex_init(&data->lock); + + ret = devm_iio_init_iio_gts(dev, BH1745_MAX_GAIN, 0, bh1745_gain, + ARRAY_SIZE(bh1745_gain), bh1745_itimes, + ARRAY_SIZE(bh1745_itimes), &data->gts); + if (ret) + return ret; + + ret = bh1745_reset(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset sensor\n"); + + ret = bh1745_power_on(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to turn on sensor\n"); + + ret = devm_add_action_or_reset(dev, bh1745_power_off, data); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add action or reset\n"); + + return 0; +} + +static int bh1745_probe(struct i2c_client *client) +{ + int ret; + int value; + int part_id; + struct bh1745_data *data; + struct iio_dev *indio_dev; + struct device *dev = &client->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &bh1745_info; + indio_dev->name = "bh1745"; + indio_dev->channels = bh1745_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = ARRAY_SIZE(bh1745_channels); + data = iio_priv(indio_dev); + data->dev = &client->dev; + data->regmap = devm_regmap_init_i2c(client, &bh1745_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to initialize Regmap\n"); + + ret = regmap_read(data->regmap, BH1745_SYS_CTRL, &value); + if (ret) + return ret; + + part_id = FIELD_GET(BH1745_SYS_CTRL_PART_ID_MASK, value); + if (part_id != BH1745_PART_ID) + dev_warn(dev, "Unknown part ID 0x%x\n", part_id); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable regulator\n"); + + ret = bh1745_init(data); + if (ret) + return ret; + + ret = bh1745_setup_triggered_buffer(indio_dev, indio_dev->dev.parent, + client->irq); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register device\n"); + + return 0; +} + +static const struct i2c_device_id bh1745_idtable[] = { + { "bh1745" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bh1745_idtable); + +static const struct of_device_id bh1745_of_match[] = { + { .compatible = "rohm,bh1745" }, + { } +}; +MODULE_DEVICE_TABLE(of, bh1745_of_match); + +static struct i2c_driver bh1745_driver = { + .driver = { + .name = "bh1745", + .of_match_table = bh1745_of_match, + }, + .probe = bh1745_probe, + .id_table = bh1745_idtable, +}; +module_i2c_driver(bh1745_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mudit Sharma <muditsharma.info@gmail.com>"); +MODULE_DESCRIPTION("BH1745 colour sensor driver"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 7125e011a38a..f8b1d7dd6f5f 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -420,7 +420,7 @@ static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg, return i2c_smbus_write_byte_data(i2c, reg, val); } -static struct regmap_bus gp2ap002_regmap_bus = { +static const struct regmap_bus gp2ap002_regmap_bus = { .reg_read = gp2ap002_regmap_i2c_read, .reg_write = gp2ap002_regmap_i2c_write, }; diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 757383456da6..b3f87dded040 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -965,8 +965,7 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) size_t d_size = 0; int i, out_val, ret; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_DATA_REG(i), &priv->buffer[d_size], 2); @@ -1397,8 +1396,7 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) * two separate IIO channels they are treated in the driver logic * as if they were controlled independently. */ - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { switch (i) { case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: err = gp2ap020a00f_exec_cmd(data, @@ -1435,8 +1433,7 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) mutex_lock(&data->lock); - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { switch (i) { case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: err = gp2ap020a00f_exec_cmd(data, diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index 59329546df58..b176bf4c884b 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -181,8 +181,7 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p) struct isl29125_data *data = iio_priv(indio_dev); int i, j = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { int ret = i2c_smbus_read_word_data(data->client, isl29125_regs[i].data); if (ret < 0) diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index fff1e899097d..7e58b50f3660 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -23,20 +23,30 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/regmap.h> +#include <linux/bitfield.h> #include <linux/iio/iio.h> #include <asm/unaligned.h> -#define LTR390_MAIN_CTRL 0x00 -#define LTR390_PART_ID 0x06 -#define LTR390_UVS_DATA 0x10 +#define LTR390_MAIN_CTRL 0x00 +#define LTR390_ALS_UVS_MEAS_RATE 0x04 +#define LTR390_ALS_UVS_GAIN 0x05 +#define LTR390_PART_ID 0x06 +#define LTR390_ALS_DATA 0x0D +#define LTR390_UVS_DATA 0x10 +#define LTR390_INT_CFG 0x19 + +#define LTR390_PART_NUMBER_ID 0xb +#define LTR390_ALS_UVS_GAIN_MASK 0x07 +#define LTR390_ALS_UVS_INT_TIME_MASK 0x70 +#define LTR390_ALS_UVS_INT_TIME(x) FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x)) #define LTR390_SW_RESET BIT(4) #define LTR390_UVS_MODE BIT(3) #define LTR390_SENSOR_ENABLE BIT(1) -#define LTR390_PART_NUMBER_ID 0xb +#define LTR390_FRACTIONAL_PRECISION 100 /* * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of @@ -55,11 +65,19 @@ */ #define LTR390_WINDOW_FACTOR 1 +enum ltr390_mode { + LTR390_SET_ALS_MODE, + LTR390_SET_UVS_MODE, +}; + struct ltr390_data { struct regmap *regmap; struct i2c_client *client; /* Protects device from simulataneous reads */ struct mutex lock; + enum ltr390_mode mode; + int gain; + int int_time_us; }; static const struct regmap_config ltr390_regmap_config = { @@ -75,8 +93,6 @@ static int ltr390_register_read(struct ltr390_data *data, u8 register_address) int ret; u8 recieve_buffer[3]; - guard(mutex)(&data->lock); - ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer, sizeof(recieve_buffer)); if (ret) { @@ -87,6 +103,38 @@ static int ltr390_register_read(struct ltr390_data *data, u8 register_address) return get_unaligned_le24(recieve_buffer); } +static int ltr390_set_mode(struct ltr390_data *data, enum ltr390_mode mode) +{ + int ret; + + if (data->mode == mode) + return 0; + + switch (mode) { + case LTR390_SET_ALS_MODE: + ret = regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE); + break; + + case LTR390_SET_UVS_MODE: + ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE); + break; + } + + if (ret) + return ret; + + data->mode = mode; + return 0; +} + +static int ltr390_counts_per_uvi(struct ltr390_data *data) +{ + const int orig_gain = 18; + const int orig_int_time = 400; + + return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time); +} + static int ltr390_read_raw(struct iio_dev *iio_device, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -94,29 +142,174 @@ static int ltr390_read_raw(struct iio_dev *iio_device, int ret; struct ltr390_data *data = iio_priv(iio_device); + guard(mutex)(&data->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - ret = ltr390_register_read(data, LTR390_UVS_DATA); - if (ret < 0) - return ret; + switch (chan->type) { + case IIO_UVINDEX: + ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE); + if (ret < 0) + return ret; + + ret = ltr390_register_read(data, LTR390_UVS_DATA); + if (ret < 0) + return ret; + break; + + case IIO_LIGHT: + ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE); + if (ret < 0) + return ret; + + ret = ltr390_register_read(data, LTR390_ALS_DATA); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = LTR390_WINDOW_FACTOR; - *val2 = LTR390_COUNTS_PER_UVI; - return IIO_VAL_FRACTIONAL; + switch (chan->type) { + case IIO_UVINDEX: + *val = LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION; + *val2 = ltr390_counts_per_uvi(data); + return IIO_VAL_FRACTIONAL; + + case IIO_LIGHT: + *val = LTR390_WINDOW_FACTOR * 6 * 100; + *val2 = data->gain * data->int_time_us; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_INT_TIME: + *val = data->int_time_us; + return IIO_VAL_INT; + default: return -EINVAL; } } -static const struct iio_info ltr390_info = { - .read_raw = ltr390_read_raw, +/* integration time in us */ +static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 }; +static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 }; + +static const struct iio_chan_spec ltr390_channels[] = { + /* UV sensor */ + { + .type = IIO_UVINDEX, + .scan_index = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE) + }, + /* ALS sensor */ + { + .type = IIO_LIGHT, + .scan_index = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE) + }, }; -static const struct iio_chan_spec ltr390_channel = { - .type = IIO_UVINDEX, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) +static int ltr390_set_gain(struct ltr390_data *data, int val) +{ + int ret, idx; + + for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) { + if (ltr390_gain_map[idx] != val) + continue; + + guard(mutex)(&data->lock); + ret = regmap_update_bits(data->regmap, + LTR390_ALS_UVS_GAIN, + LTR390_ALS_UVS_GAIN_MASK, idx); + if (ret) + return ret; + + data->gain = ltr390_gain_map[idx]; + return 0; + } + + return -EINVAL; +} + +static int ltr390_set_int_time(struct ltr390_data *data, int val) +{ + int ret, idx; + + for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) { + if (ltr390_int_time_map_us[idx] != val) + continue; + + guard(mutex)(&data->lock); + ret = regmap_update_bits(data->regmap, + LTR390_ALS_UVS_MEAS_RATE, + LTR390_ALS_UVS_INT_TIME_MASK, + LTR390_ALS_UVS_INT_TIME(idx)); + if (ret) + return ret; + + data->int_time_us = ltr390_int_time_map_us[idx]; + return 0; + } + + return -EINVAL; +} + +static int ltr390_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: + *length = ARRAY_SIZE(ltr390_gain_map); + *type = IIO_VAL_INT; + *vals = ltr390_gain_map; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(ltr390_int_time_map_us); + *type = IIO_VAL_INT; + *vals = ltr390_int_time_map_us; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltr390_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val2 != 0) + return -EINVAL; + + return ltr390_set_gain(data, val); + + case IIO_CHAN_INFO_INT_TIME: + if (val2 != 0) + return -EINVAL; + + return ltr390_set_int_time(data, val); + + default: + return -EINVAL; + } +} + +static const struct iio_info ltr390_info = { + .read_raw = ltr390_read_raw, + .write_raw = ltr390_write_raw, + .read_avail = ltr390_read_avail, }; static int ltr390_probe(struct i2c_client *client) @@ -139,11 +332,18 @@ static int ltr390_probe(struct i2c_client *client) "regmap initialization failed\n"); data->client = client; + /* default value of integration time from pg: 15 of the datasheet */ + data->int_time_us = 100000; + /* default value of gain from pg: 16 of the datasheet */ + data->gain = 3; + /* default mode for ltr390 is ALS mode */ + data->mode = LTR390_SET_ALS_MODE; + mutex_init(&data->lock); indio_dev->info = <r390_info; - indio_dev->channels = <r390_channel; - indio_dev->num_channels = 1; + indio_dev->channels = ltr390_channels; + indio_dev->num_channels = ARRAY_SIZE(ltr390_channels); indio_dev->name = "ltr390"; ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number); @@ -161,8 +361,7 @@ static int ltr390_probe(struct i2c_client *client) /* Wait for the registers to reset before proceeding */ usleep_range(1000, 2000); - ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, - LTR390_SENSOR_ENABLE | LTR390_UVS_MODE); + ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE); if (ret) return dev_err_probe(dev, ret, "failed to enable the sensor\n"); diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 68dc48420a88..bc8444516689 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -68,6 +68,13 @@ static const int ltrf216a_int_time_reg[][2] = { { 25, 0x40 }, }; +struct ltr_chip_info { + /* Chip contains CLEAR_DATA_0/1/2 registers at offset 0xa..0xc */ + bool has_clear_data; + /* Lux calculation multiplier for ALS data */ + int lux_multiplier; +}; + /* * Window Factor is needed when the device is under Window glass * with coated tinted ink. This is to compensate for the light loss @@ -79,6 +86,7 @@ static const int ltrf216a_int_time_reg[][2] = { struct ltrf216a_data { struct regmap *regmap; struct i2c_client *client; + const struct ltr_chip_info *info; u32 int_time; u16 int_time_fac; u8 als_gain_fac; @@ -246,7 +254,7 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data) ltrf216a_set_power_state(data, false); - lux = greendata * 45 * LTRF216A_WIN_FAC; + lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC; return lux; } @@ -334,15 +342,15 @@ static const struct iio_info ltrf216a_info = { static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg) { + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ltrf216a_data *data = iio_priv(indio_dev); + switch (reg) { case LTRF216A_MAIN_CTRL: case LTRF216A_ALS_MEAS_RES: case LTRF216A_ALS_GAIN: case LTRF216A_PART_ID: case LTRF216A_MAIN_STATUS: - case LTRF216A_ALS_CLEAR_DATA_0: - case LTRF216A_ALS_CLEAR_DATA_1: - case LTRF216A_ALS_CLEAR_DATA_2: case LTRF216A_ALS_DATA_0: case LTRF216A_ALS_DATA_1: case LTRF216A_ALS_DATA_2: @@ -355,6 +363,10 @@ static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg) case LTRF216A_ALS_THRES_LOW_1: case LTRF216A_ALS_THRES_LOW_2: return true; + case LTRF216A_ALS_CLEAR_DATA_0: + case LTRF216A_ALS_CLEAR_DATA_1: + case LTRF216A_ALS_CLEAR_DATA_2: + return data->info->has_clear_data; default: return false; } @@ -382,15 +394,23 @@ static bool ltrf216a_writable_reg(struct device *dev, unsigned int reg) static bool ltrf216a_volatile_reg(struct device *dev, unsigned int reg) { + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ltrf216a_data *data = iio_priv(indio_dev); + switch (reg) { case LTRF216A_MAIN_STATUS: - case LTRF216A_ALS_CLEAR_DATA_0: - case LTRF216A_ALS_CLEAR_DATA_1: - case LTRF216A_ALS_CLEAR_DATA_2: case LTRF216A_ALS_DATA_0: case LTRF216A_ALS_DATA_1: case LTRF216A_ALS_DATA_2: return true; + /* + * If these registers are not present on a chip (like LTR-308), + * the missing registers are not considered volatile. + */ + case LTRF216A_ALS_CLEAR_DATA_0: + case LTRF216A_ALS_CLEAR_DATA_1: + case LTRF216A_ALS_CLEAR_DATA_2: + return data->info->has_clear_data; default: return false; } @@ -433,6 +453,7 @@ static int ltrf216a_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; + data->info = i2c_get_match_data(client); mutex_init(&data->lock); @@ -520,15 +541,27 @@ cache_only: static DEFINE_RUNTIME_DEV_PM_OPS(ltrf216a_pm_ops, ltrf216a_runtime_suspend, ltrf216a_runtime_resume, NULL); +static const struct ltr_chip_info ltr308_chip_info = { + .has_clear_data = false, + .lux_multiplier = 60, +}; + +static const struct ltr_chip_info ltrf216a_chip_info = { + .has_clear_data = true, + .lux_multiplier = 45, +}; + static const struct i2c_device_id ltrf216a_id[] = { - { "ltrf216a" }, + { "ltr308", .driver_data = (kernel_ulong_t)<r308_chip_info }, + { "ltrf216a", .driver_data = (kernel_ulong_t)<rf216a_chip_info }, {} }; MODULE_DEVICE_TABLE(i2c, ltrf216a_id); static const struct of_device_id ltrf216a_of_match[] = { - { .compatible = "liteon,ltrf216a" }, - { .compatible = "ltr,ltrf216a" }, + { .compatible = "liteon,ltr308", .data = <r308_chip_info }, + { .compatible = "liteon,ltrf216a", .data = <rf216a_chip_info }, + { .compatible = "ltr,ltrf216a", .data = <rf216a_chip_info }, {} }; MODULE_DEVICE_TABLE(of, ltrf216a_of_match); diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c index 596cc48c4c34..25f63da70297 100644 --- a/drivers/iio/light/noa1305.c +++ b/drivers/iio/light/noa1305.c @@ -29,6 +29,7 @@ #define NOA1305_INTEGR_TIME_25MS 0x05 #define NOA1305_INTEGR_TIME_12_5MS 0x06 #define NOA1305_INTEGR_TIME_6_25MS 0x07 +#define NOA1305_INTEGR_TIME_MASK 0x07 #define NOA1305_REG_INT_SELECT 0x3 #define NOA1305_INT_SEL_ACTIVE_HIGH 0x01 #define NOA1305_INT_SEL_ACTIVE_LOW 0x02 @@ -43,12 +44,34 @@ #define NOA1305_DEVICE_ID 0x0519 #define NOA1305_DRIVER_NAME "noa1305" +static int noa1305_scale_available[] = { + 100, 8 * 77, /* 800 ms */ + 100, 4 * 77, /* 400 ms */ + 100, 2 * 77, /* 200 ms */ + 100, 1 * 77, /* 100 ms */ + 1000, 5 * 77, /* 50 ms */ + 10000, 25 * 77, /* 25 ms */ + 100000, 125 * 77, /* 12.5 ms */ + 1000000, 625 * 77, /* 6.25 ms */ +}; + +static int noa1305_int_time_available[] = { + 0, 800000, /* 800 ms */ + 0, 400000, /* 400 ms */ + 0, 200000, /* 200 ms */ + 0, 100000, /* 100 ms */ + 0, 50000, /* 50 ms */ + 0, 25000, /* 25 ms */ + 0, 12500, /* 12.5 ms */ + 0, 6250, /* 6.25 ms */ +}; + struct noa1305_priv { struct i2c_client *client; struct regmap *regmap; }; -static int noa1305_measure(struct noa1305_priv *priv) +static int noa1305_measure(struct noa1305_priv *priv, int *val) { __le16 data; int ret; @@ -58,7 +81,9 @@ static int noa1305_measure(struct noa1305_priv *priv) if (ret < 0) return ret; - return le16_to_cpu(data); + *val = le16_to_cpu(data); + + return IIO_VAL_INT; } static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2) @@ -76,91 +101,113 @@ static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2) * Integration Constant = 7.7 * Integration Time in Seconds */ - switch (data) { - case NOA1305_INTEGR_TIME_800MS: - *val = 100; - *val2 = 77 * 8; - break; - case NOA1305_INTEGR_TIME_400MS: - *val = 100; - *val2 = 77 * 4; - break; - case NOA1305_INTEGR_TIME_200MS: - *val = 100; - *val2 = 77 * 2; - break; - case NOA1305_INTEGR_TIME_100MS: - *val = 100; - *val2 = 77; - break; - case NOA1305_INTEGR_TIME_50MS: - *val = 1000; - *val2 = 77 * 5; - break; - case NOA1305_INTEGR_TIME_25MS: - *val = 10000; - *val2 = 77 * 25; - break; - case NOA1305_INTEGR_TIME_12_5MS: - *val = 100000; - *val2 = 77 * 125; - break; - case NOA1305_INTEGR_TIME_6_25MS: - *val = 1000000; - *val2 = 77 * 625; - break; - default: - return -EINVAL; - } + data &= NOA1305_INTEGR_TIME_MASK; + *val = noa1305_scale_available[2 * data + 0]; + *val2 = noa1305_scale_available[2 * data + 1]; return IIO_VAL_FRACTIONAL; } +static int noa1305_int_time(struct noa1305_priv *priv, int *val, int *val2) +{ + int data; + int ret; + + ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data); + if (ret < 0) + return ret; + + data &= NOA1305_INTEGR_TIME_MASK; + *val = noa1305_int_time_available[2 * data + 0]; + *val2 = noa1305_int_time_available[2 * data + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + static const struct iio_chan_spec noa1305_channels[] = { { .type = IIO_LIGHT, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), } }; +static int noa1305_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (chan->type != IIO_LIGHT) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = noa1305_scale_available; + *length = ARRAY_SIZE(noa1305_scale_available); + *type = IIO_VAL_FRACTIONAL; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_INT_TIME: + *vals = noa1305_int_time_available; + *length = ARRAY_SIZE(noa1305_int_time_available); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static int noa1305_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { - int ret = -EINVAL; struct noa1305_priv *priv = iio_priv(indio_dev); + if (chan->type != IIO_LIGHT) + return -EINVAL; + switch (mask) { case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_LIGHT: - ret = noa1305_measure(priv); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - default: - break; - } - break; + return noa1305_measure(priv, val); case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_LIGHT: - return noa1305_scale(priv, val, val2); - default: - break; - } - break; + return noa1305_scale(priv, val, val2); + case IIO_CHAN_INFO_INT_TIME: + return noa1305_int_time(priv, val, val2); default: - break; + return -EINVAL; } +} - return ret; +static int noa1305_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct noa1305_priv *priv = iio_priv(indio_dev); + int i; + + if (chan->type != IIO_LIGHT) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_INT_TIME) + return -EINVAL; + + if (val) /* >= 1s integration time not supported */ + return -EINVAL; + + /* Look up integration time register settings and write it if found. */ + for (i = 0; i < ARRAY_SIZE(noa1305_int_time_available) / 2; i++) + if (noa1305_int_time_available[2 * i + 1] == val2) + return regmap_write(priv->regmap, NOA1305_REG_INTEGRATION_TIME, i); + + return -EINVAL; } static const struct iio_info noa1305_info = { + .read_avail = noa1305_read_avail, .read_raw = noa1305_read_raw, + .write_raw = noa1305_write_raw, }; static bool noa1305_writable_reg(struct device *dev, unsigned int reg) diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c index 4937bf6fa046..76711c3cdf7c 100644 --- a/drivers/iio/light/rohm-bu27034.c +++ b/drivers/iio/light/rohm-bu27034.c @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * BU27034 ROHM Ambient Light Sensor + * BU27034ANUC ROHM Ambient Light Sensor * * Copyright (c) 2023, ROHM Semiconductor. - * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf */ #include <linux/bitfield.h> @@ -30,17 +29,15 @@ #define BU27034_REG_MODE_CONTROL2 0x42 #define BU27034_MASK_D01_GAIN GENMASK(7, 3) -#define BU27034_MASK_D2_GAIN_HI GENMASK(7, 6) -#define BU27034_MASK_D2_GAIN_LO GENMASK(2, 0) #define BU27034_REG_MODE_CONTROL3 0x43 #define BU27034_REG_MODE_CONTROL4 0x44 #define BU27034_MASK_MEAS_EN BIT(0) #define BU27034_MASK_VALID BIT(7) +#define BU27034_NUM_HW_DATA_CHANS 2 #define BU27034_REG_DATA0_LO 0x50 #define BU27034_REG_DATA1_LO 0x52 -#define BU27034_REG_DATA2_LO 0x54 -#define BU27034_REG_DATA2_HI 0x55 +#define BU27034_REG_DATA1_HI 0x53 #define BU27034_REG_MANUFACTURER_ID 0x92 #define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID @@ -88,58 +85,48 @@ enum { BU27034_CHAN_ALS, BU27034_CHAN_DATA0, BU27034_CHAN_DATA1, - BU27034_CHAN_DATA2, BU27034_NUM_CHANS }; static const unsigned long bu27034_scan_masks[] = { - GENMASK(BU27034_CHAN_DATA2, BU27034_CHAN_ALS), 0 + GENMASK(BU27034_CHAN_DATA1, BU27034_CHAN_DATA0), + GENMASK(BU27034_CHAN_DATA1, BU27034_CHAN_ALS), 0 }; /* - * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS + * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS * Time impacts to gain: 1x, 2x, 4x, 8x. * - * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768 + * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192 + * if 1x gain is scale 1, scale for 2x gain is 0.5, 4x => 0.25, + * ... 8192x => 0.0001220703125 => 122070.3125 nanos * - * Using NANO precision for scale we must use scale 64x corresponding gain 1x - * to avoid precision loss. (32x would result scale 976 562.5(nanos). + * Using NANO precision for scale, we must use scale 16x corresponding gain 1x + * to avoid precision loss. (8x would result scale 976 562.5(nanos). */ -#define BU27034_SCALE_1X 64 +#define BU27034_SCALE_1X 16 /* See the data sheet for the "Gain Setting" table */ #define BU27034_GSEL_1X 0x00 /* 00000 */ #define BU27034_GSEL_4X 0x08 /* 01000 */ -#define BU27034_GSEL_16X 0x0a /* 01010 */ #define BU27034_GSEL_32X 0x0b /* 01011 */ -#define BU27034_GSEL_64X 0x0c /* 01100 */ #define BU27034_GSEL_256X 0x18 /* 11000 */ #define BU27034_GSEL_512X 0x19 /* 11001 */ #define BU27034_GSEL_1024X 0x1a /* 11010 */ -#define BU27034_GSEL_2048X 0x1b /* 11011 */ -#define BU27034_GSEL_4096X 0x1c /* 11100 */ /* Available gain settings */ static const struct iio_gain_sel_pair bu27034_gains[] = { GAIN_SCALE_GAIN(1, BU27034_GSEL_1X), GAIN_SCALE_GAIN(4, BU27034_GSEL_4X), - GAIN_SCALE_GAIN(16, BU27034_GSEL_16X), GAIN_SCALE_GAIN(32, BU27034_GSEL_32X), - GAIN_SCALE_GAIN(64, BU27034_GSEL_64X), GAIN_SCALE_GAIN(256, BU27034_GSEL_256X), GAIN_SCALE_GAIN(512, BU27034_GSEL_512X), GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X), - GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X), - GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X), }; /* - * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits - * the data collection to data0-channel only and cuts the supported range to - * 10 bit. It is not supported by the driver. - * - * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct - * multiplying impact to the register values (similar to gain). + * Measurement modes are 55, 100, 200 and 400 mS modes - which do have direct + * multiplying impact to the data register values (similar to gain). * * This means that if meas-mode is changed for example from 400 => 200, * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8. @@ -156,13 +143,13 @@ static const struct iio_itime_sel_mul bu27034_itimes[] = { GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1), }; -#define BU27034_CHAN_DATA(_name, _ch2) \ +#define BU27034_CHAN_DATA(_name) \ { \ .type = IIO_INTENSITY, \ .channel = BU27034_CHAN_##_name, \ - .channel2 = (_ch2), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ .info_mask_shared_by_all_available = \ @@ -195,13 +182,12 @@ static const struct iio_chan_spec bu27034_channels[] = { /* * The BU27034 DATA0 and DATA1 channels are both on the visible light * area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm. - * These wave lengths are pretty much on the border of colours making - * these a poor candidates for R/G/B standardization. Hence they're both - * marked as clear channels + * These wave lengths are cyan(ish) and orange(ish), making these + * sub-optiomal candidates for R/G/B standardization. Hence the + * colour modifier is omitted. */ - BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR), - BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR), - BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR), + BU27034_CHAN_DATA(DATA0), + BU27034_CHAN_DATA(DATA1), IIO_CHAN_SOFT_TIMESTAMP(4), }; @@ -215,10 +201,10 @@ struct bu27034_data { struct mutex mutex; struct iio_gts gts; struct task_struct *task; - __le16 raw[3]; + __le16 raw[BU27034_NUM_HW_DATA_CHANS]; struct { u32 mlux; - __le16 channels[3]; + __le16 channels[BU27034_NUM_HW_DATA_CHANS]; s64 ts __aligned(8); } scan; }; @@ -232,7 +218,7 @@ static const struct regmap_range bu27034_volatile_ranges[] = { .range_max = BU27034_REG_MODE_CONTROL4, }, { .range_min = BU27034_REG_DATA0_LO, - .range_max = BU27034_REG_DATA2_HI, + .range_max = BU27034_REG_DATA1_HI, }, }; @@ -244,7 +230,7 @@ static const struct regmap_access_table bu27034_volatile_regs = { static const struct regmap_range bu27034_read_only_ranges[] = { { .range_min = BU27034_REG_DATA0_LO, - .range_max = BU27034_REG_DATA2_HI, + .range_max = BU27034_REG_DATA1_HI, }, { .range_min = BU27034_REG_MANUFACTURER_ID, .range_max = BU27034_REG_MANUFACTURER_ID, @@ -273,41 +259,17 @@ struct bu27034_gain_check { static int bu27034_get_gain_sel(struct bu27034_data *data, int chan) { + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; int ret, val; - switch (chan) { - case BU27034_CHAN_DATA0: - case BU27034_CHAN_DATA1: - { - int reg[] = { - [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, - [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, - }; - ret = regmap_read(data->regmap, reg[chan], &val); - if (ret) - return ret; - - return FIELD_GET(BU27034_MASK_D01_GAIN, val); - } - case BU27034_CHAN_DATA2: - { - int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO); - - ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val); - if (ret) - return ret; + ret = regmap_read(data->regmap, reg[chan], &val); + if (ret) + return ret; - /* - * The data2 channel gain is composed by 5 non continuous bits - * [7:6], [2:0]. Thus when we combine the 5-bit 'selector' - * from register value we must right shift the high bits by 3. - */ - return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits | - FIELD_GET(BU27034_MASK_D2_GAIN_LO, val); - } - default: - return -EINVAL; - } + return FIELD_GET(BU27034_MASK_D01_GAIN, val); } static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain) @@ -390,44 +352,9 @@ static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel) }; int mask, val; - if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) - return -EINVAL; - val = FIELD_PREP(BU27034_MASK_D01_GAIN, sel); - mask = BU27034_MASK_D01_GAIN; - if (chan == BU27034_CHAN_DATA0) { - /* - * We keep the same gain for channel 2 as we set for channel 0 - * We can't allow them to be individually controlled because - * setting one will impact also the other. Also, if we don't - * always update both gains we may result unsupported bit - * combinations. - * - * This is not nice but this is yet another place where the - * user space must be prepared to surprizes. Namely, see chan 2 - * gain changed when chan 0 gain is changed. - * - * This is not fatal for most users though. I don't expect the - * channel 2 to be used in any generic cases - the intensity - * values provided by the sensor for IR area are not openly - * documented. Also, channel 2 is not used for visible light. - * - * So, if there is application which is written to utilize the - * channel 2 - then it is probably specifically targeted to this - * sensor and knows how to utilize those values. It is safe to - * hope such user can also cope with the gain changes. - */ - mask |= BU27034_MASK_D2_GAIN_LO; - - /* - * The D2 gain bits are directly the lowest bits of selector. - * Just do add those bits to the value - */ - val |= sel & BU27034_MASK_D2_GAIN_LO; - } - return regmap_update_bits(data->regmap, reg[chan], mask, val); } @@ -435,13 +362,6 @@ static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain) { int ret; - /* - * We don't allow setting channel 2 gain as it messes up the - * gain for channel 0 - which shares the high bits - */ - if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) - return -EINVAL; - ret = iio_gts_find_sel_by_gain(&data->gts, gain); if (ret < 0) return ret; @@ -565,9 +485,6 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, int ret, time_sel, gain_sel, i; bool found = false; - if (chan == BU27034_CHAN_DATA2) - return -EINVAL; - if (chan == BU27034_CHAN_ALS) { if (val == 0 && val2 == 1000000) return 0; @@ -592,9 +509,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, /* * Populate information for the other channel which should also - * maintain the scale. (Due to the HW limitations the chan2 - * gets the same gain as chan0, so we only need to explicitly - * set the chan 0 and 1). + * maintain the scale. */ if (chan == BU27034_CHAN_DATA0) gain.chan = BU27034_CHAN_DATA1; @@ -608,7 +523,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, /* * Iterate through all the times to see if we find one which * can support requested scale for requested channel, while - * maintaining the scale for other channels + * maintaining the scale for the other channel */ for (i = 0; i < data->gts.num_itime; i++) { new_time_sel = data->gts.itime_table[i].sel; @@ -623,7 +538,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, if (ret) continue; - /* Can the other channel(s) maintain scale? */ + /* Can the other channel maintain scale? */ ret = iio_gts_find_new_gain_sel_by_old_gain_time( &data->gts, gain.old_gain, time_sel, new_time_sel, &gain.new_gain); @@ -635,7 +550,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, } if (!found) { dev_dbg(data->dev, - "Can't set scale maintaining other channels\n"); + "Can't set scale maintaining other channel\n"); ret = -EINVAL; goto unlock_out; @@ -659,102 +574,21 @@ unlock_out: } /* - * for (D1/D0 < 0.87): - * lx = 0.004521097 * D1 - 0.002663996 * D0 + - * 0.00012213 * D1 * D1 / D0 - * - * => 115.7400832 * ch1 / gain1 / mt - - * 68.1982976 * ch0 / gain0 / mt + - * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 * - * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) - * - * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 * - * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) - * => 0.00012213 * 25600 * (ch1 /gain1 / mt) * - * (ch1 /gain1 / mt) / (ch0 / gain0 / mt) - * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) / - * (ch0 / gain0) - * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) * - * gain0 / ch0 - * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0 - * - * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / - * mt + A - * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / - * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt / - * ch0 - * - * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 + - * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / - * mt - * - * For (0.87 <= D1/D0 < 1.00) - * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1) - * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * - * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * ((D1/D0 - 0.87) * (0.385) + 1) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * (0.385 * D1/D0 - 0.66505) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505) - * => 13.118336 * ch1 / (gain1 * mt) - * + 22.66064768 * ch0 / (gain0 * mt) - * + 8931.90144 * ch1 * ch1 * gain0 / - * (25600 * ch0 * gain1 * gain1 * mt) - * + 0.602694912 * ch1 / (gain1 * mt) - * - * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) - * + 22.66064768 * ch0 / gain0 - * + 13.721030912 * ch1 / gain1 - * ] / mt - * - * For (D1/D0 >= 1.00) + * for (D1/D0 < 1.5): + * lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1/D0 – 1.5) * (0.25) + 1) * - * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1) - * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1) - * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * - * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1) - * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * - * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1) - * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0) - * + 34.0736 * 1.1 * ch0 / (gain0 * mt) - * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0) - * + 1.1 * 0.90624 * ch1 / (gain1 * mt) - * => -43614.208 * ch1 / (gain1 * mt * 25600) - * + 37.48096 ch0 / (gain0 * mt) - * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0) - * + 0.996864 ch1 / (gain1 * mt) - * => [ - * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0) - * - 0.706816 * ch1 / gain1 - * + 37.48096 ch0 /gain0 - * ] * mt + * => -0.000745625 * D0 + 0.0002515625 * D1 + -0.000018675 * D1 * D1 / D0 * + * => (6.44 * ch1 / gain1 + 19.088 * ch0 / gain0 - + * 0.47808 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / + * mt * - * So, the first case (D1/D0 < 0.87) can be computed to a form: + * Else + * lx = 0.001193 * D0 - 0.0000747 * D1 * - * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + - * 115.7400832 * ch1 / gain1 + - * -68.1982976 * ch0 / gain0 - * / mt - * - * Second case (0.87 <= D1/D0 < 1.00) goes to form: - * - * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + - * 13.721030912 * ch1 / gain1 + - * 22.66064768 * ch0 / gain0 - * ] / mt - * - * Third case (D1/D0 >= 1.00) goes to form: - * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + - * -0.706816 * ch1 / gain1 + - * 37.48096 ch0 /(gain0 - * ] / mt + * => (1.91232 * ch1 / gain1 + 30.5408 * ch0 / gain0 + + * [0 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0] ) / + * mt * * This can be unified to format: * lx = [ @@ -764,19 +598,14 @@ unlock_out: * ] / mt * * For case 1: - * A = 3.126528, - * B = 115.7400832 - * C = -68.1982976 + * A = -0.47808, + * B = 6.44, + * C = 19.088 * * For case 2: - * A = 0.3489024 - * B = 13.721030912 - * C = 22.66064768 - * - * For case 3: - * A = -0.045312 - * B = -0.706816 - * C = 37.48096 + * A = 0 + * B = 1.91232 + * C = 30.5408 */ struct bu27034_lx_coeff { @@ -881,21 +710,16 @@ static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1, { static const struct bu27034_lx_coeff coeff[] = { { - .A = 31265280, /* 3.126528 */ - .B = 1157400832, /*115.7400832 */ - .C = 681982976, /* -68.1982976 */ - .is_neg = {false, false, true}, + .A = 4780800, /* -0.47808 */ + .B = 64400000, /* 6.44 */ + .C = 190880000, /* 19.088 */ + .is_neg = { true, false, false }, }, { - .A = 3489024, /* 0.3489024 */ - .B = 137210309, /* 13.721030912 */ - .C = 226606476, /* 22.66064768 */ + .A = 0, /* 0 */ + .B = 19123200, /* 1.91232 */ + .C = 305408000, /* 30.5408 */ /* All terms positive */ - }, { - .A = 453120, /* -0.045312 */ - .B = 7068160, /* -0.706816 */ - .C = 374809600, /* 37.48096 */ - .is_neg = {true, true, false}, - } + }, }; const struct bu27034_lx_coeff *c = &coeff[coeff_idx]; u64 res = 0, terms[3]; @@ -967,7 +791,6 @@ static int bu27034_read_result(struct bu27034_data *data, int chan, int *res) int reg[] = { [BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO, [BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO, - [BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO, }; int valid, ret; __le16 val; @@ -1034,7 +857,7 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan, { int ret; - if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA2) + if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA1) return -EINVAL; ret = bu27034_meas_set(data, true); @@ -1059,12 +882,10 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan, * D1 = data1/ch1_gain/meas_time_ms * 25600 * * Then: - * if (D1/D0 < 0.87) - * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1) - * else if (D1/D0 < 1) - * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1) - * else - * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1) + * If (D1/D0 < 1.5) + * lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1 / D0 – 1.5) * 0.25 + 1) + * Else + * lx = (0.001193 * D0 + (-0.0000747) * D1) * * We use it here. Users who have for example some colored lens * need to modify the calculation but I hope this gives a starting point for @@ -1115,12 +936,10 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val) d1_d0_ratio_scaled /= ch0 * gain1; } - if (d1_d0_ratio_scaled < 87) + if (d1_d0_ratio_scaled < 150) ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0); - else if (d1_d0_ratio_scaled < 100) - ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1); else - ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2); + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1); if (ret < 0) return ret; @@ -1133,7 +952,7 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val) static int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val) { - __le16 res[3]; + __le16 res[BU27034_NUM_HW_DATA_CHANS]; int ret; ret = bu27034_meas_set(data, true); @@ -1171,6 +990,13 @@ static int bu27034_read_raw(struct iio_dev *idev, return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = bu27034_get_gain(data, chan->channel, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: return bu27034_get_scale(data, chan->channel, val, val2); @@ -1215,12 +1041,17 @@ static int bu27034_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { + struct bu27034_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_INT_TIME: return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HARDWAREGAIN: + dev_dbg(data->dev, + "HARDWAREGAIN is read-only, use scale to set\n"); + return -EINVAL; default: return -EINVAL; } @@ -1501,7 +1332,7 @@ static int bu27034_probe(struct i2c_client *i2c) } static const struct of_device_id bu27034_of_match[] = { - { .compatible = "rohm,bu27034" }, + { .compatible = "rohm,bu27034anuc" }, { } }; MODULE_DEVICE_TABLE(of, bu27034_of_match); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 77666b780a5c..66abda021696 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -465,11 +465,10 @@ static irqreturn_t si1145_trigger_handler(int irq, void *private) goto done; } - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { int run = 1; - while (i + run < indio_dev->masklength) { + while (i + run < iio_get_masklength(indio_dev)) { if (!test_bit(i + run, indio_dev->active_scan_mask)) break; if (indio_dev->channels[i + run].address != @@ -514,7 +513,7 @@ static int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask) if (data->scan_mask == scan_mask) return 0; - for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) { switch (indio_dev->channels[i].address) { case SI1145_REG_ALSVIS_DATA: reg |= SI1145_CHLIST_EN_ALSVIS; diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e3470d6743ef..ed20b6714546 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -35,6 +35,7 @@ #define STK3310_STATE_EN_ALS BIT(1) #define STK3310_STATE_STANDBY 0x00 +#define STK3013_CHIP_ID_VAL 0x31 #define STK3310_CHIP_ID_VAL 0x13 #define STK3311_CHIP_ID_VAL 0x1D #define STK3311A_CHIP_ID_VAL 0x15 @@ -84,6 +85,7 @@ static const struct reg_field stk3310_reg_field_flag_nf = REG_FIELD(STK3310_REG_FLAG, 0, 0); static const u8 stk3310_chip_ids[] = { + STK3013_CHIP_ID_VAL, STK3310_CHIP_ID_VAL, STK3311A_CHIP_ID_VAL, STK3311S34_CHIP_ID_VAL, @@ -496,7 +498,7 @@ static int stk3310_init(struct iio_dev *indio_dev) ret = stk3310_check_chip_id(chipid); if (ret < 0) - dev_warn(&client->dev, "unknown chip id: 0x%x\n", chipid); + dev_info(&client->dev, "new unknown chip id: 0x%x\n", chipid); state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS; ret = stk3310_set_state(data, state); @@ -700,6 +702,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); static const struct i2c_device_id stk3310_i2c_id[] = { + { "STK3013" }, { "STK3310" }, { "STK3311" }, { "STK3335" }, @@ -708,6 +711,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); static const struct acpi_device_id stk3310_acpi_id[] = { + {"STK3013", 0}, {"STK3310", 0}, {"STK3311", 0}, {} @@ -716,6 +720,7 @@ static const struct acpi_device_id stk3310_acpi_id[] = { 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", }, diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index c9566615b964..4fecdf10aeb1 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -206,8 +206,7 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p) struct tcs3414_data *data = iio_priv(indio_dev); int i, j = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { int ret = i2c_smbus_read_word_data(data->client, TCS3414_DATA_GREEN + 2*i); if (ret < 0) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 89384dba83dd..04452b4664f3 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -383,8 +383,7 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) if (ret < 0) goto done; - for_each_set_bit(i, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { ret = i2c_smbus_read_word_data(data->client, TCS3472_CDATA + 2*i); if (ret < 0) diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index cd2917d71904..8eb718f5e50f 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -39,7 +39,7 @@ config AK8975 select IIO_TRIGGERED_BUFFER help Say yes here to build support for Asahi Kasei AK8975, AK8963, - AK09911, AK09912 or AK09916 3-Axis Magnetometer. + AK09911, AK09912, AK09916 or AK09918 3-Axis Magnetometer. To compile this driver as a module, choose M here: the module will be called ak8975. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index dd466c5fa621..18077fb463a9 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -78,6 +78,7 @@ */ #define AK09912_REG_WIA1 0x00 #define AK09912_REG_WIA2 0x01 +#define AK09918_DEVICE_ID 0x0C #define AK09916_DEVICE_ID 0x09 #define AK09912_DEVICE_ID 0x04 #define AK09911_DEVICE_ID 0x05 @@ -209,6 +210,7 @@ enum asahi_compass_chipset { AK09911, AK09912, AK09916, + AK09918, }; enum ak_ctrl_reg_addr { @@ -371,6 +373,34 @@ static const struct ak_def ak_def_array[] = { AK09912_REG_HXL, AK09912_REG_HYL, AK09912_REG_HZL}, + }, + [AK09918] = { + /* ak09918 is register compatible with ak09912 this is for avoid + * unknown id messages. + */ + .type = AK09918, + .raw_to_gauss = ak09912_raw_to_gauss, + .range = 32752, + .ctrl_regs = { + AK09912_REG_ST1, + AK09912_REG_ST2, + AK09912_REG_CNTL2, + AK09912_REG_ASAX, + AK09912_MAX_REGS}, + .ctrl_masks = { + AK09912_REG_ST1_DRDY_MASK, + AK09912_REG_ST2_HOFL_MASK, + 0, + AK09912_REG_CNTL2_MODE_MASK}, + .ctrl_modes = { + AK09912_REG_CNTL_MODE_POWER_DOWN, + AK09912_REG_CNTL_MODE_ONCE, + AK09912_REG_CNTL_MODE_SELF_TEST, + AK09912_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK09912_REG_HXL, + AK09912_REG_HYL, + AK09912_REG_HZL}, } }; @@ -452,6 +482,7 @@ static int ak8975_who_i_am(struct i2c_client *client, /* * Signature for each device: * Device | WIA1 | WIA2 + * AK09918 | DEVICE_ID_| AK09918_DEVICE_ID * AK09916 | DEVICE_ID_| AK09916_DEVICE_ID * AK09912 | DEVICE_ID | AK09912_DEVICE_ID * AK09911 | DEVICE_ID | AK09911_DEVICE_ID @@ -484,10 +515,18 @@ static int ak8975_who_i_am(struct i2c_client *client, if (wia_val[1] == AK09916_DEVICE_ID) return 0; break; - default: - dev_err(&client->dev, "Type %d unknown\n", type); + case AK09918: + if (wia_val[1] == AK09918_DEVICE_ID) + return 0; + break; } - return -ENODEV; + + dev_info(&client->dev, "Device ID %x is unknown.\n", wia_val[1]); + /* + * Let driver to probe on unknown id for support more register + * compatible variants. + */ + return 0; } /* @@ -692,22 +731,8 @@ static int ak8975_start_read_axis(struct ak8975_data *data, if (ret < 0) return ret; - /* This will be executed only for non-interrupt based waiting case */ - if (ret & data->def->ctrl_masks[ST1_DRDY]) { - ret = i2c_smbus_read_byte_data(client, - data->def->ctrl_regs[ST2]); - if (ret < 0) { - dev_err(&client->dev, "Error in reading ST2\n"); - return ret; - } - 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); - return -EINVAL; - } - } - - return 0; + /* Return with zero if the data is ready. */ + return !data->def->ctrl_regs[ST1_DRDY]; } /* Retrieve raw flux value for one of the x, y, or z axis. */ @@ -734,6 +759,20 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) if (ret < 0) goto exit; + /* Read out ST2 for release lock on measurment 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"); + 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); + ret = -EINVAL; + goto exit; + } + mutex_unlock(&data->lock); pm_runtime_mark_last_busy(&data->client->dev); @@ -1067,6 +1106,7 @@ static const struct i2c_device_id ak8975_id[] = { {"ak09911", (kernel_ulong_t)&ak_def_array[AK09911] }, {"ak09912", (kernel_ulong_t)&ak_def_array[AK09912] }, {"ak09916", (kernel_ulong_t)&ak_def_array[AK09916] }, + {"ak09918", (kernel_ulong_t)&ak_def_array[AK09918] }, {} }; MODULE_DEVICE_TABLE(i2c, ak8975_id); @@ -1081,7 +1121,7 @@ static const struct of_device_id ak8975_of_match[] = { { .compatible = "asahi-kasei,ak09912", .data = &ak_def_array[AK09912] }, { .compatible = "ak09912", .data = &ak_def_array[AK09912] }, { .compatible = "asahi-kasei,ak09916", .data = &ak_def_array[AK09916] }, - { .compatible = "ak09916", .data = &ak_def_array[AK09916] }, + { .compatible = "asahi-kasei,ak09918", .data = &ak_def_array[AK09918] }, {} }; MODULE_DEVICE_TABLE(of, ak8975_of_match); diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index 42b70cd42b39..0e03a772fa43 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -464,7 +464,7 @@ static irqreturn_t rm3100_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; unsigned long scan_mask = *indio_dev->active_scan_mask; - unsigned int mask_len = indio_dev->masklength; + unsigned int mask_len = iio_get_masklength(indio_dev); struct rm3100_data *data = iio_priv(indio_dev); struct regmap *regmap = data->regmap; int ret, i, bit; diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 3ad38506028e..ce369dbb17fc 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -31,6 +31,8 @@ config BMP280 select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Bosch Sensortec BMP180, BMP280, BMP380 and BMP580 pressure and temperature sensors. Also supports the BME280 with @@ -248,6 +250,15 @@ config MS5637 This driver can also be built as a module. If so, the module will be called ms5637. +config SDP500 + tristate "Sensirion SDP500 differential pressure sensor I2C driver" + depends on I2C + help + Say Y here to build support for Sensirion SDP500 differential pressure + sensor I2C driver. + To compile this driver as a module, choose M here: the core module + will be called sdp500. + config IIO_ST_PRESS tristate "STMicroelectronics pressure sensor Driver" depends on (I2C || SPI_MASTER) && SYSFS diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index a93709e35760..6482288e07ee 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_MS5611) += ms5611_core.o obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o obj-$(CONFIG_MS5637) += ms5637.o +obj-$(CONFIG_SDP500) += sdp500.o obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o st_pressure-y := st_pressure_core.o st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 49081b729618..da379230c837 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -41,7 +41,10 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/iio/buffer.h> #include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include <asm/unaligned.h> @@ -134,46 +137,169 @@ enum { BMP380_P11 = 20, }; +enum bmp280_scan { + BMP280_PRESS, + BMP280_TEMP, + BME280_HUMID, +}; + static const struct iio_chan_spec bmp280_channels[] = { { .type = IIO_PRESSURE, + /* PROCESSED maintained for ABI backwards compatibility */ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_TEMP, + /* PROCESSED maintained for ABI backwards compatibility */ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_chan_spec bme280_channels[] = { + { + .type = IIO_PRESSURE, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_TEMP, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_HUMIDITYRELATIVE, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(3), }; static const struct iio_chan_spec bmp380_channels[] = { { .type = IIO_PRESSURE, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_TEMP, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_chan_spec bmp580_channels[] = { { - .type = IIO_HUMIDITYRELATIVE, + .type = IIO_PRESSURE, + /* PROCESSED maintained for ABI backwards compatibility */ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_TEMP, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(2), }; static int bmp280_read_calib(struct bmp280_data *data) @@ -289,7 +415,7 @@ static int bme280_read_humid_adc(struct bmp280_data *data, u16 *adc_humidity) int ret; ret = regmap_bulk_read(data->regmap, BME280_REG_HUMIDITY_MSB, - &data->be16, sizeof(data->be16)); + &data->be16, BME280_NUM_HUMIDITY_BYTES); if (ret) { dev_err(data->dev, "failed to read humidity\n"); return ret; @@ -335,7 +461,7 @@ static int bmp280_read_temp_adc(struct bmp280_data *data, u32 *adc_temp) int ret; ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, - data->buf, sizeof(data->buf)); + data->buf, BMP280_NUM_TEMP_BYTES); if (ret) { dev_err(data->dev, "failed to read temperature\n"); return ret; @@ -396,7 +522,7 @@ static int bmp280_read_press_adc(struct bmp280_data *data, u32 *adc_press) int ret; ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, - data->buf, sizeof(data->buf)); + data->buf, BMP280_NUM_PRESS_BYTES); if (ret) { dev_err(data->dev, "failed to read pressure\n"); return ret; @@ -445,10 +571,8 @@ static u32 bmp280_compensate_press(struct bmp280_data *data, return (u32)p; } -static int bmp280_read_temp(struct bmp280_data *data, - int *val, int *val2) +static int bmp280_read_temp(struct bmp280_data *data, s32 *comp_temp) { - s32 comp_temp; u32 adc_temp; int ret; @@ -456,16 +580,15 @@ static int bmp280_read_temp(struct bmp280_data *data, if (ret) return ret; - comp_temp = bmp280_compensate_temp(data, adc_temp); + *comp_temp = bmp280_compensate_temp(data, adc_temp); - *val = comp_temp * 10; - return IIO_VAL_INT; + return 0; } -static int bmp280_read_press(struct bmp280_data *data, - int *val, int *val2) +static int bmp280_read_press(struct bmp280_data *data, u32 *comp_press) { - u32 comp_press, adc_press, t_fine; + u32 adc_press; + s32 t_fine; int ret; ret = bmp280_get_t_fine(data, &t_fine); @@ -476,17 +599,13 @@ static int bmp280_read_press(struct bmp280_data *data, if (ret) return ret; - comp_press = bmp280_compensate_press(data, adc_press, t_fine); + *comp_press = bmp280_compensate_press(data, adc_press, t_fine); - *val = comp_press; - *val2 = 256000; - - return IIO_VAL_FRACTIONAL; + return 0; } -static int bme280_read_humid(struct bmp280_data *data, int *val, int *val2) +static int bme280_read_humid(struct bmp280_data *data, u32 *comp_humidity) { - u32 comp_humidity; u16 adc_humidity; s32 t_fine; int ret; @@ -499,11 +618,9 @@ static int bme280_read_humid(struct bmp280_data *data, int *val, int *val2) if (ret) return ret; - comp_humidity = bme280_compensate_humidity(data, adc_humidity, t_fine); + *comp_humidity = bme280_compensate_humidity(data, adc_humidity, t_fine); - *val = comp_humidity * 1000 / 1024; - - return IIO_VAL_INT; + return 0; } static int bmp280_read_raw_impl(struct iio_dev *indio_dev, @@ -511,6 +628,8 @@ static int bmp280_read_raw_impl(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct bmp280_data *data = iio_priv(indio_dev); + int chan_value; + int ret; guard(mutex)(&data->lock); @@ -518,11 +637,72 @@ static int bmp280_read_raw_impl(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { case IIO_HUMIDITYRELATIVE: - return data->chip_info->read_humid(data, val, val2); + ret = data->chip_info->read_humid(data, &chan_value); + if (ret) + return ret; + + *val = data->chip_info->humid_coeffs[0] * chan_value; + *val2 = data->chip_info->humid_coeffs[1]; + return data->chip_info->humid_coeffs_type; + case IIO_PRESSURE: + ret = data->chip_info->read_press(data, &chan_value); + if (ret) + return ret; + + *val = data->chip_info->press_coeffs[0] * chan_value; + *val2 = data->chip_info->press_coeffs[1]; + return data->chip_info->press_coeffs_type; + case IIO_TEMP: + ret = data->chip_info->read_temp(data, &chan_value); + if (ret) + return ret; + + *val = data->chip_info->temp_coeffs[0] * chan_value; + *val2 = data->chip_info->temp_coeffs[1]; + return data->chip_info->temp_coeffs_type; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + ret = data->chip_info->read_humid(data, &chan_value); + if (ret) + return ret; + + *val = chan_value; + return IIO_VAL_INT; + case IIO_PRESSURE: + ret = data->chip_info->read_press(data, &chan_value); + if (ret) + return ret; + + *val = chan_value; + return IIO_VAL_INT; + case IIO_TEMP: + ret = data->chip_info->read_temp(data, &chan_value); + if (ret) + return ret; + + *val = chan_value; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + *val = data->chip_info->humid_coeffs[0]; + *val2 = data->chip_info->humid_coeffs[1]; + return data->chip_info->humid_coeffs_type; case IIO_PRESSURE: - return data->chip_info->read_press(data, val, val2); + *val = data->chip_info->press_coeffs[0]; + *val2 = data->chip_info->press_coeffs[1]; + return data->chip_info->press_coeffs_type; case IIO_TEMP: - return data->chip_info->read_temp(data, val, val2); + *val = data->chip_info->temp_coeffs[0]; + *val2 = data->chip_info->temp_coeffs[1]; + return data->chip_info->temp_coeffs_type; default: return -EINVAL; } @@ -793,6 +973,16 @@ static const struct iio_info bmp280_info = { .write_raw = &bmp280_write_raw, }; +static const unsigned long bmp280_avail_scan_masks[] = { + BIT(BMP280_TEMP) | BIT(BMP280_PRESS), + 0 +}; + +static const unsigned long bme280_avail_scan_masks[] = { + BIT(BME280_HUMID) | BIT(BMP280_TEMP) | BIT(BMP280_PRESS), + 0 +}; + static int bmp280_chip_config(struct bmp280_data *data) { u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) | @@ -820,8 +1010,57 @@ static int bmp280_chip_config(struct bmp280_data *data) return ret; } +static irqreturn_t bmp280_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmp280_data *data = iio_priv(indio_dev); + s32 adc_temp, adc_press, t_fine; + int ret; + + guard(mutex)(&data->lock); + + /* Burst read data registers */ + ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, + data->buf, BMP280_BURST_READ_BYTES); + if (ret) { + dev_err(data->dev, "failed to burst read sensor data\n"); + goto out; + } + + /* Temperature calculations */ + adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[3])); + if (adc_temp == BMP280_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + goto out; + } + + data->sensor_data[1] = bmp280_compensate_temp(data, adc_temp); + + /* Pressure calculations */ + adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); + if (adc_press == BMP280_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + goto out; + } + + t_fine = bmp280_calc_t_fine(data, adc_temp); + + data->sensor_data[0] = bmp280_compensate_press(data, adc_press, t_fine); + + iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; static const u8 bmp280_chip_ids[] = { BMP280_CHIP_ID }; +static const int bmp280_temp_coeffs[] = { 10, 1 }; +static const int bmp280_press_coeffs[] = { 1, 256000 }; const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, @@ -830,7 +1069,8 @@ const struct bmp280_chip_info bmp280_chip_info = { .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, - .num_channels = 2, + .num_channels = ARRAY_SIZE(bmp280_channels), + .avail_scan_masks = bmp280_avail_scan_masks, .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), @@ -850,10 +1090,17 @@ const struct bmp280_chip_info bmp280_chip_info = { .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1, + .temp_coeffs = bmp280_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL, + .press_coeffs = bmp280_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + .chip_config = bmp280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, .read_calib = bmp280_read_calib, + + .trigger_handler = bmp280_trigger_handler, }; EXPORT_SYMBOL_NS(bmp280_chip_info, IIO_BMP280); @@ -876,16 +1123,74 @@ static int bme280_chip_config(struct bmp280_data *data) return bmp280_chip_config(data); } +static irqreturn_t bme280_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmp280_data *data = iio_priv(indio_dev); + s32 adc_temp, adc_press, adc_humidity, t_fine; + int ret; + + guard(mutex)(&data->lock); + + /* Burst read data registers */ + ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, + data->buf, BME280_BURST_READ_BYTES); + if (ret) { + dev_err(data->dev, "failed to burst read sensor data\n"); + goto out; + } + + /* Temperature calculations */ + adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[3])); + if (adc_temp == BMP280_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + goto out; + } + + data->sensor_data[1] = bmp280_compensate_temp(data, adc_temp); + + /* Pressure calculations */ + adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); + if (adc_press == BMP280_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + goto out; + } + + t_fine = bmp280_calc_t_fine(data, adc_temp); + + data->sensor_data[0] = bmp280_compensate_press(data, adc_press, t_fine); + + /* Humidity calculations */ + adc_humidity = get_unaligned_be16(&data->buf[6]); + + if (adc_humidity == BMP280_HUMIDITY_SKIPPED) { + dev_err(data->dev, "reading humidity skipped\n"); + goto out; + } + data->sensor_data[2] = bme280_compensate_humidity(data, adc_humidity, t_fine); + + iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const u8 bme280_chip_ids[] = { BME280_CHIP_ID }; +static const int bme280_humid_coeffs[] = { 1000, 1024 }; const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, .chip_id = bme280_chip_ids, .num_chip_id = ARRAY_SIZE(bme280_chip_ids), - .regmap_config = &bmp280_regmap_config, + .regmap_config = &bme280_regmap_config, .start_up_time = 2000, - .channels = bmp280_channels, - .num_channels = 3, + .channels = bme280_channels, + .num_channels = ARRAY_SIZE(bme280_channels), + .avail_scan_masks = bme280_avail_scan_masks, .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), @@ -899,11 +1204,20 @@ const struct bmp280_chip_info bme280_chip_info = { .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail), .oversampling_humid_default = BME280_OSRS_HUMIDITY_16X - 1, + .temp_coeffs = bmp280_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL, + .press_coeffs = bmp280_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + .humid_coeffs = bme280_humid_coeffs, + .humid_coeffs_type = IIO_VAL_FRACTIONAL, + .chip_config = bme280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, .read_humid = bme280_read_humid, .read_calib = bme280_read_calib, + + .trigger_handler = bme280_trigger_handler, }; EXPORT_SYMBOL_NS(bme280_chip_info, IIO_BMP280); @@ -958,7 +1272,7 @@ static int bmp380_read_temp_adc(struct bmp280_data *data, u32 *adc_temp) int ret; ret = regmap_bulk_read(data->regmap, BMP380_REG_TEMP_XLSB, - data->buf, sizeof(data->buf)); + data->buf, BMP280_NUM_TEMP_BYTES); if (ret) { dev_err(data->dev, "failed to read temperature\n"); return ret; @@ -1027,7 +1341,7 @@ static int bmp380_read_press_adc(struct bmp280_data *data, u32 *adc_press) int ret; ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB, - data->buf, sizeof(data->buf)); + data->buf, BMP280_NUM_PRESS_BYTES); if (ret) { dev_err(data->dev, "failed to read pressure\n"); return ret; @@ -1091,9 +1405,8 @@ static u32 bmp380_compensate_press(struct bmp280_data *data, return comp_press; } -static int bmp380_read_temp(struct bmp280_data *data, int *val, int *val2) +static int bmp380_read_temp(struct bmp280_data *data, s32 *comp_temp) { - s32 comp_temp; u32 adc_temp; int ret; @@ -1101,15 +1414,14 @@ static int bmp380_read_temp(struct bmp280_data *data, int *val, int *val2) if (ret) return ret; - comp_temp = bmp380_compensate_temp(data, adc_temp); + *comp_temp = bmp380_compensate_temp(data, adc_temp); - *val = comp_temp * 10; - return IIO_VAL_INT; + return 0; } -static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) +static int bmp380_read_press(struct bmp280_data *data, u32 *comp_press) { - u32 adc_press, comp_press, t_fine; + u32 adc_press, t_fine; int ret; ret = bmp380_get_t_fine(data, &t_fine); @@ -1120,12 +1432,9 @@ static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) if (ret) return ret; - comp_press = bmp380_compensate_press(data, adc_press, t_fine); - - *val = comp_press; - *val2 = 100000; + *comp_press = bmp380_compensate_press(data, adc_press, t_fine); - return IIO_VAL_FRACTIONAL; + return 0; } static int bmp380_read_calib(struct bmp280_data *data) @@ -1272,10 +1581,11 @@ static int bmp380_chip_config(struct bmp280_data *data) } /* * Waits for measurement before checking configuration error - * flag. Selected longest measure time indicated in - * section 3.9.1 in the datasheet. + * flag. Selected longest measurement time, calculated from + * formula in datasheet section 3.9.2 with an offset of ~+15% + * as it seen as well in table 3.9.1. */ - msleep(80); + msleep(150); /* Check config error flag */ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp); @@ -1293,9 +1603,58 @@ static int bmp380_chip_config(struct bmp280_data *data) return 0; } +static irqreturn_t bmp380_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmp280_data *data = iio_priv(indio_dev); + s32 adc_temp, adc_press, t_fine; + int ret; + + guard(mutex)(&data->lock); + + /* Burst read data registers */ + ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB, + data->buf, BMP280_BURST_READ_BYTES); + if (ret) { + dev_err(data->dev, "failed to burst read sensor data\n"); + goto out; + } + + /* Temperature calculations */ + adc_temp = get_unaligned_le24(&data->buf[3]); + if (adc_temp == BMP380_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + goto out; + } + + data->sensor_data[1] = bmp380_compensate_temp(data, adc_temp); + + /* Pressure calculations */ + adc_press = get_unaligned_le24(&data->buf[0]); + if (adc_press == BMP380_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + goto out; + } + + t_fine = bmp380_calc_t_fine(data, adc_temp); + + data->sensor_data[0] = bmp380_compensate_press(data, adc_press, t_fine); + + iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; static const u8 bmp380_chip_ids[] = { BMP380_CHIP_ID, BMP390_CHIP_ID }; +static const int bmp380_temp_coeffs[] = { 10, 1 }; +static const int bmp380_press_coeffs[] = { 1, 100000 }; const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, @@ -1305,7 +1664,8 @@ const struct bmp280_chip_info bmp380_chip_info = { .spi_read_extra_byte = true, .start_up_time = 2000, .channels = bmp380_channels, - .num_channels = 2, + .num_channels = ARRAY_SIZE(bmp380_channels), + .avail_scan_masks = bmp280_avail_scan_masks, .oversampling_temp_avail = bmp380_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp380_oversampling_avail), @@ -1323,11 +1683,18 @@ const struct bmp280_chip_info bmp380_chip_info = { .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), .iir_filter_coeff_default = 2, + .temp_coeffs = bmp380_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL, + .press_coeffs = bmp380_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + .chip_config = bmp380_chip_config, .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, .preinit = bmp380_preinit, + + .trigger_handler = bmp380_trigger_handler, }; EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); @@ -1443,58 +1810,48 @@ static int bmp580_nvm_operation(struct bmp280_data *data, bool is_write) * for what is expected on IIO ABI. */ -static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2) +static int bmp580_read_temp(struct bmp280_data *data, s32 *raw_temp) { - s32 raw_temp; + s32 value_temp; int ret; - ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, data->buf, - sizeof(data->buf)); + ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, + data->buf, BMP280_NUM_TEMP_BYTES); if (ret) { dev_err(data->dev, "failed to read temperature\n"); return ret; } - raw_temp = get_unaligned_le24(data->buf); - if (raw_temp == BMP580_TEMP_SKIPPED) { + value_temp = get_unaligned_le24(data->buf); + if (value_temp == BMP580_TEMP_SKIPPED) { dev_err(data->dev, "reading temperature skipped\n"); return -EIO; } + *raw_temp = sign_extend32(value_temp, 23); - /* - * Temperature is returned in Celsius degrees in fractional - * form down 2^16. We rescale by x1000 to return millidegrees - * Celsius to respect IIO ABI. - */ - raw_temp = sign_extend32(raw_temp, 23); - *val = ((s64)raw_temp * 1000) / (1 << 16); - return IIO_VAL_INT; + return 0; } -static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2) +static int bmp580_read_press(struct bmp280_data *data, u32 *raw_press) { - u32 raw_press; + u32 value_press; int ret; - ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB, data->buf, - sizeof(data->buf)); + ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB, + data->buf, BMP280_NUM_PRESS_BYTES); if (ret) { dev_err(data->dev, "failed to read pressure\n"); return ret; } - raw_press = get_unaligned_le24(data->buf); - if (raw_press == BMP580_PRESS_SKIPPED) { + value_press = get_unaligned_le24(data->buf); + if (value_press == BMP580_PRESS_SKIPPED) { dev_err(data->dev, "reading pressure skipped\n"); return -EIO; } - /* - * Pressure is returned in Pascals in fractional form down 2^16. - * We rescale /1000 to convert to kilopascal to respect IIO ABI. - */ - *val = raw_press; - *val2 = 64000; /* 2^6 * 1000 */ - return IIO_VAL_FRACTIONAL; + *raw_press = value_press; + + return 0; } static const int bmp580_odr_table[][2] = { @@ -1828,8 +2185,43 @@ static int bmp580_chip_config(struct bmp280_data *data) return 0; } +static irqreturn_t bmp580_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmp280_data *data = iio_priv(indio_dev); + int ret; + + guard(mutex)(&data->lock); + + /* Burst read data registers */ + ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, + data->buf, BMP280_BURST_READ_BYTES); + if (ret) { + dev_err(data->dev, "failed to burst read sensor data\n"); + goto out; + } + + /* Temperature calculations */ + memcpy(&data->sensor_data[1], &data->buf[0], 3); + + /* Pressure calculations */ + memcpy(&data->sensor_data[0], &data->buf[3], 3); + + iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; static const u8 bmp580_chip_ids[] = { BMP580_CHIP_ID, BMP580_CHIP_ID_ALT }; +/* Instead of { 1000, 16 } we do this, to avoid overflow issues */ +static const int bmp580_temp_coeffs[] = { 125, 13 }; +static const int bmp580_press_coeffs[] = { 1, 64000}; const struct bmp280_chip_info bmp580_chip_info = { .id_reg = BMP580_REG_CHIP_ID, @@ -1837,8 +2229,9 @@ const struct bmp280_chip_info bmp580_chip_info = { .num_chip_id = ARRAY_SIZE(bmp580_chip_ids), .regmap_config = &bmp580_regmap_config, .start_up_time = 2000, - .channels = bmp380_channels, - .num_channels = 2, + .channels = bmp580_channels, + .num_channels = ARRAY_SIZE(bmp580_channels), + .avail_scan_masks = bmp280_avail_scan_masks, .oversampling_temp_avail = bmp580_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp580_oversampling_avail), @@ -1856,16 +2249,23 @@ const struct bmp280_chip_info bmp580_chip_info = { .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), .iir_filter_coeff_default = 2, + .temp_coeffs = bmp580_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL_LOG2, + .press_coeffs = bmp580_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + .chip_config = bmp580_chip_config, .read_temp = bmp580_read_temp, .read_press = bmp580_read_press, .preinit = bmp580_preinit, + + .trigger_handler = bmp580_trigger_handler, }; EXPORT_SYMBOL_NS(bmp580_chip_info, IIO_BMP280); static int bmp180_wait_for_eoc(struct bmp280_data *data, u8 ctrl_meas) { - const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; + static const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; unsigned int delay_us; unsigned int ctrl; int ret; @@ -2011,9 +2411,8 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, u32 adc_temp) return (bmp180_calc_t_fine(data, adc_temp) + 8) / 16; } -static int bmp180_read_temp(struct bmp280_data *data, int *val, int *val2) +static int bmp180_read_temp(struct bmp280_data *data, s32 *comp_temp) { - s32 comp_temp; u32 adc_temp; int ret; @@ -2021,10 +2420,9 @@ static int bmp180_read_temp(struct bmp280_data *data, int *val, int *val2) if (ret) return ret; - comp_temp = bmp180_compensate_temp(data, adc_temp); + *comp_temp = bmp180_compensate_temp(data, adc_temp); - *val = comp_temp * 100; - return IIO_VAL_INT; + return 0; } static int bmp180_read_press_adc(struct bmp280_data *data, u32 *adc_press) @@ -2040,7 +2438,7 @@ static int bmp180_read_press_adc(struct bmp280_data *data, u32 *adc_press) return ret; ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, - data->buf, sizeof(data->buf)); + data->buf, BMP280_NUM_PRESS_BYTES); if (ret) { dev_err(data->dev, "failed to read pressure\n"); return ret; @@ -2087,9 +2485,9 @@ static u32 bmp180_compensate_press(struct bmp280_data *data, u32 adc_press, return p + ((x1 + x2 + 3791) >> 4); } -static int bmp180_read_press(struct bmp280_data *data, int *val, int *val2) +static int bmp180_read_press(struct bmp280_data *data, u32 *comp_press) { - u32 comp_press, adc_press; + u32 adc_press; s32 t_fine; int ret; @@ -2101,12 +2499,9 @@ static int bmp180_read_press(struct bmp280_data *data, int *val, int *val2) if (ret) return ret; - comp_press = bmp180_compensate_press(data, adc_press, t_fine); - - *val = comp_press; - *val2 = 1000; + *comp_press = bmp180_compensate_press(data, adc_press, t_fine); - return IIO_VAL_FRACTIONAL; + return 0; } static int bmp180_chip_config(struct bmp280_data *data) @@ -2114,9 +2509,41 @@ static int bmp180_chip_config(struct bmp280_data *data) return 0; } +static irqreturn_t bmp180_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmp280_data *data = iio_priv(indio_dev); + int ret, chan_value; + + guard(mutex)(&data->lock); + + ret = bmp180_read_temp(data, &chan_value); + if (ret) + goto out; + + data->sensor_data[1] = chan_value; + + ret = bmp180_read_press(data, &chan_value); + if (ret) + goto out; + + data->sensor_data[0] = chan_value; + + iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; static const u8 bmp180_chip_ids[] = { BMP180_CHIP_ID }; +static const int bmp180_temp_coeffs[] = { 100, 1 }; +static const int bmp180_press_coeffs[] = { 1, 1000 }; const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, @@ -2125,7 +2552,8 @@ const struct bmp280_chip_info bmp180_chip_info = { .regmap_config = &bmp180_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, - .num_channels = 2, + .num_channels = ARRAY_SIZE(bmp280_channels), + .avail_scan_masks = bmp280_avail_scan_masks, .oversampling_temp_avail = bmp180_oversampling_temp_avail, .num_oversampling_temp_avail = @@ -2137,10 +2565,17 @@ const struct bmp280_chip_info bmp180_chip_info = { ARRAY_SIZE(bmp180_oversampling_press_avail), .oversampling_press_default = BMP180_MEAS_PRESS_8X, + .temp_coeffs = bmp180_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL, + .press_coeffs = bmp180_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + .chip_config = bmp180_chip_config, .read_temp = bmp180_read_temp, .read_press = bmp180_read_press, .read_calib = bmp180_read_calib, + + .trigger_handler = bmp180_trigger_handler, }; EXPORT_SYMBOL_NS(bmp180_chip_info, IIO_BMP280); @@ -2186,6 +2621,30 @@ static int bmp085_fetch_eoc_irq(struct device *dev, return 0; } +static int bmp280_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bmp280_data *data = iio_priv(indio_dev); + + pm_runtime_get_sync(data->dev); + + return 0; +} + +static int bmp280_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct bmp280_data *data = iio_priv(indio_dev); + + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + return 0; +} + +static const struct iio_buffer_setup_ops bmp280_buffer_setup_ops = { + .preenable = bmp280_buffer_preenable, + .postdisable = bmp280_buffer_postdisable, +}; + static void bmp280_pm_disable(void *data) { struct device *dev = data; @@ -2232,6 +2691,7 @@ int bmp280_common_probe(struct device *dev, /* Apply initial values from chip info structure */ indio_dev->channels = chip_info->channels; indio_dev->num_channels = chip_info->num_channels; + indio_dev->available_scan_masks = chip_info->avail_scan_masks; data->oversampling_press = chip_info->oversampling_press_default; data->oversampling_humid = chip_info->oversampling_humid_default; data->oversampling_temp = chip_info->oversampling_temp_default; @@ -2317,6 +2777,14 @@ int bmp280_common_probe(struct device *dev, "failed to read calibration coefficients\n"); } + ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, + iio_pollfunc_store_time, + data->chip_info->trigger_handler, + &bmp280_buffer_setup_ops); + if (ret) + return dev_err_probe(data->dev, ret, + "iio triggered buffer setup failed\n"); + /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this * however as it happens, the BMP085 shares the chip ID of BMP180 diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 34e3bc758493..5c3a63b4327c 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <linux/module.h> #include <linux/i2c.h> +#include <linux/module.h> #include <linux/regmap.h> #include "bmp280.h" diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c index fa52839474b1..d27d68edd906 100644 --- a/drivers/iio/pressure/bmp280-regmap.c +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -41,7 +41,7 @@ const struct regmap_config bmp180_regmap_config = { }; EXPORT_SYMBOL_NS(bmp180_regmap_config, IIO_BMP280); -static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) +static bool bme280_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case BMP280_REG_CONFIG: @@ -54,9 +54,37 @@ static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) } } +static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP280_REG_CONFIG: + case BMP280_REG_CTRL_MEAS: + case BMP280_REG_RESET: + return true; + default: + return false; + } +} + static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case BMP280_REG_TEMP_XLSB: + case BMP280_REG_TEMP_LSB: + case BMP280_REG_TEMP_MSB: + case BMP280_REG_PRESS_XLSB: + case BMP280_REG_PRESS_LSB: + case BMP280_REG_PRESS_MSB: + case BMP280_REG_STATUS: + return true; + default: + return false; + } +} + +static bool bme280_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { case BME280_REG_HUMIDITY_LSB: case BME280_REG_HUMIDITY_MSB: case BMP280_REG_TEMP_XLSB: @@ -71,7 +99,6 @@ static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) return false; } } - static bool bmp380_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -167,7 +194,7 @@ const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = BME280_REG_HUMIDITY_LSB, + .max_register = BMP280_REG_TEMP_XLSB, .cache_type = REGCACHE_RBTREE, .writeable_reg = bmp280_is_writeable_reg, @@ -175,6 +202,18 @@ const struct regmap_config bmp280_regmap_config = { }; EXPORT_SYMBOL_NS(bmp280_regmap_config, IIO_BMP280); +const struct regmap_config bme280_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BME280_REG_HUMIDITY_LSB, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bme280_is_writeable_reg, + .volatile_reg = bme280_is_volatile_reg, +}; +EXPORT_SYMBOL_NS(bme280_regmap_config, IIO_BMP280); + const struct regmap_config bmp380_regmap_config = { .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 62b4e58104cf..d18549d9bb64 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -5,10 +5,10 @@ * Inspired by the older BMP085 driver drivers/misc/bmp085-spi.c */ #include <linux/bits.h> -#include <linux/module.h> -#include <linux/spi/spi.h> #include <linux/err.h> +#include <linux/module.h> #include <linux/regmap.h> +#include <linux/spi/spi.h> #include "bmp280.h" @@ -40,14 +40,10 @@ static int bmp380_regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { struct spi_device *spi = to_spi_device(context); - u8 rx_buf[4]; + u8 rx_buf[BME280_BURST_READ_BYTES + 1]; ssize_t status; - /* - * Maximum number of consecutive bytes read for a temperature or - * pressure measurement is 3. - */ - if (val_size > 3) + if (val_size > BME280_BURST_READ_BYTES) return -EINVAL; /* @@ -64,14 +60,14 @@ static int bmp380_regmap_spi_read(void *context, const void *reg, return 0; } -static struct regmap_bus bmp280_regmap_bus = { +static const struct regmap_bus bmp280_regmap_bus = { .write = bmp280_regmap_spi_write, .read = bmp280_regmap_spi_read, .reg_format_endian_default = REGMAP_ENDIAN_BIG, .val_format_endian_default = REGMAP_ENDIAN_BIG, }; -static struct regmap_bus bmp380_regmap_bus = { +static const struct regmap_bus bmp380_regmap_bus = { .write = bmp280_regmap_spi_write, .read = bmp380_regmap_spi_read, .read_flag_mask = BIT(7), @@ -83,7 +79,7 @@ static int bmp280_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); const struct bmp280_chip_info *chip_info; - struct regmap_bus *bmp_regmap_bus; + struct regmap_bus const *bmp_regmap_bus; struct regmap *regmap; int ret; diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 7c30e4d523be..ccacc67c1473 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -1,10 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include <linux/bitops.h> #include <linux/device.h> -#include <linux/iio/iio.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> /* BMP580 specific registers */ #define BMP580_REG_CMD 0x7E @@ -304,6 +304,16 @@ #define BMP280_PRESS_SKIPPED 0x80000 #define BMP280_HUMIDITY_SKIPPED 0x8000 +/* Number of bytes for each value */ +#define BMP280_NUM_PRESS_BYTES 3 +#define BMP280_NUM_TEMP_BYTES 3 +#define BME280_NUM_HUMIDITY_BYTES 2 +#define BMP280_BURST_READ_BYTES (BMP280_NUM_PRESS_BYTES + \ + BMP280_NUM_TEMP_BYTES) +#define BME280_BURST_READ_BYTES (BMP280_NUM_PRESS_BYTES + \ + BMP280_NUM_TEMP_BYTES + \ + BME280_NUM_HUMIDITY_BYTES) + /* Core exported structs */ static const char *const bmp280_supply_names[] = { @@ -398,12 +408,18 @@ struct bmp280_data { int sampling_freq; /* + * Data to push to userspace triggered buffer. Up to 3 channels and + * s64 timestamp, aligned. + */ + s32 sensor_data[6] __aligned(8); + + /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ union { /* Sensor data buffer */ - u8 buf[3]; + u8 buf[BME280_BURST_READ_BYTES]; /* Calibration data buffers */ __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; @@ -425,6 +441,7 @@ struct bmp280_chip_info { const struct iio_chan_spec *channels; int num_channels; unsigned int start_up_time; + const unsigned long *avail_scan_masks; const int *oversampling_temp_avail; int num_oversampling_temp_avail; @@ -446,12 +463,21 @@ struct bmp280_chip_info { int num_sampling_freq_avail; int sampling_freq_default; + const int *temp_coeffs; + const int temp_coeffs_type; + const int *press_coeffs; + const int press_coeffs_type; + const int *humid_coeffs; + const int humid_coeffs_type; + int (*chip_config)(struct bmp280_data *data); - int (*read_temp)(struct bmp280_data *data, int *val, int *val2); - int (*read_press)(struct bmp280_data *data, int *val, int *val2); - int (*read_humid)(struct bmp280_data *data, int *val, int *val2); + int (*read_temp)(struct bmp280_data *data, s32 *adc_temp); + int (*read_press)(struct bmp280_data *data, u32 *adc_press); + int (*read_humid)(struct bmp280_data *data, u32 *adc_humidity); int (*read_calib)(struct bmp280_data *data); int (*preinit)(struct bmp280_data *data); + + irqreturn_t (*trigger_handler)(int irq, void *p); }; /* Chip infos for each variant */ @@ -464,6 +490,7 @@ extern const struct bmp280_chip_info bmp580_chip_info; /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; +extern const struct regmap_config bme280_regmap_config; extern const struct regmap_config bmp380_regmap_config; extern const struct regmap_config bmp580_regmap_config; diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 0bba4c5a8d40..c1cea9d40424 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -256,8 +256,7 @@ static irqreturn_t dlh_trigger_handler(int irq, void *private) if (ret) goto out; - for_each_set_bit(chn, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, chn) { memcpy(&tmp_buf[i++], &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES, DLH_NUM_DATA_BYTES); diff --git a/drivers/iio/pressure/sdp500.c b/drivers/iio/pressure/sdp500.c new file mode 100644 index 000000000000..6ff32e3fa637 --- /dev/null +++ b/drivers/iio/pressure/sdp500.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Sensirion sdp500 and sdp510 pressure sensors + * + * Datasheet: https://sensirion.com/resource/datasheet/sdp600 + */ + +#include <linux/i2c.h> +#include <linux/crc8.h> +#include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> +#include <linux/regulator/consumer.h> +#include <asm/unaligned.h> + +#define SDP500_CRC8_POLYNOMIAL 0x31 /* x8+x5+x4+1 (normalized to 0x31) */ +#define SDP500_READ_SIZE 3 + +#define SDP500_I2C_START_MEAS 0xF1 + +struct sdp500_data { + struct device *dev; +}; + +DECLARE_CRC8_TABLE(sdp500_crc8_table); + +static int sdp500_start_measurement(struct sdp500_data *data) +{ + struct i2c_client *client = to_i2c_client(data->dev); + + return i2c_smbus_write_byte(client, SDP500_I2C_START_MEAS); +} + +static const struct iio_chan_spec sdp500_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int sdp500_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + u8 rxbuf[SDP500_READ_SIZE]; + u8 received_crc, calculated_crc; + struct sdp500_data *data = iio_priv(indio_dev); + struct i2c_client *client = to_i2c_client(data->dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_master_recv(client, rxbuf, SDP500_READ_SIZE); + if (ret < 0) { + dev_err(data->dev, "Failed to receive data"); + return ret; + } + if (ret != SDP500_READ_SIZE) { + dev_err(data->dev, "Data is received wrongly"); + return -EIO; + } + + received_crc = rxbuf[2]; + calculated_crc = crc8(sdp500_crc8_table, rxbuf, + sizeof(rxbuf) - 1, 0x00); + if (received_crc != calculated_crc) { + dev_err(data->dev, + "calculated crc = 0x%.2X, received 0x%.2X", + calculated_crc, received_crc); + return -EIO; + } + + *val = get_unaligned_be16(rxbuf); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1; + *val2 = 60; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static const struct iio_info sdp500_info = { + .read_raw = &sdp500_read_raw, +}; + +static int sdp500_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct sdp500_data *data; + struct device *dev = &client->dev; + int ret; + u8 rxbuf[SDP500_READ_SIZE]; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable regulator\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + /* has to be done before the first i2c communication */ + crc8_populate_msb(sdp500_crc8_table, SDP500_CRC8_POLYNOMIAL); + + data = iio_priv(indio_dev); + data->dev = dev; + + indio_dev->name = "sdp500"; + indio_dev->channels = sdp500_channels; + indio_dev->info = &sdp500_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = ARRAY_SIZE(sdp500_channels); + + ret = sdp500_start_measurement(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to start measurement"); + + /* First measurement is not correct, read it out to get rid of it */ + i2c_master_recv(client, rxbuf, SDP500_READ_SIZE); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register indio_dev"); + + return 0; +} + +static const struct i2c_device_id sdp500_id[] = { + { "sdp500" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sdp500_id); + +static const struct of_device_id sdp500_of_match[] = { + { .compatible = "sensirion,sdp500" }, + { } +}; +MODULE_DEVICE_TABLE(of, sdp500_of_match); + +static struct i2c_driver sdp500_driver = { + .driver = { + .name = "sensirion,sdp500", + .of_match_table = sdp500_of_match, + }, + .probe = sdp500_probe, + .id_table = sdp500_id, +}; +module_i2c_driver(sdp500_driver); + +MODULE_AUTHOR("Thomas Sioutas <thomas.sioutas@prodrive-technologies.com>"); +MODULE_DESCRIPTION("Driver for Sensirion SDP500 differential pressure sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 2ca3b0bc5eba..31c679074b25 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -32,6 +32,20 @@ config CROS_EC_MKBP_PROXIMITY To compile this driver as a module, choose M here: the module will be called cros_ec_mkbp_proximity. +config HX9023S + tristate "TYHX HX9023S SAR sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for TYHX HX9023S capacitive SAR sensor. + This driver supports the TYHX HX9023S capacitive + SAR sensors. This sensors is used for proximity detection applications. + + To compile this driver as a module, choose M here: the + module will be called hx9023s. + config IRSD200 tristate "Murata IRS-D200 PIR sensor" select IIO_BUFFER @@ -219,4 +233,15 @@ config VL53L0X_I2C To compile this driver as a module, choose M here: the module will be called vl53l0x-i2c. +config AW96103 + tristate "AW96103/AW96105 Awinic proximity sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for Awinic's AW96103/AW96105 capacitive + proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called aw96103. + endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index f36598380446..c5e76995764a 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -6,6 +6,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o +obj-$(CONFIG_HX9023S) += hx9023s.o obj-$(CONFIG_IRSD200) += irsd200.o obj-$(CONFIG_ISL29501) += isl29501.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o @@ -21,4 +22,5 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o +obj-$(CONFIG_AW96103) += aw96103.o diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c new file mode 100644 index 000000000000..db9d78e961fd --- /dev/null +++ b/drivers/iio/proximity/aw96103.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AWINIC aw96103 proximity sensor driver + * + * Author: Wang Shuaijie <wangshuaijie@awinic.com> + * + * Copyright (c) 2024 awinic Technology CO., LTD + */ +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#define AW_DATA_PROCESS_FACTOR 1024 +#define AW96103_CHIP_ID 0xa961 +#define AW96103_BIN_VALID_DATA_OFFSET 64 +#define AW96103_BIN_DATA_LEN_OFFSET 16 +#define AW96103_BIN_DATA_REG_NUM_SIZE 4 +#define AW96103_BIN_CHIP_TYPE_SIZE 8 +#define AW96103_BIN_CHIP_TYPE_OFFSET 24 + +#define AW96103_REG_SCANCTRL0 0x0000 +#define AW96103_REG_STAT0 0x0090 +#define AW96103_REG_BLFILT_CH0 0x00A8 +#define AW96103_REG_BLRSTRNG_CH0 0x00B4 +#define AW96103_REG_DIFF_CH0 0x0240 +#define AW96103_REG_FWVER2 0x0410 +#define AW96103_REG_CMD 0xF008 +#define AW96103_REG_IRQSRC 0xF080 +#define AW96103_REG_IRQEN 0xF084 +#define AW96103_REG_RESET 0xFF0C +#define AW96103_REG_CHIPID 0xFF10 +#define AW96103_REG_EEDA0 0x0408 +#define AW96103_REG_EEDA1 0x040C +#define AW96103_REG_PROXCTRL_CH0 0x00B0 +#define AW96103_REG_PROXTH0_CH0 0x00B8 +#define AW96103_PROXTH_CH_STEP 0x3C +#define AW96103_THHYST_MASK GENMASK(13, 12) +#define AW96103_INDEB_MASK GENMASK(11, 10) +#define AW96103_OUTDEB_MASK GENMASK(9, 8) +#define AW96103_INITOVERIRQ_MASK BIT(0) +#define AW96103_BLFILT_CH_STEP 0x3C +#define AW96103_BLRSTRNG_MASK GENMASK(5, 0) +#define AW96103_CHIPID_MASK GENMASK(31, 16) +#define AW96103_BLERRTRIG_MASK BIT(25) +#define AW96103_CHAN_EN_MASK GENMASK(5, 0) +#define AW96103_REG_PROXCTRL_CH(x) \ + (AW96103_REG_PROXCTRL_CH0 + (x) * AW96103_PROXTH_CH_STEP) + +#define AW96103_REG_PROXTH0_CH(x) \ + (AW96103_REG_PROXTH0_CH0 + (x) * AW96103_PROXTH_CH_STEP) + +/** + * struct aw_bin - Store the data obtained from parsing the configuration file. + * @chip_type: Frame header information-chip type + * @valid_data_len: Length of valid data obtained after parsing + * @valid_data_addr: The offset address of the valid data obtained + * after parsing relative to info + * @len: The size of the bin file obtained from the firmware + * @data: Store the bin file obtained from the firmware + */ +struct aw_bin { + unsigned char chip_type[8]; + unsigned int valid_data_len; + unsigned int valid_data_addr; + unsigned int len; + unsigned char data[] __counted_by(len); +}; + +enum aw96103_sar_vers { + AW96103 = 2, + AW96103A = 6, + AW96103B = 0xa, +}; + +enum aw96103_operation_mode { + AW96103_ACTIVE_MODE = 1, + AW96103_SLEEP_MODE = 2, + AW96103_DEEPSLEEP_MODE = 3, + AW96103B_DEEPSLEEP_MODE = 4, +}; + +enum aw96103_sensor_type { + AW96103_VAL, + AW96105_VAL, +}; + +struct aw_channels_info { + bool used; + unsigned int old_irq_status; +}; + +struct aw_chip_info { + const char *name; + struct iio_chan_spec const *channels; + int num_channels; +}; + +struct aw96103 { + unsigned int hostirqen; + struct regmap *regmap; + struct device *dev; + /* + * There is one more logical channel than the actual channels, + * and the extra logical channel is used for temperature detection + * but not for status detection. The specific channel used for + * temperature detection is determined by the register configuration. + */ + struct aw_channels_info channels_arr[6]; + unsigned int max_channels; + unsigned int chan_en; +}; + +static const unsigned int aw96103_reg_default[] = { + 0x0000, 0x00003f3f, 0x0004, 0x00000064, 0x0008, 0x0017c11e, + 0x000c, 0x05000000, 0x0010, 0x00093ffd, 0x0014, 0x19240009, + 0x0018, 0xd81c0207, 0x001c, 0xff000000, 0x0020, 0x00241900, + 0x0024, 0x00093ff7, 0x0028, 0x58020009, 0x002c, 0xd81c0207, + 0x0030, 0xff000000, 0x0034, 0x00025800, 0x0038, 0x00093fdf, + 0x003c, 0x7d3b0009, 0x0040, 0xd81c0207, 0x0044, 0xff000000, + 0x0048, 0x003b7d00, 0x004c, 0x00093f7f, 0x0050, 0xe9310009, + 0x0054, 0xd81c0207, 0x0058, 0xff000000, 0x005c, 0x0031e900, + 0x0060, 0x00093dff, 0x0064, 0x1a0c0009, 0x0068, 0xd81c0207, + 0x006c, 0xff000000, 0x0070, 0x000c1a00, 0x0074, 0x80093fff, + 0x0078, 0x043d0009, 0x007c, 0xd81c0207, 0x0080, 0xff000000, + 0x0084, 0x003d0400, 0x00a0, 0xe6400000, 0x00a4, 0x00000000, + 0x00a8, 0x010408d2, 0x00ac, 0x00000000, 0x00b0, 0x00000000, + 0x00b8, 0x00005fff, 0x00bc, 0x00000000, 0x00c0, 0x00000000, + 0x00c4, 0x00000000, 0x00c8, 0x00000000, 0x00cc, 0x00000000, + 0x00d0, 0x00000000, 0x00d4, 0x00000000, 0x00d8, 0x00000000, + 0x00dc, 0xe6447800, 0x00e0, 0x78000000, 0x00e4, 0x010408d2, + 0x00e8, 0x00000000, 0x00ec, 0x00000000, 0x00f4, 0x00005fff, + 0x00f8, 0x00000000, 0x00fc, 0x00000000, 0x0100, 0x00000000, + 0x0104, 0x00000000, 0x0108, 0x00000000, 0x010c, 0x02000000, + 0x0110, 0x00000000, 0x0114, 0x00000000, 0x0118, 0xe6447800, + 0x011c, 0x78000000, 0x0120, 0x010408d2, 0x0124, 0x00000000, + 0x0128, 0x00000000, 0x0130, 0x00005fff, 0x0134, 0x00000000, + 0x0138, 0x00000000, 0x013c, 0x00000000, 0x0140, 0x00000000, + 0x0144, 0x00000000, 0x0148, 0x02000000, 0x014c, 0x00000000, + 0x0150, 0x00000000, 0x0154, 0xe6447800, 0x0158, 0x78000000, + 0x015c, 0x010408d2, 0x0160, 0x00000000, 0x0164, 0x00000000, + 0x016c, 0x00005fff, 0x0170, 0x00000000, 0x0174, 0x00000000, + 0x0178, 0x00000000, 0x017c, 0x00000000, 0x0180, 0x00000000, + 0x0184, 0x02000000, 0x0188, 0x00000000, 0x018c, 0x00000000, + 0x0190, 0xe6447800, 0x0194, 0x78000000, 0x0198, 0x010408d2, + 0x019c, 0x00000000, 0x01a0, 0x00000000, 0x01a8, 0x00005fff, + 0x01ac, 0x00000000, 0x01b0, 0x00000000, 0x01b4, 0x00000000, + 0x01b8, 0x00000000, 0x01bc, 0x00000000, 0x01c0, 0x02000000, + 0x01c4, 0x00000000, 0x01c8, 0x00000000, 0x01cc, 0xe6407800, + 0x01d0, 0x78000000, 0x01d4, 0x010408d2, 0x01d8, 0x00000000, + 0x01dc, 0x00000000, 0x01e4, 0x00005fff, 0x01e8, 0x00000000, + 0x01ec, 0x00000000, 0x01f0, 0x00000000, 0x01f4, 0x00000000, + 0x01f8, 0x00000000, 0x01fc, 0x02000000, 0x0200, 0x00000000, + 0x0204, 0x00000000, 0x0208, 0x00000008, 0x020c, 0x0000000d, + 0x41fc, 0x00000000, 0x4400, 0x00000000, 0x4410, 0x00000000, + 0x4420, 0x00000000, 0x4430, 0x00000000, 0x4440, 0x00000000, + 0x4450, 0x00000000, 0x4460, 0x00000000, 0x4470, 0x00000000, + 0xf080, 0x00003018, 0xf084, 0x00000fff, 0xf800, 0x00000000, + 0xf804, 0x00002e00, 0xf8d0, 0x00000001, 0xf8d4, 0x00000000, + 0xff00, 0x00000301, 0xff0c, 0x01000000, 0xffe0, 0x00000000, + 0xfff4, 0x00004011, 0x0090, 0x00000000, 0x0094, 0x00000000, + 0x0098, 0x00000000, 0x009c, 0x3f3f3f3f, +}; + +static const struct iio_event_spec aw_common_events[3] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_VALUE), + } +}; + +#define AW_IIO_CHANNEL(idx) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = aw_common_events, \ + .num_event_specs = ARRAY_SIZE(aw_common_events), \ +} \ + +static const struct iio_chan_spec aw96103_channels[] = { + AW_IIO_CHANNEL(0), + AW_IIO_CHANNEL(1), + AW_IIO_CHANNEL(2), + AW_IIO_CHANNEL(3), +}; + +static const struct iio_chan_spec aw96105_channels[] = { + AW_IIO_CHANNEL(0), + AW_IIO_CHANNEL(1), + AW_IIO_CHANNEL(2), + AW_IIO_CHANNEL(3), + AW_IIO_CHANNEL(4), + AW_IIO_CHANNEL(5), +}; + +static const struct aw_chip_info aw_chip_info_tbl[] = { + [AW96103_VAL] = { + .name = "aw96103_sensor", + .channels = aw96103_channels, + .num_channels = ARRAY_SIZE(aw96103_channels), + }, + [AW96105_VAL] = { + .name = "aw96105_sensor", + .channels = aw96105_channels, + .num_channels = ARRAY_SIZE(aw96105_channels), + }, +}; + +static void aw96103_parsing_bin_file(struct aw_bin *bin) +{ + bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET; + bin->valid_data_len = + *(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) - + AW96103_BIN_DATA_REG_NUM_SIZE; + memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET, + AW96103_BIN_CHIP_TYPE_SIZE); +} + +static const struct regmap_config aw96103_regmap_confg = { + .reg_bits = 16, + .val_bits = 32, +}; + +static int aw96103_get_diff_raw(struct aw96103 *aw96103, unsigned int chan, + int *buf) +{ + u32 data; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_DIFF_CH0 + chan * 4, &data); + if (ret) + return ret; + *buf = (int)(data / AW_DATA_PROCESS_FACTOR); + + return 0; +} + +static int aw96103_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = aw96103_get_diff_raw(aw96103, chan->channel, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int aw96103_read_thresh(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXTH0_CH(chan->channel), val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int aw96103_read_out_debounce(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, + int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_OUTDEB_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_read_in_debounce(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_INDEB_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_read_hysteresis(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_THHYST_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_read_event_val(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return aw96103_read_thresh(aw96103, chan, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return aw96103_read_out_debounce(aw96103, chan, val); + case IIO_EV_DIR_FALLING: + return aw96103_read_in_debounce(aw96103, chan, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return aw96103_read_hysteresis(aw96103, chan, val); + default: + return -EINVAL; + } +} + +static int aw96103_write_event_val(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return regmap_write(aw96103->regmap, + AW96103_REG_PROXTH0_CH(chan->channel), val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_OUTDEB_MASK, + FIELD_PREP(AW96103_OUTDEB_MASK, val)); + + case IIO_EV_DIR_FALLING: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_INDEB_MASK, + FIELD_PREP(AW96103_INDEB_MASK, val)); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_THHYST_MASK, + FIELD_PREP(AW96103_THHYST_MASK, val)); + default: + return -EINVAL; + } +} + +static int aw96103_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + return aw96103->channels_arr[chan->channel].used; +} + +static int aw96103_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + aw96103->channels_arr[chan->channel].used = !!state; + + return regmap_update_bits(aw96103->regmap, AW96103_REG_SCANCTRL0, + BIT(chan->channel), + state ? BIT(chan->channel) : 0); +} + +static struct iio_info iio_info = { + .read_raw = aw96103_read_raw, + .read_event_value = aw96103_read_event_val, + .write_event_value = aw96103_write_event_val, + .read_event_config = aw96103_read_event_config, + .write_event_config = aw96103_write_event_config, +}; + +static int aw96103_channel_scan_start(struct aw96103 *aw96103) +{ + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_CMD, + AW96103_ACTIVE_MODE); + if (ret) + return ret; + + return regmap_write(aw96103->regmap, AW96103_REG_IRQEN, + aw96103->hostirqen); +} + +static int aw96103_reg_version_comp(struct aw96103 *aw96103, + struct aw_bin *aw_bin) +{ + u32 blfilt1_data, fw_ver; + unsigned char i; + int ret; + + ret = regmap_read(aw96103->regmap, AW96103_REG_FWVER2, &fw_ver); + if (ret) + return ret; + /* + * If the chip version is AW96103A and the loaded register + * configuration file is for AW96103, special handling of the + * AW96103_REG_BLRSTRNG_CH0 register is required. + */ + if ((fw_ver != AW96103A) || (aw_bin->chip_type[7] != '\0')) + return 0; + + for (i = 0; i < aw96103->max_channels; i++) { + ret = regmap_read(aw96103->regmap, + AW96103_REG_BLFILT_CH0 + (AW96103_BLFILT_CH_STEP * i), + &blfilt1_data); + if (ret) + return ret; + if (FIELD_GET(AW96103_BLERRTRIG_MASK, blfilt1_data) != 1) + return 0; + + ret = regmap_update_bits(aw96103->regmap, + AW96103_REG_BLRSTRNG_CH0 + (AW96103_BLFILT_CH_STEP * i), + AW96103_BLRSTRNG_MASK, 1 << i); + if (ret) + return ret; + } + + return 0; +} + +static int aw96103_bin_valid_loaded(struct aw96103 *aw96103, + struct aw_bin *aw_bin_data_s) +{ + unsigned int start_addr = aw_bin_data_s->valid_data_addr; + u32 i, reg_data; + u16 reg_addr; + int ret; + + for (i = 0; i < aw_bin_data_s->valid_data_len; + i += 6, start_addr += 6) { + reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr); + reg_data = get_unaligned_le32(aw_bin_data_s->data + + start_addr + 2); + if ((reg_addr == AW96103_REG_EEDA0) || + (reg_addr == AW96103_REG_EEDA1)) + continue; + if (reg_addr == AW96103_REG_IRQEN) { + aw96103->hostirqen = reg_data; + continue; + } + if (reg_addr == AW96103_REG_SCANCTRL0) + aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK, + reg_data); + + ret = regmap_write(aw96103->regmap, reg_addr, reg_data); + if (ret < 0) + return ret; + } + + ret = aw96103_reg_version_comp(aw96103, aw_bin_data_s); + if (ret) + return ret; + + return aw96103_channel_scan_start(aw96103); +} + +static int aw96103_para_loaded(struct aw96103 *aw96103) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(aw96103_reg_default); i += 2) { + ret = regmap_write(aw96103->regmap, + (u16)aw96103_reg_default[i], + (u32)aw96103_reg_default[i + 1]); + if (ret) + return ret; + if (aw96103_reg_default[i] == AW96103_REG_IRQEN) + aw96103->hostirqen = aw96103_reg_default[i + 1]; + else if (aw96103_reg_default[i] == AW96103_REG_SCANCTRL0) + aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK, + aw96103_reg_default[i + 1]); + } + + return aw96103_channel_scan_start(aw96103); +} + +static int aw96103_cfg_all_loaded(const struct firmware *cont, + struct aw96103 *aw96103) +{ + if (!cont) + return -EINVAL; + + struct aw_bin *aw_bin __free(kfree) = + kzalloc(cont->size + sizeof(*aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->len = cont->size; + memcpy(aw_bin->data, cont->data, cont->size); + release_firmware(cont); + aw96103_parsing_bin_file(aw_bin); + + return aw96103_bin_valid_loaded(aw96103, aw_bin); +} + +static void aw96103_cfg_update(const struct firmware *fw, void *data) +{ + struct aw96103 *aw96103 = data; + int ret, i; + + if (!fw || !fw->data) { + dev_err(aw96103->dev, "No firmware.\n"); + return; + } + + ret = aw96103_cfg_all_loaded(fw, aw96103); + /* + * If loading the register configuration file fails, + * load the default register configuration in the driver to + * ensure the basic functionality of the device. + */ + if (ret) { + ret = aw96103_para_loaded(aw96103); + if (ret) { + dev_err(aw96103->dev, "load param error.\n"); + return; + } + } + + for (i = 0; i < aw96103->max_channels; i++) { + if ((aw96103->chan_en >> i) & 0x01) + aw96103->channels_arr[i].used = true; + else + aw96103->channels_arr[i].used = false; + } +} + +static int aw96103_sw_reset(struct aw96103 *aw96103) +{ + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_RESET, 0); + /* + * After reset, the initialization process starts to perform and + * it will last for a bout 20ms. + */ + msleep(20); + + return ret; +} + +enum aw96103_irq_trigger_position { + FAR = 0, + TRIGGER_TH0 = 0x01, + TRIGGER_TH1 = 0x03, + TRIGGER_TH2 = 0x07, + TRIGGER_TH3 = 0x0f, +}; + +static irqreturn_t aw96103_irq(int irq, void *data) +{ + unsigned int irq_status, curr_status_val, curr_status; + struct iio_dev *indio_dev = data; + struct aw96103 *aw96103 = iio_priv(indio_dev); + int ret, i; + + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status); + if (ret) + return IRQ_HANDLED; + + ret = regmap_read(aw96103->regmap, AW96103_REG_STAT0, &curr_status_val); + if (ret) + return IRQ_HANDLED; + + /* + * Iteratively analyze the interrupt status of different channels, + * with each channel having 4 interrupt states. + */ + for (i = 0; i < aw96103->max_channels; i++) { + if (!aw96103->channels_arr[i].used) + continue; + + curr_status = (((curr_status_val >> (24 + i)) & 0x1)) | + (((curr_status_val >> (16 + i)) & 0x1) << 1) | + (((curr_status_val >> (8 + i)) & 0x1) << 2) | + (((curr_status_val >> i) & 0x1) << 3); + if (aw96103->channels_arr[i].old_irq_status == curr_status) + continue; + + switch (curr_status) { + case FAR: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + break; + case TRIGGER_TH0: + case TRIGGER_TH1: + case TRIGGER_TH2: + case TRIGGER_TH3: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + break; + default: + return IRQ_HANDLED; + } + aw96103->channels_arr[i].old_irq_status = curr_status; + } + + return IRQ_HANDLED; +} + +static int aw96103_interrupt_init(struct iio_dev *indio_dev, + struct i2c_client *i2c) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + unsigned int irq_status; + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_IRQEN, 0); + if (ret) + return ret; + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status); + if (ret) + return ret; + ret = devm_request_threaded_irq(aw96103->dev, i2c->irq, NULL, + aw96103_irq, IRQF_ONESHOT, + "aw96103_irq", indio_dev); + if (ret) + return ret; + + return regmap_write(aw96103->regmap, AW96103_REG_IRQEN, + aw96103->hostirqen); +} + +static int aw96103_wait_chip_init(struct aw96103 *aw96103) +{ + unsigned int cnt = 20; + u32 reg_data; + int ret; + + while (cnt--) { + /* + * The device should generate an initialization completion + * interrupt within 20ms. + */ + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, + ®_data); + if (ret) + return ret; + + if (FIELD_GET(AW96103_INITOVERIRQ_MASK, reg_data)) + return 0; + fsleep(1000); + } + + return -ETIMEDOUT; +} + +static int aw96103_read_chipid(struct aw96103 *aw96103) +{ + unsigned char cnt = 0; + u32 reg_val = 0; + int ret; + + while (cnt < 3) { + /* + * This retry mechanism and the subsequent delay are just + * attempts to read the chip ID as much as possible, + * preventing occasional communication failures from causing + * the chip ID read to fail. + */ + ret = regmap_read(aw96103->regmap, AW96103_REG_CHIPID, + ®_val); + if (ret < 0) { + cnt++; + fsleep(2000); + continue; + } + break; + } + if (cnt == 3) + return -ETIMEDOUT; + + if (FIELD_GET(AW96103_CHIPID_MASK, reg_val) != AW96103_CHIP_ID) + dev_info(aw96103->dev, + "unexpected chipid, id=0x%08X\n", reg_val); + + return 0; +} + +static int aw96103_i2c_probe(struct i2c_client *i2c) +{ + const struct aw_chip_info *chip_info; + struct iio_dev *indio_dev; + struct aw96103 *aw96103; + int ret; + + indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*aw96103)); + if (!indio_dev) + return -ENOMEM; + + aw96103 = iio_priv(indio_dev); + aw96103->dev = &i2c->dev; + chip_info = i2c_get_match_data(i2c); + aw96103->max_channels = chip_info->num_channels; + + aw96103->regmap = devm_regmap_init_i2c(i2c, &aw96103_regmap_confg); + if (IS_ERR(aw96103->regmap)) + return PTR_ERR(aw96103->regmap); + + ret = devm_regulator_get_enable(aw96103->dev, "vcc"); + if (ret < 0) + return ret; + + ret = aw96103_read_chipid(aw96103); + if (ret) + return ret; + + ret = aw96103_sw_reset(aw96103); + if (ret) + return ret; + + ret = aw96103_wait_chip_init(aw96103); + if (ret) + return ret; + + ret = request_firmware_nowait(THIS_MODULE, true, "aw96103_0.bin", + aw96103->dev, GFP_KERNEL, aw96103, + aw96103_cfg_update); + if (ret) + return ret; + + ret = aw96103_interrupt_init(indio_dev, i2c); + if (ret) + return ret; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = chip_info->num_channels; + indio_dev->channels = chip_info->channels; + indio_dev->info = &iio_info; + indio_dev->name = chip_info->name; + + return devm_iio_device_register(aw96103->dev, indio_dev); +} + +static const struct of_device_id aw96103_dt_match[] = { + { + .compatible = "awinic,aw96103", + .data = &aw_chip_info_tbl[AW96103_VAL] + }, + { + .compatible = "awinic,aw96105", + .data = &aw_chip_info_tbl[AW96105_VAL] + }, + { } +}; +MODULE_DEVICE_TABLE(of, aw96103_dt_match); + +static const struct i2c_device_id aw96103_i2c_id[] = { + { "aw96103", (kernel_ulong_t)&aw_chip_info_tbl[AW96103_VAL] }, + { "aw96105", (kernel_ulong_t)&aw_chip_info_tbl[AW96105_VAL] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw96103_i2c_id); + +static struct i2c_driver aw96103_i2c_driver = { + .driver = { + .name = "aw96103_sensor", + .of_match_table = aw96103_dt_match, + }, + .probe = aw96103_i2c_probe, + .id_table = aw96103_i2c_id, +}; +module_i2c_driver(aw96103_i2c_driver); + +MODULE_AUTHOR("Wang Shuaijie <wangshuaijie@awinic.com>"); +MODULE_DESCRIPTION("Driver for Awinic AW96103 proximity sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c index 4df506bb8b38..cff57d851762 100644 --- a/drivers/iio/proximity/cros_ec_mkbp_proximity.c +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -6,10 +6,10 @@ */ #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/notifier.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c new file mode 100644 index 000000000000..8b9f84400e00 --- /dev/null +++ b/drivers/iio/proximity/hx9023s.c @@ -0,0 +1,1144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 NanjingTianyihexin Electronics Ltd. + * http://www.tianyihexin.com + * + * Driver for NanjingTianyihexin HX9023S Cap Sensor. + * Datasheet available at: + * http://www.tianyihexin.com/ueditor/php/upload/file/20240614/1718336303992081.pdf + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/types.h> + +#define HX9023S_CHIP_ID 0x1D +#define HX9023S_CH_NUM 5 +#define HX9023S_POS 0x03 +#define HX9023S_NEG 0x02 +#define HX9023S_NOT_CONNECTED 16 + +#define HX9023S_GLOBAL_CTRL0 0x00 +#define HX9023S_PRF_CFG 0x02 +#define HX9023S_CH0_CFG_7_0 0x03 +#define HX9023S_CH4_CFG_9_8 0x0C +#define HX9023S_RANGE_7_0 0x0D +#define HX9023S_RANGE_9_8 0x0E +#define HX9023S_RANGE_18_16 0x0F +#define HX9023S_AVG0_NOSR0_CFG 0x10 +#define HX9023S_NOSR12_CFG 0x11 +#define HX9023S_NOSR34_CFG 0x12 +#define HX9023S_AVG12_CFG 0x13 +#define HX9023S_AVG34_CFG 0x14 +#define HX9023S_OFFSET_DAC0_7_0 0x15 +#define HX9023S_OFFSET_DAC4_9_8 0x1E +#define HX9023S_SAMPLE_NUM_7_0 0x1F +#define HX9023S_INTEGRATION_NUM_7_0 0x21 +#define HX9023S_CH_NUM_CFG 0x24 +#define HX9023S_LP_ALP_4_CFG 0x29 +#define HX9023S_LP_ALP_1_0_CFG 0x2A +#define HX9023S_LP_ALP_3_2_CFG 0x2B +#define HX9023S_UP_ALP_1_0_CFG 0x2C +#define HX9023S_UP_ALP_3_2_CFG 0x2D +#define HX9023S_DN_UP_ALP_0_4_CFG 0x2E +#define HX9023S_DN_ALP_2_1_CFG 0x2F +#define HX9023S_DN_ALP_4_3_CFG 0x30 +#define HX9023S_RAW_BL_RD_CFG 0x38 +#define HX9023S_INTERRUPT_CFG 0x39 +#define HX9023S_INTERRUPT_CFG1 0x3A +#define HX9023S_CALI_DIFF_CFG 0x3B +#define HX9023S_DITHER_CFG 0x3C +#define HX9023S_DEVICE_ID 0x60 +#define HX9023S_PROX_STATUS 0x6B +#define HX9023S_PROX_INT_HIGH_CFG 0x6C +#define HX9023S_PROX_INT_LOW_CFG 0x6D +#define HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 0x80 +#define HX9023S_PROX_LOW_DIFF_CFG_CH0_0 0x88 +#define HX9023S_PROX_LOW_DIFF_CFG_CH3_1 0x8F +#define HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 0x9E +#define HX9023S_PROX_HIGH_DIFF_CFG_CH4_1 0x9F +#define HX9023S_PROX_LOW_DIFF_CFG_CH4_0 0xA2 +#define HX9023S_PROX_LOW_DIFF_CFG_CH4_1 0xA3 +#define HX9023S_CAP_INI_CH4_0 0xB3 +#define HX9023S_LP_DIFF_CH4_2 0xBA +#define HX9023S_RAW_BL_CH4_0 0xB5 +#define HX9023S_LP_DIFF_CH4_0 0xB8 +#define HX9023S_DSP_CONFIG_CTRL1 0xC8 +#define HX9023S_CAP_INI_CH0_0 0xE0 +#define HX9023S_RAW_BL_CH0_0 0xE8 +#define HX9023S_LP_DIFF_CH0_0 0xF4 +#define HX9023S_LP_DIFF_CH3_2 0xFF + +#define HX9023S_DATA_LOCK_MASK BIT(4) +#define HX9023S_INTERRUPT_MASK GENMASK(9, 0) +#define HX9023S_PROX_DEBOUNCE_MASK GENMASK(3, 0) + +struct hx9023s_ch_data { + s16 raw; /* Raw Data*/ + s16 lp; /* Low Pass Filter Data*/ + s16 bl; /* Base Line Data */ + s16 diff; /* Difference of Low Pass Data and Base Line Data */ + + struct { + unsigned int near; + unsigned int far; + } thres; + + u16 dac; + u8 channel_positive; + u8 channel_negative; + bool sel_bl; + bool sel_raw; + bool sel_diff; + bool sel_lp; + bool enable; +}; + +struct hx9023s_data { + struct iio_trigger *trig; + struct regmap *regmap; + unsigned long chan_prox_stat; + unsigned long chan_read; + unsigned long chan_event; + unsigned long ch_en_stat; + unsigned long chan_in_use; + unsigned int prox_state_reg; + bool trigger_enabled; + + struct { + __le16 channels[HX9023S_CH_NUM]; + s64 ts __aligned(8); + } buffer; + + /* + * Serialize access to registers below: + * HX9023S_PROX_INT_LOW_CFG, + * HX9023S_PROX_INT_HIGH_CFG, + * HX9023S_INTERRUPT_CFG, + * HX9023S_CH_NUM_CFG + * Serialize access to channel configuration in + * hx9023s_push_events and hx9023s_trigger_handler. + */ + struct mutex mutex; + struct hx9023s_ch_data ch_data[HX9023S_CH_NUM]; +}; + +static const struct reg_sequence hx9023s_reg_init_list[] = { + /* scan period */ + REG_SEQ0(HX9023S_PRF_CFG, 0x17), + + /* full scale of conversion phase of each channel */ + REG_SEQ0(HX9023S_RANGE_7_0, 0x11), + REG_SEQ0(HX9023S_RANGE_9_8, 0x02), + REG_SEQ0(HX9023S_RANGE_18_16, 0x00), + + /* ADC average number and OSR number of each channel */ + REG_SEQ0(HX9023S_AVG0_NOSR0_CFG, 0x71), + REG_SEQ0(HX9023S_NOSR12_CFG, 0x44), + REG_SEQ0(HX9023S_NOSR34_CFG, 0x00), + REG_SEQ0(HX9023S_AVG12_CFG, 0x33), + REG_SEQ0(HX9023S_AVG34_CFG, 0x00), + + /* sample & integration frequency of the ADC */ + REG_SEQ0(HX9023S_SAMPLE_NUM_7_0, 0x65), + REG_SEQ0(HX9023S_INTEGRATION_NUM_7_0, 0x65), + + /* coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_LP_ALP_1_0_CFG, 0x22), + REG_SEQ0(HX9023S_LP_ALP_3_2_CFG, 0x22), + REG_SEQ0(HX9023S_LP_ALP_4_CFG, 0x02), + + /* up coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_UP_ALP_1_0_CFG, 0x88), + REG_SEQ0(HX9023S_UP_ALP_3_2_CFG, 0x88), + REG_SEQ0(HX9023S_DN_UP_ALP_0_4_CFG, 0x18), + + /* down coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_DN_ALP_2_1_CFG, 0x11), + REG_SEQ0(HX9023S_DN_ALP_4_3_CFG, 0x11), + + /* selection of data for the Data Mux Register to output data */ + REG_SEQ0(HX9023S_RAW_BL_RD_CFG, 0xF0), + + /* enable the interrupt function */ + REG_SEQ0(HX9023S_INTERRUPT_CFG, 0xFF), + REG_SEQ0(HX9023S_INTERRUPT_CFG1, 0x3B), + REG_SEQ0(HX9023S_DITHER_CFG, 0x21), + + /* threshold of the offset compensation */ + REG_SEQ0(HX9023S_CALI_DIFF_CFG, 0x07), + + /* proximity persistency number(near & far) */ + REG_SEQ0(HX9023S_PROX_INT_HIGH_CFG, 0x01), + REG_SEQ0(HX9023S_PROX_INT_LOW_CFG, 0x01), + + /* disable the data lock */ + REG_SEQ0(HX9023S_DSP_CONFIG_CTRL1, 0x00), +}; + +static const struct iio_event_spec hx9023s_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_VALUE), + + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define HX9023S_CHANNEL(idx) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .indexed = 1, \ + .channel = idx, \ + .address = 0, \ + .event_spec = hx9023s_events, \ + .num_event_specs = ARRAY_SIZE(hx9023s_events), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec hx9023s_channels[] = { + HX9023S_CHANNEL(0), + HX9023S_CHANNEL(1), + HX9023S_CHANNEL(2), + HX9023S_CHANNEL(3), + HX9023S_CHANNEL(4), + IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const unsigned int hx9023s_samp_freq_table[] = { + 2, 2, 4, 6, 8, 10, 14, 18, 22, 26, + 30, 34, 38, 42, 46, 50, 56, 62, 68, 74, + 80, 90, 100, 200, 300, 400, 600, 800, 1000, 2000, + 3000, 4000, +}; + +static const struct regmap_range hx9023s_rd_reg_ranges[] = { + regmap_reg_range(HX9023S_GLOBAL_CTRL0, HX9023S_LP_DIFF_CH3_2), +}; + +static const struct regmap_range hx9023s_wr_reg_ranges[] = { + regmap_reg_range(HX9023S_GLOBAL_CTRL0, HX9023S_LP_DIFF_CH3_2), +}; + +static const struct regmap_range hx9023s_volatile_reg_ranges[] = { + regmap_reg_range(HX9023S_CAP_INI_CH4_0, HX9023S_LP_DIFF_CH4_2), + regmap_reg_range(HX9023S_CAP_INI_CH0_0, HX9023S_LP_DIFF_CH3_2), + regmap_reg_range(HX9023S_PROX_STATUS, HX9023S_PROX_STATUS), +}; + +static const struct regmap_access_table hx9023s_rd_regs = { + .yes_ranges = hx9023s_rd_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_rd_reg_ranges), +}; + +static const struct regmap_access_table hx9023s_wr_regs = { + .yes_ranges = hx9023s_wr_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_wr_reg_ranges), +}; + +static const struct regmap_access_table hx9023s_volatile_regs = { + .yes_ranges = hx9023s_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_volatile_reg_ranges), +}; + +static const struct regmap_config hx9023s_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .rd_table = &hx9023s_rd_regs, + .wr_table = &hx9023s_wr_regs, + .volatile_table = &hx9023s_volatile_regs, +}; + +static int hx9023s_interrupt_enable(struct hx9023s_data *data) +{ + return regmap_update_bits(data->regmap, HX9023S_INTERRUPT_CFG, + HX9023S_INTERRUPT_MASK, HX9023S_INTERRUPT_MASK); +} + +static int hx9023s_interrupt_disable(struct hx9023s_data *data) +{ + return regmap_update_bits(data->regmap, HX9023S_INTERRUPT_CFG, + HX9023S_INTERRUPT_MASK, 0x00); +} + +static int hx9023s_data_lock(struct hx9023s_data *data, bool locked) +{ + if (locked) + return regmap_update_bits(data->regmap, + HX9023S_DSP_CONFIG_CTRL1, + HX9023S_DATA_LOCK_MASK, + HX9023S_DATA_LOCK_MASK); + else + return regmap_update_bits(data->regmap, + HX9023S_DSP_CONFIG_CTRL1, + HX9023S_DATA_LOCK_MASK, 0); +} + +static int hx9023s_ch_cfg(struct hx9023s_data *data) +{ + __le16 reg_list[HX9023S_CH_NUM]; + u8 ch_pos[HX9023S_CH_NUM]; + u8 ch_neg[HX9023S_CH_NUM]; + /* Bit positions corresponding to input pin connections */ + u8 conn_cs[HX9023S_CH_NUM] = { 0, 2, 4, 6, 8 }; + unsigned int i; + u16 reg; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + ch_pos[i] = data->ch_data[i].channel_positive == HX9023S_NOT_CONNECTED ? + HX9023S_NOT_CONNECTED : conn_cs[data->ch_data[i].channel_positive]; + ch_neg[i] = data->ch_data[i].channel_negative == HX9023S_NOT_CONNECTED ? + HX9023S_NOT_CONNECTED : conn_cs[data->ch_data[i].channel_negative]; + + reg = (HX9023S_POS << ch_pos[i]) | (HX9023S_NEG << ch_neg[i]); + reg_list[i] = cpu_to_le16(reg); + } + + return regmap_bulk_write(data->regmap, HX9023S_CH0_CFG_7_0, reg_list, + sizeof(reg_list)); +} + +static int hx9023s_write_far_debounce(struct hx9023s_data *data, int val) +{ + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, HX9023S_PROX_INT_LOW_CFG, + HX9023S_PROX_DEBOUNCE_MASK, + FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, val)); +} + +static int hx9023s_write_near_debounce(struct hx9023s_data *data, int val) +{ + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, HX9023S_PROX_INT_HIGH_CFG, + HX9023S_PROX_DEBOUNCE_MASK, + FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, val)); +} + +static int hx9023s_read_far_debounce(struct hx9023s_data *data, int *val) +{ + int ret; + + ret = regmap_read(data->regmap, HX9023S_PROX_INT_LOW_CFG, val); + if (ret) + return ret; + + *val = FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, *val); + + return IIO_VAL_INT; +} + +static int hx9023s_read_near_debounce(struct hx9023s_data *data, int *val) +{ + int ret; + + ret = regmap_read(data->regmap, HX9023S_PROX_INT_HIGH_CFG, val); + if (ret) + return ret; + + *val = FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, *val); + + return IIO_VAL_INT; +} + +static int hx9023s_get_thres_near(struct hx9023s_data *data, u8 ch, int *val) +{ + int ret; + __le16 buf; + unsigned int reg, tmp; + + reg = (ch == 4) ? HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 : + HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 + (ch * 2); + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + tmp = (le16_to_cpu(buf) & GENMASK(9, 0)) * 32; + data->ch_data[ch].thres.near = tmp; + *val = tmp; + + return IIO_VAL_INT; +} + +static int hx9023s_get_thres_far(struct hx9023s_data *data, u8 ch, int *val) +{ + int ret; + __le16 buf; + unsigned int reg, tmp; + + reg = (ch == 4) ? HX9023S_PROX_LOW_DIFF_CFG_CH4_0 : + HX9023S_PROX_LOW_DIFF_CFG_CH0_0 + (ch * 2); + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + tmp = (le16_to_cpu(buf) & GENMASK(9, 0)) * 32; + data->ch_data[ch].thres.far = tmp; + *val = tmp; + + return IIO_VAL_INT; +} + +static int hx9023s_set_thres_near(struct hx9023s_data *data, u8 ch, int val) +{ + __le16 val_le16 = cpu_to_le16((val / 32) & GENMASK(9, 0)); + unsigned int reg; + + data->ch_data[ch].thres.near = ((val / 32) & GENMASK(9, 0)) * 32; + reg = (ch == 4) ? HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 : + HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 + (ch * 2); + + return regmap_bulk_write(data->regmap, reg, &val_le16, sizeof(val_le16)); +} + +static int hx9023s_set_thres_far(struct hx9023s_data *data, u8 ch, int val) +{ + __le16 val_le16 = cpu_to_le16((val / 32) & GENMASK(9, 0)); + unsigned int reg; + + data->ch_data[ch].thres.far = ((val / 32) & GENMASK(9, 0)) * 32; + reg = (ch == 4) ? HX9023S_PROX_LOW_DIFF_CFG_CH4_0 : + HX9023S_PROX_LOW_DIFF_CFG_CH0_0 + (ch * 2); + + return regmap_bulk_write(data->regmap, reg, &val_le16, sizeof(val_le16)); +} + +static int hx9023s_get_prox_state(struct hx9023s_data *data) +{ + return regmap_read(data->regmap, HX9023S_PROX_STATUS, &data->prox_state_reg); +} + +static int hx9023s_data_select(struct hx9023s_data *data) +{ + int ret; + unsigned int i, buf; + unsigned long tmp; + + ret = regmap_read(data->regmap, HX9023S_RAW_BL_RD_CFG, &buf); + if (ret) + return ret; + + tmp = buf; + for (i = 0; i < 4; i++) { + data->ch_data[i].sel_diff = test_bit(i, &tmp); + data->ch_data[i].sel_lp = !data->ch_data[i].sel_diff; + data->ch_data[i].sel_bl = test_bit(i + 4, &tmp); + data->ch_data[i].sel_raw = !data->ch_data[i].sel_bl; + } + + ret = regmap_read(data->regmap, HX9023S_INTERRUPT_CFG1, &buf); + if (ret) + return ret; + + tmp = buf; + data->ch_data[4].sel_diff = test_bit(2, &tmp); + data->ch_data[4].sel_lp = !data->ch_data[4].sel_diff; + data->ch_data[4].sel_bl = test_bit(3, &tmp); + data->ch_data[4].sel_raw = !data->ch_data[4].sel_bl; + + return 0; +} + +static int hx9023s_sample(struct hx9023s_data *data) +{ + int ret; + unsigned int i; + u8 buf[HX9023S_CH_NUM * 3]; + u16 value; + + ret = hx9023s_data_lock(data, true); + if (ret) + return ret; + + ret = hx9023s_data_select(data); + if (ret) + goto err; + + /* 3 bytes for each of channels 0 to 3 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH0_0, buf, 12); + if (ret) + goto err; + + /* 3 bytes for channel 4 */ + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH4_0, buf + 12, 3); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 3 + 1]); + data->ch_data[i].raw = 0; + data->ch_data[i].bl = 0; + if (data->ch_data[i].sel_raw) + data->ch_data[i].raw = value; + if (data->ch_data[i].sel_bl) + data->ch_data[i].bl = value; + } + + /* 3 bytes for each of channels 0 to 3 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH0_0, buf, 12); + if (ret) + goto err; + + /* 3 bytes for channel 4 */ + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH4_0, buf + 12, 3); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 3 + 1]); + data->ch_data[i].lp = 0; + data->ch_data[i].diff = 0; + if (data->ch_data[i].sel_lp) + data->ch_data[i].lp = value; + if (data->ch_data[i].sel_diff) + data->ch_data[i].diff = value; + } + + for (i = 0; i < HX9023S_CH_NUM; i++) { + if (data->ch_data[i].sel_lp && data->ch_data[i].sel_bl) + data->ch_data[i].diff = data->ch_data[i].lp - data->ch_data[i].bl; + } + + /* 2 bytes for each of channels 0 to 4 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_OFFSET_DAC0_7_0, buf, 10); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 2]); + value = FIELD_GET(GENMASK(11, 0), value); + data->ch_data[i].dac = value; + } + +err: + return hx9023s_data_lock(data, false); +} + +static int hx9023s_ch_en(struct hx9023s_data *data, u8 ch_id, bool en) +{ + int ret; + unsigned int buf; + + ret = regmap_read(data->regmap, HX9023S_CH_NUM_CFG, &buf); + if (ret) + return ret; + + data->ch_en_stat = buf; + if (en && data->ch_en_stat == 0) + data->prox_state_reg = 0; + + data->ch_data[ch_id].enable = en; + __assign_bit(ch_id, &data->ch_en_stat, en); + + return regmap_write(data->regmap, HX9023S_CH_NUM_CFG, data->ch_en_stat); +} + +static int hx9023s_property_get(struct hx9023s_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + u32 array[2]; + u32 i, reg, temp; + int ret; + + data->chan_in_use = 0; + for (i = 0; i < HX9023S_CH_NUM; i++) { + data->ch_data[i].channel_positive = HX9023S_NOT_CONNECTED; + data->ch_data[i].channel_negative = HX9023S_NOT_CONNECTED; + } + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg >= HX9023S_CH_NUM) + return dev_err_probe(dev, ret < 0 ? ret : -EINVAL, + "Failed to read reg\n"); + __set_bit(reg, &data->chan_in_use); + + ret = fwnode_property_read_u32(child, "single-channel", &temp); + if (ret == 0) { + data->ch_data[reg].channel_positive = temp; + data->ch_data[reg].channel_negative = HX9023S_NOT_CONNECTED; + } else { + ret = fwnode_property_read_u32_array(child, "diff-channels", + array, ARRAY_SIZE(array)); + if (ret == 0) { + data->ch_data[reg].channel_positive = array[0]; + data->ch_data[reg].channel_negative = array[1]; + } else { + return dev_err_probe(dev, ret, + "Property read failed: %d\n", + reg); + } + } + } + + return 0; +} + +static int hx9023s_update_chan_en(struct hx9023s_data *data, + unsigned long chan_read, + unsigned long chan_event) +{ + unsigned int i; + unsigned long channels = chan_read | chan_event; + + if ((data->chan_read | data->chan_event) != channels) { + for_each_set_bit(i, &channels, HX9023S_CH_NUM) + hx9023s_ch_en(data, i, test_bit(i, &data->chan_in_use)); + for_each_clear_bit(i, &channels, HX9023S_CH_NUM) + hx9023s_ch_en(data, i, false); + } + + data->chan_read = chan_read; + data->chan_event = chan_event; + + return 0; +} + +static int hx9023s_get_proximity(struct hx9023s_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + int ret; + + ret = hx9023s_sample(data); + if (ret) + return ret; + + ret = hx9023s_get_prox_state(data); + if (ret) + return ret; + + *val = data->ch_data[chan->channel].diff; + return IIO_VAL_INT; +} + +static int hx9023s_get_samp_freq(struct hx9023s_data *data, int *val, int *val2) +{ + int ret; + unsigned int odr, index; + + ret = regmap_read(data->regmap, HX9023S_PRF_CFG, &index); + if (ret) + return ret; + + odr = hx9023s_samp_freq_table[index]; + *val = KILO / odr; + *val2 = div_u64((KILO % odr) * MICRO, odr); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int hx9023s_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = hx9023s_get_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return hx9023s_get_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int hx9023s_set_samp_freq(struct hx9023s_data *data, int val, int val2) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int i, period_ms; + + period_ms = div_u64(NANO, (val * MEGA + val2)); + + for (i = 0; i < ARRAY_SIZE(hx9023s_samp_freq_table); i++) { + if (period_ms == hx9023s_samp_freq_table[i]) + break; + } + if (i == ARRAY_SIZE(hx9023s_samp_freq_table)) { + dev_err(dev, "Period:%dms NOT found!\n", period_ms); + return -EINVAL; + } + + return regmap_write(data->regmap, HX9023S_PRF_CFG, i); +} + +static int hx9023s_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + return hx9023s_set_samp_freq(data, val, val2); +} + +static irqreturn_t hx9023s_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct hx9023s_data *data = iio_priv(indio_dev); + + if (data->trigger_enabled) + iio_trigger_poll(data->trig); + + return IRQ_WAKE_THREAD; +} + +static void hx9023s_push_events(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long prox_changed; + unsigned int chan; + int ret; + + ret = hx9023s_sample(data); + if (ret) + return; + + ret = hx9023s_get_prox_state(data); + if (ret) + return; + + prox_changed = (data->chan_prox_stat ^ data->prox_state_reg) & data->chan_event; + for_each_set_bit(chan, &prox_changed, HX9023S_CH_NUM) { + unsigned int dir; + + dir = (data->prox_state_reg & BIT(chan)) ? + IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, + IIO_EV_TYPE_THRESH, dir), + timestamp); + } + data->chan_prox_stat = data->prox_state_reg; +} + +static irqreturn_t hx9023s_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + hx9023s_push_events(indio_dev); + + return IRQ_HANDLED; +} + +static int hx9023s_read_event_val(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_get_thres_far(data, chan->channel, val); + case IIO_EV_DIR_FALLING: + return hx9023s_get_thres_near(data, chan->channel, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_read_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return hx9023s_read_near_debounce(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int hx9023s_write_event_val(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_set_thres_far(data, chan->channel, val); + case IIO_EV_DIR_FALLING: + return hx9023s_set_thres_near(data, chan->channel, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_write_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return hx9023s_write_near_debounce(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int hx9023s_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + return test_bit(chan->channel, &data->chan_event); +} + +static int hx9023s_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (test_bit(chan->channel, &data->chan_in_use)) { + hx9023s_ch_en(data, chan->channel, !!state); + __assign_bit(chan->channel, &data->chan_event, + data->ch_data[chan->channel].enable); + } + + return 0; +} + +static const struct iio_info hx9023s_info = { + .read_raw = hx9023s_read_raw, + .write_raw = hx9023s_write_raw, + .read_event_value = hx9023s_read_event_val, + .write_event_value = hx9023s_write_event_val, + .read_event_config = hx9023s_read_event_config, + .write_event_config = hx9023s_write_event_config, +}; + +static int hx9023s_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + if (state) + hx9023s_interrupt_enable(data); + else if (!data->chan_read) + hx9023s_interrupt_disable(data); + data->trigger_enabled = state; + + return 0; +} + +static const struct iio_trigger_ops hx9023s_trigger_ops = { + .set_trigger_state = hx9023s_set_trigger_state, +}; + +static irqreturn_t hx9023s_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct hx9023s_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int bit, index, i = 0; + int ret; + + guard(mutex)(&data->mutex); + ret = hx9023s_sample(data); + if (ret) { + dev_warn(dev, "sampling failed\n"); + goto out; + } + + ret = hx9023s_get_prox_state(data); + if (ret) { + dev_warn(dev, "get prox failed\n"); + goto out; + } + + iio_for_each_active_channel(indio_dev, bit) { + index = indio_dev->channels[bit].channel; + data->buffer.channels[i++] = cpu_to_le16(data->ch_data[index].diff); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int hx9023s_buffer_preenable(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + unsigned long channels = 0; + unsigned int bit; + + guard(mutex)(&data->mutex); + iio_for_each_active_channel(indio_dev, bit) + __set_bit(indio_dev->channels[bit].channel, &channels); + + hx9023s_update_chan_en(data, channels, data->chan_event); + + return 0; +} + +static int hx9023s_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + hx9023s_update_chan_en(data, 0, data->chan_event); + + return 0; +} + +static const struct iio_buffer_setup_ops hx9023s_buffer_setup_ops = { + .preenable = hx9023s_buffer_preenable, + .postdisable = hx9023s_buffer_postdisable, +}; + +static int hx9023s_id_check(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int id; + int ret; + + ret = regmap_read(data->regmap, HX9023S_DEVICE_ID, &id); + if (ret) + return ret; + + if (id != HX9023S_CHIP_ID) + dev_warn(dev, "Unexpected chip ID, assuming compatible\n"); + + return 0; +} + +static int hx9023s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct hx9023s_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + mutex_init(&data->mutex); + + data->regmap = devm_regmap_init_i2c(client, &hx9023s_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap init failed\n"); + + ret = hx9023s_property_get(data); + if (ret) + return dev_err_probe(dev, ret, "dts phase failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "regulator get failed\n"); + + ret = hx9023s_id_check(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "id check failed\n"); + + indio_dev->name = "hx9023s"; + indio_dev->channels = hx9023s_channels; + indio_dev->num_channels = ARRAY_SIZE(hx9023s_channels); + indio_dev->info = &hx9023s_info; + indio_dev->modes = INDIO_DIRECT_MODE; + i2c_set_clientdata(client, indio_dev); + + ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, + ARRAY_SIZE(hx9023s_reg_init_list)); + if (ret) + return dev_err_probe(dev, ret, "device init failed\n"); + + ret = hx9023s_ch_cfg(data); + if (ret) + return dev_err_probe(dev, ret, "channel config failed\n"); + + ret = regcache_sync(data->regmap); + if (ret) + return dev_err_probe(dev, ret, "regcache sync failed\n"); + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, + hx9023s_irq_handler, + hx9023s_irq_thread_handler, + IRQF_ONESHOT, + "hx9023s_event", indio_dev); + if (ret) + return dev_err_probe(dev, ret, "irq request failed\n"); + + data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return dev_err_probe(dev, -ENOMEM, + "iio trigger alloc failed\n"); + + data->trig->ops = &hx9023s_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + + ret = devm_iio_trigger_register(dev, data->trig); + if (ret) + return dev_err_probe(dev, ret, + "iio trigger register failed\n"); + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + hx9023s_trigger_handler, + &hx9023s_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int hx9023s_suspend(struct device *dev) +{ + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); + + guard(mutex)(&data->mutex); + hx9023s_interrupt_disable(data); + + return 0; +} + +static int hx9023s_resume(struct device *dev) +{ + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); + + guard(mutex)(&data->mutex); + if (data->trigger_enabled) + hx9023s_interrupt_enable(data); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hx9023s_pm_ops, hx9023s_suspend, + hx9023s_resume); + +static const struct of_device_id hx9023s_of_match[] = { + { .compatible = "tyhx,hx9023s" }, + {} +}; +MODULE_DEVICE_TABLE(of, hx9023s_of_match); + +static const struct i2c_device_id hx9023s_id[] = { + { "hx9023s" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, hx9023s_id); + +static struct i2c_driver hx9023s_driver = { + .driver = { + .name = "hx9023s", + .of_match_table = hx9023s_of_match, + .pm = &hx9023s_pm_ops, + + /* + * The I2C operations in hx9023s_reg_init() and hx9023s_ch_cfg() + * are time-consuming. Prefer async so we don't delay boot + * if we're builtin to the kernel. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = hx9023s_probe, + .id_table = hx9023s_id, +}; +module_i2c_driver(hx9023s_driver); + +MODULE_AUTHOR("Yasin Lee <yasin.lee.x@gmail.com>"); +MODULE_DESCRIPTION("Driver for TYHX HX9023S SAR sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 92630812ece2..3f4eace05cfc 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -654,8 +654,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = sx9500_read_prox_data(data, &indio_dev->channels[bit], &val); if (ret < 0) diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index a95e9814aaf2..71aa6dced7d3 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -369,8 +369,7 @@ static irqreturn_t sx_common_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = data->chip_info->ops.read_prox_data(data, &indio_dev->channels[bit], &val); @@ -398,8 +397,7 @@ static int sx_common_buffer_preenable(struct iio_dev *indio_dev) int bit, ret; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) + iio_for_each_active_channel(indio_dev, bit) __set_bit(indio_dev->channels[bit].channel, &channels); ret = sx_common_update_chan_en(data, channels, data->chan_event); diff --git a/drivers/interconnect/icc-clk.c b/drivers/interconnect/icc-clk.c index f788db15cd76..b956e4050f38 100644 --- a/drivers/interconnect/icc-clk.c +++ b/drivers/interconnect/icc-clk.c @@ -87,6 +87,7 @@ struct icc_provider *icc_clk_register(struct device *dev, onecell = devm_kzalloc(dev, struct_size(onecell, nodes, 2 * num_clocks), GFP_KERNEL); if (!onecell) return ERR_PTR(-ENOMEM); + onecell->num_nodes = 2 * num_clocks; qp = devm_kzalloc(dev, struct_size(qp, clocks, num_clocks), GFP_KERNEL); if (!qp) @@ -133,8 +134,6 @@ struct icc_provider *icc_clk_register(struct device *dev, onecell->nodes[j++] = node; } - onecell->num_nodes = j; - ret = icc_provider_register(provider); if (ret) goto err; diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 9b84cd8becef..de96d4661340 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -26,6 +26,15 @@ config INTERCONNECT_QCOM_MSM8916 This is a driver for the Qualcomm Network-on-Chip on msm8916-based platforms. +config INTERCONNECT_QCOM_MSM8937 + tristate "Qualcomm MSM8937 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8937-based + platforms. + config INTERCONNECT_QCOM_MSM8939 tristate "Qualcomm MSM8939 interconnect driver" depends on INTERCONNECT_QCOM @@ -53,6 +62,15 @@ config INTERCONNECT_QCOM_MSM8974 This is a driver for the Qualcomm Network-on-Chip on msm8974-based platforms. +config INTERCONNECT_QCOM_MSM8976 + tristate "Qualcomm MSM8976 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8976-based + platforms. + config INTERCONNECT_QCOM_MSM8996 tristate "Qualcomm MSM8996 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 7a7b6a71876f..bfeea8416fcf 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -6,9 +6,11 @@ interconnect_qcom-y := icc-common.o icc-bcm-voter-objs := bcm-voter.o qnoc-msm8909-objs := msm8909.o qnoc-msm8916-objs := msm8916.o +qnoc-msm8937-objs := msm8937.o qnoc-msm8939-objs := msm8939.o qnoc-msm8953-objs := msm8953.o qnoc-msm8974-objs := msm8974.o +qnoc-msm8976-objs := msm8976.o qnoc-msm8996-objs := msm8996.o icc-osm-l3-objs := osm-l3.o qnoc-qcm2290-objs := qcm2290.o @@ -41,9 +43,11 @@ icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8937) += qnoc-msm8937.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8953) += qnoc-msm8953.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8976) += qnoc-msm8976.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o diff --git a/drivers/interconnect/qcom/msm8937.c b/drivers/interconnect/qcom/msm8937.c new file mode 100644 index 000000000000..052b14c28ef8 --- /dev/null +++ b/drivers/interconnect/qcom/msm8937.c @@ -0,0 +1,1350 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on data from msm8937-bus.dtsi in Qualcomm's msm-3.18 release: + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + */ + +#include <linux/device.h> +#include <linux/interconnect-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/interconnect/qcom,msm8937.h> + +#include "icc-rpm.h" + +enum { + QNOC_MASTER_AMPSS_M0 = 1, + QNOC_MASTER_GRAPHICS_3D, + QNOC_SNOC_BIMC_0_MAS, + QNOC_SNOC_BIMC_2_MAS, + QNOC_SNOC_BIMC_1_MAS, + QNOC_MASTER_TCU_0, + QNOC_MASTER_SPDM, + QNOC_MASTER_BLSP_1, + QNOC_MASTER_BLSP_2, + QNOC_MASTER_USB_HS, + QNOC_MASTER_XM_USB_HS1, + QNOC_MASTER_CRYPTO_CORE0, + QNOC_MASTER_SDCC_1, + QNOC_MASTER_SDCC_2, + QNOC_SNOC_PNOC_MAS, + QNOC_MASTER_QDSS_BAM, + QNOC_BIMC_SNOC_MAS, + QNOC_MASTER_JPEG, + QNOC_MASTER_MDP_PORT0, + QNOC_PNOC_SNOC_MAS, + QNOC_MASTER_VIDEO_P0, + QNOC_MASTER_VFE, + QNOC_MASTER_VFE1, + QNOC_MASTER_CPP, + QNOC_MASTER_QDSS_ETR, + QNOC_PNOC_M_0, + QNOC_PNOC_M_1, + QNOC_PNOC_INT_0, + QNOC_PNOC_INT_1, + QNOC_PNOC_INT_2, + QNOC_PNOC_INT_3, + QNOC_PNOC_SLV_0, + QNOC_PNOC_SLV_1, + QNOC_PNOC_SLV_2, + QNOC_PNOC_SLV_3, + QNOC_PNOC_SLV_4, + QNOC_PNOC_SLV_6, + QNOC_PNOC_SLV_7, + QNOC_PNOC_SLV_8, + QNOC_SNOC_QDSS_INT, + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_INT_2, + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV, + QNOC_SLAVE_SDCC_2, + QNOC_SLAVE_SPDM_WRAPPER, + QNOC_SLAVE_PDM, + QNOC_SLAVE_PRNG, + QNOC_SLAVE_TCSR, + QNOC_SLAVE_SNOC_CFG, + QNOC_SLAVE_MESSAGE_RAM, + QNOC_SLAVE_CAMERA_CFG, + QNOC_SLAVE_DISPLAY_CFG, + QNOC_SLAVE_VENUS_CFG, + QNOC_SLAVE_GRAPHICS_3D_CFG, + QNOC_SLAVE_TLMM, + QNOC_SLAVE_BLSP_1, + QNOC_SLAVE_BLSP_2, + QNOC_SLAVE_PMIC_ARB, + QNOC_SLAVE_SDCC_1, + QNOC_SLAVE_CRYPTO_0_CFG, + QNOC_SLAVE_USB_HS, + QNOC_SLAVE_TCU, + QNOC_PNOC_SNOC_SLV, + QNOC_SLAVE_APPSS, + QNOC_SLAVE_WCSS, + QNOC_SNOC_BIMC_0_SLV, + QNOC_SNOC_BIMC_1_SLV, + QNOC_SNOC_BIMC_2_SLV, + QNOC_SLAVE_OCIMEM, + QNOC_SNOC_PNOC_SLV, + QNOC_SLAVE_QDSS_STM, + QNOC_SLAVE_CATS_128, + QNOC_SLAVE_OCMEM_64, + QNOC_SLAVE_LPASS, +}; + +static const u16 mas_apps_proc_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_apps_proc = { + .name = "mas_apps_proc", + .id = QNOC_MASTER_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_apps_proc_links), + .links = mas_apps_proc_links, +}; + +static const u16 mas_oxili_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_oxili = { + .name = "mas_oxili", + .id = QNOC_MASTER_GRAPHICS_3D, + .buswidth = 8, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_oxili_links), + .links = mas_oxili_links, +}; + +static const u16 mas_snoc_bimc_0_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_snoc_bimc_0 = { + .name = "mas_snoc_bimc_0", + .id = QNOC_SNOC_BIMC_0_MAS, + .buswidth = 8, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_snoc_bimc_0_links), + .links = mas_snoc_bimc_0_links, +}; + +static const u16 mas_snoc_bimc_2_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_snoc_bimc_2 = { + .name = "mas_snoc_bimc_2", + .id = QNOC_SNOC_BIMC_2_MAS, + .buswidth = 8, + .mas_rpm_id = 108, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_snoc_bimc_2_links), + .links = mas_snoc_bimc_2_links, +}; + +static const u16 mas_snoc_bimc_1_links[] = { + QNOC_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node mas_snoc_bimc_1 = { + .name = "mas_snoc_bimc_1", + .id = QNOC_SNOC_BIMC_1_MAS, + .buswidth = 8, + .mas_rpm_id = 76, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(mas_snoc_bimc_1_links), + .links = mas_snoc_bimc_1_links, +}; + +static const u16 mas_tcu_0_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_tcu_0 = { + .name = "mas_tcu_0", + .id = QNOC_MASTER_TCU_0, + .buswidth = 8, + .mas_rpm_id = 102, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 2, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(mas_tcu_0_links), + .links = mas_tcu_0_links, +}; + +static const u16 mas_spdm_links[] = { + QNOC_PNOC_M_0 +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = QNOC_MASTER_SPDM, + .buswidth = 4, + .mas_rpm_id = 50, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_spdm_links), + .links = mas_spdm_links, +}; + +static const u16 mas_blsp_1_links[] = { + QNOC_PNOC_M_1 +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = QNOC_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_blsp_2_links[] = { + QNOC_PNOC_M_1 +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = QNOC_MASTER_BLSP_2, + .buswidth = 4, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_usb_hs1_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_usb_hs1 = { + .name = "mas_usb_hs1", + .id = QNOC_MASTER_USB_HS, + .buswidth = 4, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 12, + .num_links = ARRAY_SIZE(mas_usb_hs1_links), + .links = mas_usb_hs1_links, +}; + +static const u16 mas_xi_usb_hs1_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_xi_usb_hs1 = { + .name = "mas_xi_usb_hs1", + .id = QNOC_MASTER_XM_USB_HS1, + .buswidth = 8, + .mas_rpm_id = 138, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 11, + .num_links = ARRAY_SIZE(mas_xi_usb_hs1_links), + .links = mas_xi_usb_hs1_links, +}; + +static const u16 mas_crypto_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_crypto = { + .name = "mas_crypto", + .id = QNOC_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_crypto_links), + .links = mas_crypto_links, +}; + +static const u16 mas_sdcc_1_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = QNOC_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_2_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = QNOC_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_snoc_pcnoc_links[] = { + QNOC_PNOC_SLV_7, + QNOC_PNOC_INT_2, + QNOC_PNOC_INT_3 +}; + +static struct qcom_icc_node mas_snoc_pcnoc = { + .name = "mas_snoc_pcnoc", + .id = QNOC_SNOC_PNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 77, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, + .num_links = ARRAY_SIZE(mas_snoc_pcnoc_links), + .links = mas_snoc_pcnoc_links, +}; + +static const u16 mas_qdss_bam_links[] = { + QNOC_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = QNOC_MASTER_QDSS_BAM, + .buswidth = 4, + .mas_rpm_id = 19, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 11, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_INT_2 +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .name = "mas_bimc_snoc", + .id = QNOC_BIMC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_jpeg_links[] = { + QNOC_SNOC_BIMC_2_SLV +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = QNOC_MASTER_JPEG, + .buswidth = 16, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp_links[] = { + QNOC_SNOC_BIMC_0_SLV +}; + +static struct qcom_icc_node mas_mdp = { + .name = "mas_mdp", + .id = QNOC_MASTER_MDP_PORT0, + .buswidth = 16, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, + .num_links = ARRAY_SIZE(mas_mdp_links), + .links = mas_mdp_links, +}; + +static const u16 mas_pcnoc_snoc_links[] = { + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_BIMC_1_SLV +}; + +static struct qcom_icc_node mas_pcnoc_snoc = { + .name = "mas_pcnoc_snoc", + .id = QNOC_PNOC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(mas_pcnoc_snoc_links), + .links = mas_pcnoc_snoc_links, +}; + +static const u16 mas_venus_links[] = { + QNOC_SNOC_BIMC_2_SLV +}; + +static struct qcom_icc_node mas_venus = { + .name = "mas_venus", + .id = QNOC_MASTER_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = 9, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, + .num_links = ARRAY_SIZE(mas_venus_links), + .links = mas_venus_links, +}; + +static const u16 mas_vfe0_links[] = { + QNOC_SNOC_BIMC_0_SLV +}; + +static struct qcom_icc_node mas_vfe0 = { + .name = "mas_vfe0", + .id = QNOC_MASTER_VFE, + .buswidth = 16, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, + .num_links = ARRAY_SIZE(mas_vfe0_links), + .links = mas_vfe0_links, +}; + +static const u16 mas_vfe1_links[] = { + QNOC_SNOC_BIMC_0_SLV +}; + +static struct qcom_icc_node mas_vfe1 = { + .name = "mas_vfe1", + .id = QNOC_MASTER_VFE1, + .buswidth = 16, + .mas_rpm_id = 133, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 13, + .num_links = ARRAY_SIZE(mas_vfe1_links), + .links = mas_vfe1_links, +}; + +static const u16 mas_cpp_links[] = { + QNOC_SNOC_BIMC_2_SLV +}; + +static struct qcom_icc_node mas_cpp = { + .name = "mas_cpp", + .id = QNOC_MASTER_CPP, + .buswidth = 16, + .mas_rpm_id = 115, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 12, + .num_links = ARRAY_SIZE(mas_cpp_links), + .links = mas_cpp_links, +}; + +static const u16 mas_qdss_etr_links[] = { + QNOC_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = QNOC_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = 31, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 10, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 pcnoc_m_0_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node pcnoc_m_0 = { + .name = "pcnoc_m_0", + .id = QNOC_PNOC_M_0, + .buswidth = 4, + .mas_rpm_id = 87, + .slv_rpm_id = 116, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(pcnoc_m_0_links), + .links = pcnoc_m_0_links, +}; + +static const u16 pcnoc_m_1_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node pcnoc_m_1 = { + .name = "pcnoc_m_1", + .id = QNOC_PNOC_M_1, + .buswidth = 4, + .mas_rpm_id = 88, + .slv_rpm_id = 117, + .num_links = ARRAY_SIZE(pcnoc_m_1_links), + .links = pcnoc_m_1_links, +}; + +static const u16 pcnoc_int_0_links[] = { + QNOC_PNOC_SNOC_SLV, + QNOC_PNOC_SLV_7, + QNOC_PNOC_INT_3, + QNOC_PNOC_INT_2 +}; + +static struct qcom_icc_node pcnoc_int_0 = { + .name = "pcnoc_int_0", + .id = QNOC_PNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 85, + .slv_rpm_id = 114, + .num_links = ARRAY_SIZE(pcnoc_int_0_links), + .links = pcnoc_int_0_links, +}; + +static const u16 pcnoc_int_1_links[] = { + QNOC_PNOC_SNOC_SLV, + QNOC_PNOC_SLV_7, + QNOC_PNOC_INT_3, + QNOC_PNOC_INT_2 +}; + +static struct qcom_icc_node pcnoc_int_1 = { + .name = "pcnoc_int_1", + .id = QNOC_PNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_1_links), + .links = pcnoc_int_1_links, +}; + +static const u16 pcnoc_int_2_links[] = { + QNOC_PNOC_SLV_2, + QNOC_PNOC_SLV_3, + QNOC_PNOC_SLV_6, + QNOC_PNOC_SLV_8 +}; + +static struct qcom_icc_node pcnoc_int_2 = { + .name = "pcnoc_int_2", + .id = QNOC_PNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 124, + .slv_rpm_id = 184, + .num_links = ARRAY_SIZE(pcnoc_int_2_links), + .links = pcnoc_int_2_links, +}; + +static const u16 pcnoc_int_3_links[] = { + QNOC_PNOC_SLV_1, + QNOC_PNOC_SLV_0, + QNOC_PNOC_SLV_4, + QNOC_SLAVE_GRAPHICS_3D_CFG, + QNOC_SLAVE_TCU +}; + +static struct qcom_icc_node pcnoc_int_3 = { + .name = "pcnoc_int_3", + .id = QNOC_PNOC_INT_3, + .buswidth = 8, + .mas_rpm_id = 125, + .slv_rpm_id = 185, + .num_links = ARRAY_SIZE(pcnoc_int_3_links), + .links = pcnoc_int_3_links, +}; + +static const u16 pcnoc_s_0_links[] = { + QNOC_SLAVE_SPDM_WRAPPER, + QNOC_SLAVE_PDM, + QNOC_SLAVE_PRNG, + QNOC_SLAVE_SDCC_2 +}; + +static struct qcom_icc_node pcnoc_s_0 = { + .name = "pcnoc_s_0", + .id = QNOC_PNOC_SLV_0, + .buswidth = 4, + .mas_rpm_id = 89, + .slv_rpm_id = 118, + .num_links = ARRAY_SIZE(pcnoc_s_0_links), + .links = pcnoc_s_0_links, +}; + +static const u16 pcnoc_s_1_links[] = { + QNOC_SLAVE_TCSR +}; + +static struct qcom_icc_node pcnoc_s_1 = { + .name = "pcnoc_s_1", + .id = QNOC_PNOC_SLV_1, + .buswidth = 4, + .mas_rpm_id = 90, + .slv_rpm_id = 119, + .num_links = ARRAY_SIZE(pcnoc_s_1_links), + .links = pcnoc_s_1_links, +}; + +static const u16 pcnoc_s_2_links[] = { + QNOC_SLAVE_SNOC_CFG +}; + +static struct qcom_icc_node pcnoc_s_2 = { + .name = "pcnoc_s_2", + .id = QNOC_PNOC_SLV_2, + .buswidth = 4, + .mas_rpm_id = 91, + .slv_rpm_id = 120, + .num_links = ARRAY_SIZE(pcnoc_s_2_links), + .links = pcnoc_s_2_links, +}; + +static const u16 pcnoc_s_3_links[] = { + QNOC_SLAVE_MESSAGE_RAM +}; + +static struct qcom_icc_node pcnoc_s_3 = { + .name = "pcnoc_s_3", + .id = QNOC_PNOC_SLV_3, + .buswidth = 4, + .mas_rpm_id = 92, + .slv_rpm_id = 121, + .num_links = ARRAY_SIZE(pcnoc_s_3_links), + .links = pcnoc_s_3_links, +}; + +static const u16 pcnoc_s_4_links[] = { + QNOC_SLAVE_CAMERA_CFG, + QNOC_SLAVE_DISPLAY_CFG, + QNOC_SLAVE_VENUS_CFG +}; + +static struct qcom_icc_node pcnoc_s_4 = { + .name = "pcnoc_s_4", + .id = QNOC_PNOC_SLV_4, + .buswidth = 4, + .mas_rpm_id = 93, + .slv_rpm_id = 122, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(pcnoc_s_4_links), + .links = pcnoc_s_4_links, +}; + +static const u16 pcnoc_s_6_links[] = { + QNOC_SLAVE_TLMM, + QNOC_SLAVE_BLSP_1, + QNOC_SLAVE_BLSP_2 +}; + +static struct qcom_icc_node pcnoc_s_6 = { + .name = "pcnoc_s_6", + .id = QNOC_PNOC_SLV_6, + .buswidth = 4, + .mas_rpm_id = 94, + .slv_rpm_id = 123, + .num_links = ARRAY_SIZE(pcnoc_s_6_links), + .links = pcnoc_s_6_links, +}; + +static const u16 pcnoc_s_7_links[] = { + QNOC_SLAVE_SDCC_1, + QNOC_SLAVE_PMIC_ARB +}; + +static struct qcom_icc_node pcnoc_s_7 = { + .name = "pcnoc_s_7", + .id = QNOC_PNOC_SLV_7, + .buswidth = 4, + .mas_rpm_id = 95, + .slv_rpm_id = 124, + .num_links = ARRAY_SIZE(pcnoc_s_7_links), + .links = pcnoc_s_7_links, +}; + +static const u16 pcnoc_s_8_links[] = { + QNOC_SLAVE_USB_HS, + QNOC_SLAVE_CRYPTO_0_CFG +}; + +static struct qcom_icc_node pcnoc_s_8 = { + .name = "pcnoc_s_8", + .id = QNOC_PNOC_SLV_8, + .buswidth = 4, + .mas_rpm_id = 96, + .slv_rpm_id = 125, + .num_links = ARRAY_SIZE(pcnoc_s_8_links), + .links = pcnoc_s_8_links, +}; + +static const u16 qdss_int_links[] = { + QNOC_SNOC_INT_1, + QNOC_SNOC_BIMC_1_SLV +}; + +static struct qcom_icc_node qdss_int = { + .name = "qdss_int", + .id = QNOC_SNOC_QDSS_INT, + .buswidth = 8, + .mas_rpm_id = 98, + .slv_rpm_id = 128, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(qdss_int_links), + .links = qdss_int_links, +}; + +static const u16 snoc_int_0_links[] = { + QNOC_SLAVE_LPASS, + QNOC_SLAVE_WCSS, + QNOC_SLAVE_APPSS +}; + +static struct qcom_icc_node snoc_int_0 = { + .name = "snoc_int_0", + .id = QNOC_SNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 99, + .slv_rpm_id = 130, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(snoc_int_0_links), + .links = snoc_int_0_links, +}; + +static const u16 snoc_int_1_links[] = { + QNOC_SLAVE_QDSS_STM, + QNOC_SLAVE_OCIMEM, + QNOC_SNOC_PNOC_SLV +}; + +static struct qcom_icc_node snoc_int_1 = { + .name = "snoc_int_1", + .id = QNOC_SNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = 100, + .slv_rpm_id = 131, + .num_links = ARRAY_SIZE(snoc_int_1_links), + .links = snoc_int_1_links, +}; + +static const u16 snoc_int_2_links[] = { + QNOC_SLAVE_CATS_128, + QNOC_SLAVE_OCMEM_64 +}; + +static struct qcom_icc_node snoc_int_2 = { + .name = "snoc_int_2", + .id = QNOC_SNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 134, + .slv_rpm_id = 197, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(snoc_int_2_links), + .links = snoc_int_2_links, +}; + +static struct qcom_icc_node slv_ebi = { + .name = "slv_ebi", + .id = QNOC_SLAVE_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 slv_bimc_snoc_links[] = { + QNOC_BIMC_SNOC_MAS +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = QNOC_BIMC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = QNOC_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static struct qcom_icc_node slv_spdm = { + .name = "slv_spdm", + .id = QNOC_SLAVE_SPDM_WRAPPER, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 60, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = QNOC_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = QNOC_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 44, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = QNOC_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = QNOC_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 70, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = QNOC_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_camera_ss_cfg = { + .name = "slv_camera_ss_cfg", + .id = QNOC_SLAVE_CAMERA_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 3, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_disp_ss_cfg = { + .name = "slv_disp_ss_cfg", + .id = QNOC_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = QNOC_SLAVE_VENUS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 10, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_gpu_cfg = { + .name = "slv_gpu_cfg", + .id = QNOC_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 11, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = QNOC_SLAVE_TLMM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 51, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = QNOC_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 39, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = QNOC_SLAVE_BLSP_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 37, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = QNOC_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = QNOC_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = QNOC_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 52, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = QNOC_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 40, +}; + +static struct qcom_icc_node slv_tcu = { + .name = "slv_tcu", + .id = QNOC_SLAVE_TCU, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 133, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static const u16 slv_pcnoc_snoc_links[] = { + QNOC_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node slv_pcnoc_snoc = { + .name = "slv_pcnoc_snoc", + .id = QNOC_PNOC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(slv_pcnoc_snoc_links), + .links = slv_pcnoc_snoc_links, +}; + +static struct qcom_icc_node slv_kpss_ahb = { + .name = "slv_kpss_ahb", + .id = QNOC_SLAVE_APPSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 20, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_wcss = { + .name = "slv_wcss", + .id = QNOC_SLAVE_WCSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 23, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static const u16 slv_snoc_bimc_0_links[] = { + QNOC_SNOC_BIMC_0_MAS +}; + +static struct qcom_icc_node slv_snoc_bimc_0 = { + .name = "slv_snoc_bimc_0", + .id = QNOC_SNOC_BIMC_0_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_snoc_bimc_0_links), + .links = slv_snoc_bimc_0_links, +}; + +static const u16 slv_snoc_bimc_1_links[] = { + QNOC_SNOC_BIMC_1_MAS +}; + +static struct qcom_icc_node slv_snoc_bimc_1 = { + .name = "slv_snoc_bimc_1", + .id = QNOC_SNOC_BIMC_1_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 104, + .num_links = ARRAY_SIZE(slv_snoc_bimc_1_links), + .links = slv_snoc_bimc_1_links, +}; + +static const u16 slv_snoc_bimc_2_links[] = { + QNOC_SNOC_BIMC_2_MAS +}; + +static struct qcom_icc_node slv_snoc_bimc_2 = { + .name = "slv_snoc_bimc_2", + .id = QNOC_SNOC_BIMC_2_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 137, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_snoc_bimc_2_links), + .links = slv_snoc_bimc_2_links, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = QNOC_SLAVE_OCIMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static const u16 slv_snoc_pcnoc_links[] = { + QNOC_SNOC_PNOC_MAS +}; + +static struct qcom_icc_node slv_snoc_pcnoc = { + .name = "slv_snoc_pcnoc", + .id = QNOC_SNOC_PNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(slv_snoc_pcnoc_links), + .links = slv_snoc_pcnoc_links, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = QNOC_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_cats_0 = { + .name = "slv_cats_0", + .id = QNOC_SLAVE_CATS_128, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 106, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_cats_1 = { + .name = "slv_cats_1", + .id = QNOC_SLAVE_OCMEM_64, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 107, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = QNOC_SLAVE_LPASS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 21, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node *msm8937_bimc_nodes[] = { + [MAS_APPS_PROC] = &mas_apps_proc, + [MAS_OXILI] = &mas_oxili, + [MAS_SNOC_BIMC_0] = &mas_snoc_bimc_0, + [MAS_SNOC_BIMC_2] = &mas_snoc_bimc_2, + [MAS_SNOC_BIMC_1] = &mas_snoc_bimc_1, + [MAS_TCU_0] = &mas_tcu_0, + [SLV_EBI] = &slv_ebi, + [SLV_BIMC_SNOC] = &slv_bimc_snoc, +}; + +static const struct regmap_config msm8937_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5A000, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8937_bimc = { + .type = QCOM_ICC_BIMC, + .nodes = msm8937_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8937_bimc_nodes), + .bus_clk_desc = &bimc_clk, + .regmap_cfg = &msm8937_bimc_regmap_config, + .qos_offset = 0x8000, + .ab_coeff = 154, +}; + +static struct qcom_icc_node *msm8937_pcnoc_nodes[] = { + [MAS_SPDM] = &mas_spdm, + [MAS_BLSP_1] = &mas_blsp_1, + [MAS_BLSP_2] = &mas_blsp_2, + [MAS_USB_HS1] = &mas_usb_hs1, + [MAS_XI_USB_HS1] = &mas_xi_usb_hs1, + [MAS_CRYPTO] = &mas_crypto, + [MAS_SDCC_1] = &mas_sdcc_1, + [MAS_SDCC_2] = &mas_sdcc_2, + [MAS_SNOC_PCNOC] = &mas_snoc_pcnoc, + [PCNOC_M_0] = &pcnoc_m_0, + [PCNOC_M_1] = &pcnoc_m_1, + [PCNOC_INT_0] = &pcnoc_int_0, + [PCNOC_INT_1] = &pcnoc_int_1, + [PCNOC_INT_2] = &pcnoc_int_2, + [PCNOC_INT_3] = &pcnoc_int_3, + [PCNOC_S_0] = &pcnoc_s_0, + [PCNOC_S_1] = &pcnoc_s_1, + [PCNOC_S_2] = &pcnoc_s_2, + [PCNOC_S_3] = &pcnoc_s_3, + [PCNOC_S_4] = &pcnoc_s_4, + [PCNOC_S_6] = &pcnoc_s_6, + [PCNOC_S_7] = &pcnoc_s_7, + [PCNOC_S_8] = &pcnoc_s_8, + [SLV_SDCC_2] = &slv_sdcc_2, + [SLV_SPDM] = &slv_spdm, + [SLV_PDM] = &slv_pdm, + [SLV_PRNG] = &slv_prng, + [SLV_TCSR] = &slv_tcsr, + [SLV_SNOC_CFG] = &slv_snoc_cfg, + [SLV_MESSAGE_RAM] = &slv_message_ram, + [SLV_CAMERA_SS_CFG] = &slv_camera_ss_cfg, + [SLV_DISP_SS_CFG] = &slv_disp_ss_cfg, + [SLV_VENUS_CFG] = &slv_venus_cfg, + [SLV_GPU_CFG] = &slv_gpu_cfg, + [SLV_TLMM] = &slv_tlmm, + [SLV_BLSP_1] = &slv_blsp_1, + [SLV_BLSP_2] = &slv_blsp_2, + [SLV_PMIC_ARB] = &slv_pmic_arb, + [SLV_SDCC_1] = &slv_sdcc_1, + [SLV_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [SLV_USB_HS] = &slv_usb_hs, + [SLV_TCU] = &slv_tcu, + [SLV_PCNOC_SNOC] = &slv_pcnoc_snoc, +}; + +static const struct regmap_config msm8937_pcnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x13080, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8937_pcnoc = { + .type = QCOM_ICC_NOC, + .nodes = msm8937_pcnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8937_pcnoc_nodes), + .bus_clk_desc = &bus_0_clk, + .qos_offset = 0x7000, + .keep_alive = true, + .regmap_cfg = &msm8937_pcnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8937_snoc_nodes[] = { + [MAS_QDSS_BAM] = &mas_qdss_bam, + [MAS_BIMC_SNOC] = &mas_bimc_snoc, + [MAS_PCNOC_SNOC] = &mas_pcnoc_snoc, + [MAS_QDSS_ETR] = &mas_qdss_etr, + [QDSS_INT] = &qdss_int, + [SNOC_INT_0] = &snoc_int_0, + [SNOC_INT_1] = &snoc_int_1, + [SNOC_INT_2] = &snoc_int_2, + [SLV_KPSS_AHB] = &slv_kpss_ahb, + [SLV_WCSS] = &slv_wcss, + [SLV_SNOC_BIMC_1] = &slv_snoc_bimc_1, + [SLV_IMEM] = &slv_imem, + [SLV_SNOC_PCNOC] = &slv_snoc_pcnoc, + [SLV_QDSS_STM] = &slv_qdss_stm, + [SLV_CATS_1] = &slv_cats_1, + [SLV_LPASS] = &slv_lpass, +}; + +static const struct regmap_config msm8937_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x16080, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8937_snoc = { + .type = QCOM_ICC_NOC, + .nodes = msm8937_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8937_snoc_nodes), + .bus_clk_desc = &bus_1_clk, + .regmap_cfg = &msm8937_snoc_regmap_config, + .qos_offset = 0x7000, +}; + +static struct qcom_icc_node *msm8937_snoc_mm_nodes[] = { + [MAS_JPEG] = &mas_jpeg, + [MAS_MDP] = &mas_mdp, + [MAS_VENUS] = &mas_venus, + [MAS_VFE0] = &mas_vfe0, + [MAS_VFE1] = &mas_vfe1, + [MAS_CPP] = &mas_cpp, + [SLV_SNOC_BIMC_0] = &slv_snoc_bimc_0, + [SLV_SNOC_BIMC_2] = &slv_snoc_bimc_2, + [SLV_CATS_0] = &slv_cats_0, +}; + +static const struct qcom_icc_desc msm8937_snoc_mm = { + .type = QCOM_ICC_NOC, + .nodes = msm8937_snoc_mm_nodes, + .num_nodes = ARRAY_SIZE(msm8937_snoc_mm_nodes), + .bus_clk_desc = &bus_2_clk, + .regmap_cfg = &msm8937_snoc_regmap_config, + .qos_offset = 0x7000, + .ab_coeff = 154, +}; + +static const struct of_device_id msm8937_noc_of_match[] = { + { .compatible = "qcom,msm8937-bimc", .data = &msm8937_bimc }, + { .compatible = "qcom,msm8937-pcnoc", .data = &msm8937_pcnoc }, + { .compatible = "qcom,msm8937-snoc", .data = &msm8937_snoc }, + { .compatible = "qcom,msm8937-snoc-mm", .data = &msm8937_snoc_mm }, + { } +}; +MODULE_DEVICE_TABLE(of, msm8937_noc_of_match); + +static struct platform_driver msm8937_noc_driver = { + .probe = qnoc_probe, + .remove_new = qnoc_remove, + .driver = { + .name = "qnoc-msm8937", + .of_match_table = msm8937_noc_of_match, + .sync_state = icc_sync_state, + }, +}; +module_platform_driver(msm8937_noc_driver); + +MODULE_DESCRIPTION("Qualcomm MSM8937 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/msm8953.c b/drivers/interconnect/qcom/msm8953.c index 9e8867c07692..62f8c0774b3e 100644 --- a/drivers/interconnect/qcom/msm8953.c +++ b/drivers/interconnect/qcom/msm8953.c @@ -1169,6 +1169,7 @@ static const struct qcom_icc_desc msm8953_bimc = { .nodes = msm8953_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8953_bimc_nodes), .qos_offset = 0x8000, + .ab_coeff = 153, .regmap_cfg = &msm8953_bimc_regmap_config }; @@ -1295,6 +1296,7 @@ static const struct qcom_icc_desc msm8953_snoc_mm = { .nodes = msm8953_snoc_mm_nodes, .num_nodes = ARRAY_SIZE(msm8953_snoc_mm_nodes), .qos_offset = 0x7000, + .ab_coeff = 153, .regmap_cfg = &msm8953_snoc_regmap_config, }; diff --git a/drivers/interconnect/qcom/msm8976.c b/drivers/interconnect/qcom/msm8976.c new file mode 100644 index 000000000000..ab963def77c3 --- /dev/null +++ b/drivers/interconnect/qcom/msm8976.c @@ -0,0 +1,1440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on data from msm8976-bus.dtsi in Qualcomm's msm-3.10 release: + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + */ + +#include <linux/device.h> +#include <linux/interconnect-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/interconnect/qcom,msm8976.h> + +#include "icc-rpm.h" + +enum { + QNOC_MASTER_AMPSS_M0 = 1, + QNOC_MNOC_BIMC_MAS, + QNOC_SNOC_BIMC_MAS, + QNOC_MASTER_TCU_0, + QNOC_MASTER_USB_HS2, + QNOC_MASTER_BLSP_1, + QNOC_MASTER_USB_HS, + QNOC_MASTER_BLSP_2, + QNOC_MASTER_CRYPTO_CORE0, + QNOC_MASTER_SDCC_1, + QNOC_MASTER_SDCC_2, + QNOC_MASTER_SDCC_3, + QNOC_SNOC_PNOC_MAS, + QNOC_MASTER_LPASS_AHB, + QNOC_MASTER_SPDM, + QNOC_MASTER_DEHR, + QNOC_MASTER_XM_USB_HS1, + QNOC_MASTER_QDSS_BAM, + QNOC_BIMC_SNOC_MAS, + QNOC_MASTER_JPEG, + QNOC_MASTER_GRAPHICS_3D, + QNOC_MASTER_MDP_PORT0, + QNOC_MASTER_MDP_PORT1, + QNOC_PNOC_SNOC_MAS, + QNOC_MASTER_VIDEO_P0, + QNOC_MASTER_VIDEO_P1, + QNOC_MASTER_VFE0, + QNOC_MASTER_VFE1, + QNOC_MASTER_CPP, + QNOC_MASTER_QDSS_ETR, + QNOC_MASTER_LPASS_PROC, + QNOC_MASTER_IPA, + QNOC_PNOC_M_0, + QNOC_PNOC_M_1, + QNOC_PNOC_INT_0, + QNOC_PNOC_INT_1, + QNOC_PNOC_INT_2, + QNOC_PNOC_SLV_1, + QNOC_PNOC_SLV_2, + QNOC_PNOC_SLV_3, + QNOC_PNOC_SLV_4, + QNOC_PNOC_SLV_8, + QNOC_PNOC_SLV_9, + QNOC_SNOC_MM_INT_0, + QNOC_SNOC_QDSS_INT, + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_INT_2, + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV, + QNOC_SLAVE_TCSR, + QNOC_SLAVE_TLMM, + QNOC_SLAVE_CRYPTO_0_CFG, + QNOC_SLAVE_MESSAGE_RAM, + QNOC_SLAVE_PDM, + QNOC_SLAVE_PRNG, + QNOC_SLAVE_PMIC_ARB, + QNOC_SLAVE_SNOC_CFG, + QNOC_SLAVE_DCC_CFG, + QNOC_SLAVE_CAMERA_CFG, + QNOC_SLAVE_DISPLAY_CFG, + QNOC_SLAVE_VENUS_CFG, + QNOC_SLAVE_SDCC_1, + QNOC_SLAVE_BLSP_1, + QNOC_SLAVE_USB_HS, + QNOC_SLAVE_SDCC_3, + QNOC_SLAVE_SDCC_2, + QNOC_SLAVE_GRAPHICS_3D_CFG, + QNOC_SLAVE_USB_HS2, + QNOC_SLAVE_BLSP_2, + QNOC_PNOC_SNOC_SLV, + QNOC_SLAVE_APPSS, + QNOC_MNOC_BIMC_SLV, + QNOC_SNOC_BIMC_SLV, + QNOC_SLAVE_SYSTEM_IMEM, + QNOC_SNOC_PNOC_SLV, + QNOC_SLAVE_QDSS_STM, + QNOC_SLAVE_CATS_128, + QNOC_SLAVE_OCMEM_64, + QNOC_SLAVE_LPASS, +}; + +static const u16 mas_apps_proc_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_apps_proc = { + .name = "mas_apps_proc", + .id = QNOC_MASTER_AMPSS_M0, + .buswidth = 16, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_apps_proc_links), + .links = mas_apps_proc_links, +}; + +static const u16 mas_smmnoc_bimc_links[] = { + QNOC_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node mas_smmnoc_bimc = { + .name = "mas_smmnoc_bimc", + .id = QNOC_MNOC_BIMC_MAS, + .channels = 2, + .buswidth = 16, + .mas_rpm_id = 135, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_smmnoc_bimc_links), + .links = mas_smmnoc_bimc_links, +}; + +static const u16 mas_snoc_bimc_links[] = { + QNOC_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node mas_snoc_bimc = { + .name = "mas_snoc_bimc", + .id = QNOC_SNOC_BIMC_MAS, + .channels = 2, + .buswidth = 16, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_snoc_bimc_links), + .links = mas_snoc_bimc_links, +}; + +static const u16 mas_tcu_0_links[] = { + QNOC_SLAVE_EBI_CH0, + QNOC_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_tcu_0 = { + .name = "mas_tcu_0", + .id = QNOC_MASTER_TCU_0, + .buswidth = 16, + .mas_rpm_id = 102, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 2, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_tcu_0_links), + .links = mas_tcu_0_links, +}; + +static const u16 mas_usb_hs2_links[] = { + QNOC_PNOC_M_0 +}; + +static struct qcom_icc_node mas_usb_hs2 = { + .name = "mas_usb_hs2", + .id = QNOC_MASTER_USB_HS2, + .buswidth = 4, + .mas_rpm_id = 57, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs2_links), + .links = mas_usb_hs2_links, +}; + +static const u16 mas_blsp_1_links[] = { + QNOC_PNOC_M_1 +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = QNOC_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_usb_hs1_links[] = { + QNOC_PNOC_M_1 +}; + +static struct qcom_icc_node mas_usb_hs1 = { + .name = "mas_usb_hs1", + .id = QNOC_MASTER_USB_HS, + .buswidth = 4, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs1_links), + .links = mas_usb_hs1_links, +}; + +static const u16 mas_blsp_2_links[] = { + QNOC_PNOC_M_1 +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = QNOC_MASTER_BLSP_2, + .buswidth = 4, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_crypto_links[] = { + QNOC_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_crypto = { + .name = "mas_crypto", + .id = QNOC_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_crypto_links), + .links = mas_crypto_links, +}; + +static const u16 mas_sdcc_1_links[] = { + QNOC_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = QNOC_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_2_links[] = { + QNOC_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = QNOC_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_sdcc_3_links[] = { + QNOC_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_sdcc_3 = { + .name = "mas_sdcc_3", + .id = QNOC_MASTER_SDCC_3, + .buswidth = 8, + .mas_rpm_id = 34, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 10, + .num_links = ARRAY_SIZE(mas_sdcc_3_links), + .links = mas_sdcc_3_links, +}; + +static const u16 mas_snoc_pcnoc_links[] = { + QNOC_PNOC_INT_2 +}; + +static struct qcom_icc_node mas_snoc_pcnoc = { + .name = "mas_snoc_pcnoc", + .id = QNOC_SNOC_PNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 77, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, + .num_links = ARRAY_SIZE(mas_snoc_pcnoc_links), + .links = mas_snoc_pcnoc_links, +}; + +static const u16 mas_lpass_ahb_links[] = { + QNOC_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node mas_lpass_ahb = { + .name = "mas_lpass_ahb", + .id = QNOC_MASTER_LPASS_AHB, + .buswidth = 8, + .mas_rpm_id = 18, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 12, + .num_links = ARRAY_SIZE(mas_lpass_ahb_links), + .links = mas_lpass_ahb_links, +}; + +static const u16 mas_spdm_links[] = { + QNOC_PNOC_M_0 +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = QNOC_MASTER_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_spdm_links), + .links = mas_spdm_links, +}; + +static const u16 mas_dehr_links[] = { + QNOC_PNOC_M_0 +}; + +static struct qcom_icc_node mas_dehr = { + .name = "mas_dehr", + .id = QNOC_MASTER_DEHR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_dehr_links), + .links = mas_dehr_links, +}; + +static const u16 mas_xm_usb_hs1_links[] = { + QNOC_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_xm_usb_hs1 = { + .name = "mas_xm_usb_hs1", + .id = QNOC_MASTER_XM_USB_HS1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_xm_usb_hs1_links), + .links = mas_xm_usb_hs1_links, +}; + +static const u16 mas_qdss_bam_links[] = { + QNOC_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = QNOC_MASTER_QDSS_BAM, + .buswidth = 4, + .mas_rpm_id = 19, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 11, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + QNOC_SNOC_INT_2 +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .name = "mas_bimc_snoc", + .id = QNOC_BIMC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_jpeg_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = QNOC_MASTER_JPEG, + .buswidth = 16, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_oxili_links[] = { + QNOC_MNOC_BIMC_SLV, + QNOC_SNOC_MM_INT_0 +}; + +static struct qcom_icc_node mas_oxili = { + .name = "mas_oxili", + .id = QNOC_MASTER_GRAPHICS_3D, + .channels = 2, + .buswidth = 16, + .ib_coeff = 200, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 16, /* [16, 17] */ + .num_links = ARRAY_SIZE(mas_oxili_links), + .links = mas_oxili_links, +}; + +static const u16 mas_mdp0_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_mdp0 = { + .name = "mas_mdp0", + .id = QNOC_MASTER_MDP_PORT0, + .buswidth = 16, + .ib_coeff = 50, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, + .num_links = ARRAY_SIZE(mas_mdp0_links), + .links = mas_mdp0_links, +}; + +static const u16 mas_mdp1_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_mdp1 = { + .name = "mas_mdp1", + .id = QNOC_MASTER_MDP_PORT1, + .buswidth = 16, + .ib_coeff = 50, + .mas_rpm_id = 61, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 13, + .num_links = ARRAY_SIZE(mas_mdp1_links), + .links = mas_mdp1_links, +}; + +static const u16 mas_pcnoc_snoc_links[] = { + QNOC_SNOC_INT_2 +}; + +static struct qcom_icc_node mas_pcnoc_snoc = { + .name = "mas_pcnoc_snoc", + .id = QNOC_PNOC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(mas_pcnoc_snoc_links), + .links = mas_pcnoc_snoc_links, +}; + +static const u16 mas_venus_0_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_venus_0 = { + .name = "mas_venus_0", + .id = QNOC_MASTER_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = 9, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, + .num_links = ARRAY_SIZE(mas_venus_0_links), + .links = mas_venus_0_links, +}; + +static const u16 mas_venus_1_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_venus_1 = { + .name = "mas_venus_1", + .id = QNOC_MASTER_VIDEO_P1, + .buswidth = 16, + .mas_rpm_id = 10, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 14, + .num_links = ARRAY_SIZE(mas_venus_1_links), + .links = mas_venus_1_links, +}; + +static const u16 mas_vfe_0_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_vfe_0 = { + .name = "mas_vfe_0", + .id = QNOC_MASTER_VFE0, + .buswidth = 16, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, + .num_links = ARRAY_SIZE(mas_vfe_0_links), + .links = mas_vfe_0_links, +}; + +static const u16 mas_vfe_1_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_vfe_1 = { + .name = "mas_vfe_1", + .id = QNOC_MASTER_VFE1, + .buswidth = 16, + .mas_rpm_id = 133, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 15, + .num_links = ARRAY_SIZE(mas_vfe_1_links), + .links = mas_vfe_1_links, +}; + +static const u16 mas_cpp_links[] = { + QNOC_SNOC_MM_INT_0, + QNOC_MNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_cpp = { + .name = "mas_cpp", + .id = QNOC_MASTER_CPP, + .buswidth = 16, + .mas_rpm_id = 115, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 12, + .num_links = ARRAY_SIZE(mas_cpp_links), + .links = mas_cpp_links, +}; + +static const u16 mas_qdss_etr_links[] = { + QNOC_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = QNOC_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = 31, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 10, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_lpass_proc_links[] = { + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_BIMC_SLV +}; + +static struct qcom_icc_node mas_lpass_proc = { + .name = "mas_lpass_proc", + .id = QNOC_MASTER_LPASS_PROC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 19, + .num_links = ARRAY_SIZE(mas_lpass_proc_links), + .links = mas_lpass_proc_links, +}; + +static const u16 mas_ipa_links[] = { + QNOC_SNOC_INT_2 +}; + +static struct qcom_icc_node mas_ipa = { + .name = "mas_ipa", + .id = QNOC_MASTER_IPA, + .buswidth = 8, + .mas_rpm_id = 59, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 18, + .num_links = ARRAY_SIZE(mas_ipa_links), + .links = mas_ipa_links, +}; + +static const u16 pcnoc_m_0_links[] = { + QNOC_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node pcnoc_m_0 = { + .name = "pcnoc_m_0", + .id = QNOC_PNOC_M_0, + .buswidth = 4, + .mas_rpm_id = 87, + .slv_rpm_id = 116, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(pcnoc_m_0_links), + .links = pcnoc_m_0_links, +}; + +static const u16 pcnoc_m_1_links[] = { + QNOC_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node pcnoc_m_1 = { + .name = "pcnoc_m_1", + .id = QNOC_PNOC_M_1, + .buswidth = 4, + .mas_rpm_id = 88, + .slv_rpm_id = 117, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(pcnoc_m_1_links), + .links = pcnoc_m_1_links, +}; + +static const u16 pcnoc_int_0_links[] = { + QNOC_PNOC_SNOC_SLV, + QNOC_PNOC_INT_2 +}; + +static struct qcom_icc_node pcnoc_int_0 = { + .name = "pcnoc_int_0", + .id = QNOC_PNOC_INT_0, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_0_links), + .links = pcnoc_int_0_links, +}; + +static const u16 pcnoc_int_1_links[] = { + QNOC_PNOC_SNOC_SLV, + QNOC_PNOC_INT_2 +}; + +static struct qcom_icc_node pcnoc_int_1 = { + .name = "pcnoc_int_1", + .id = QNOC_PNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = 86, + .slv_rpm_id = 115, + .num_links = ARRAY_SIZE(pcnoc_int_1_links), + .links = pcnoc_int_1_links, +}; + +static const u16 pcnoc_int_2_links[] = { + QNOC_PNOC_SLV_1, + QNOC_PNOC_SLV_2, + QNOC_PNOC_SLV_4, + QNOC_PNOC_SLV_8, + QNOC_PNOC_SLV_9, + QNOC_PNOC_SLV_3 +}; + +static struct qcom_icc_node pcnoc_int_2 = { + .name = "pcnoc_int_2", + .id = QNOC_PNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 124, + .slv_rpm_id = 184, + .num_links = ARRAY_SIZE(pcnoc_int_2_links), + .links = pcnoc_int_2_links, +}; + +static const u16 pcnoc_s_1_links[] = { + QNOC_SLAVE_CRYPTO_0_CFG, + QNOC_SLAVE_PRNG, + QNOC_SLAVE_PDM, + QNOC_SLAVE_MESSAGE_RAM +}; + +static struct qcom_icc_node pcnoc_s_1 = { + .name = "pcnoc_s_1", + .id = QNOC_PNOC_SLV_1, + .buswidth = 4, + .mas_rpm_id = 90, + .slv_rpm_id = 119, + .num_links = ARRAY_SIZE(pcnoc_s_1_links), + .links = pcnoc_s_1_links, +}; + +static const u16 pcnoc_s_2_links[] = { + QNOC_SLAVE_PMIC_ARB +}; + +static struct qcom_icc_node pcnoc_s_2 = { + .name = "pcnoc_s_2", + .id = QNOC_PNOC_SLV_2, + .buswidth = 4, + .mas_rpm_id = 91, + .slv_rpm_id = 120, + .num_links = ARRAY_SIZE(pcnoc_s_2_links), + .links = pcnoc_s_2_links, +}; + +static const u16 pcnoc_s_3_links[] = { + QNOC_SLAVE_SNOC_CFG, + QNOC_SLAVE_DCC_CFG +}; + +static struct qcom_icc_node pcnoc_s_3 = { + .name = "pcnoc_s_3", + .id = QNOC_PNOC_SLV_3, + .buswidth = 4, + .mas_rpm_id = 92, + .slv_rpm_id = 121, + .num_links = ARRAY_SIZE(pcnoc_s_3_links), + .links = pcnoc_s_3_links, +}; + +static const u16 pcnoc_s_4_links[] = { + QNOC_SLAVE_CAMERA_CFG, + QNOC_SLAVE_DISPLAY_CFG, + QNOC_SLAVE_VENUS_CFG +}; + +static struct qcom_icc_node pcnoc_s_4 = { + .name = "pcnoc_s_4", + .id = QNOC_PNOC_SLV_4, + .buswidth = 4, + .mas_rpm_id = 93, + .slv_rpm_id = 122, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(pcnoc_s_4_links), + .links = pcnoc_s_4_links, +}; + +static const u16 pcnoc_s_8_links[] = { + QNOC_SLAVE_USB_HS, + QNOC_SLAVE_SDCC_3, + QNOC_SLAVE_BLSP_1, + QNOC_SLAVE_SDCC_1 +}; + +static struct qcom_icc_node pcnoc_s_8 = { + .name = "pcnoc_s_8", + .id = QNOC_PNOC_SLV_8, + .buswidth = 4, + .mas_rpm_id = 96, + .slv_rpm_id = 125, + .num_links = ARRAY_SIZE(pcnoc_s_8_links), + .links = pcnoc_s_8_links, +}; + +static const u16 pcnoc_s_9_links[] = { + QNOC_SLAVE_GRAPHICS_3D_CFG, + QNOC_SLAVE_USB_HS2, + QNOC_SLAVE_SDCC_2, + QNOC_SLAVE_BLSP_2 +}; + +static struct qcom_icc_node pcnoc_s_9 = { + .name = "pcnoc_s_9", + .id = QNOC_PNOC_SLV_9, + .buswidth = 4, + .mas_rpm_id = 97, + .slv_rpm_id = 126, + .num_links = ARRAY_SIZE(pcnoc_s_9_links), + .links = pcnoc_s_9_links, +}; + +static const u16 mm_int_0_links[] = { + QNOC_SNOC_INT_0 +}; + +static struct qcom_icc_node mm_int_0 = { + .name = "mm_int_0", + .id = QNOC_SNOC_MM_INT_0, + .buswidth = 16, + .ib_coeff = 200, + .mas_rpm_id = 79, + .slv_rpm_id = 108, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mm_int_0_links), + .links = mm_int_0_links, +}; + +static const u16 qdss_int_links[] = { + QNOC_SNOC_INT_2 +}; + +static struct qcom_icc_node qdss_int = { + .name = "qdss_int", + .id = QNOC_SNOC_QDSS_INT, + .buswidth = 8, + .mas_rpm_id = 98, + .slv_rpm_id = 128, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(qdss_int_links), + .links = qdss_int_links, +}; + +static const u16 snoc_int_0_links[] = { + QNOC_SLAVE_QDSS_STM, + QNOC_SLAVE_SYSTEM_IMEM, + QNOC_SNOC_PNOC_SLV +}; + +static struct qcom_icc_node snoc_int_0 = { + .name = "snoc_int_0", + .id = QNOC_SNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 99, + .slv_rpm_id = 130, + .num_links = ARRAY_SIZE(snoc_int_0_links), + .links = snoc_int_0_links, +}; + +static const u16 snoc_int_1_links[] = { + QNOC_SLAVE_LPASS, + QNOC_SLAVE_CATS_128, + QNOC_SLAVE_OCMEM_64, + QNOC_SLAVE_APPSS +}; + +static struct qcom_icc_node snoc_int_1 = { + .name = "snoc_int_1", + .id = QNOC_SNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = 100, + .slv_rpm_id = 131, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(snoc_int_1_links), + .links = snoc_int_1_links, +}; + +static const u16 snoc_int_2_links[] = { + QNOC_SNOC_INT_0, + QNOC_SNOC_INT_1, + QNOC_SNOC_BIMC_SLV +}; + +static struct qcom_icc_node snoc_int_2 = { + .name = "snoc_int_2", + .id = QNOC_SNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 134, + .slv_rpm_id = 197, + .num_links = ARRAY_SIZE(snoc_int_2_links), + .links = snoc_int_2_links, +}; + +static struct qcom_icc_node slv_ebi = { + .name = "slv_ebi", + .id = QNOC_SLAVE_EBI_CH0, + .channels = 2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 slv_bimc_snoc_links[] = { + QNOC_BIMC_SNOC_MAS +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = QNOC_BIMC_SNOC_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = QNOC_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = QNOC_SLAVE_TLMM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 51, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = QNOC_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 52, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = QNOC_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = QNOC_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = QNOC_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 44, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = QNOC_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = QNOC_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 70, +}; + +static struct qcom_icc_node slv_dcc_cfg = { + .name = "slv_dcc_cfg", + .id = QNOC_SLAVE_DCC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 155, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_camera_ss_cfg = { + .name = "slv_camera_ss_cfg", + .id = QNOC_SLAVE_CAMERA_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 3, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_disp_ss_cfg = { + .name = "slv_disp_ss_cfg", + .id = QNOC_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = QNOC_SLAVE_VENUS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 10, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = QNOC_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = QNOC_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 39, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = QNOC_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 40, +}; + +static struct qcom_icc_node slv_sdcc_3 = { + .name = "slv_sdcc_3", + .id = QNOC_SLAVE_SDCC_3, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 32, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = QNOC_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static struct qcom_icc_node slv_gpu_cfg = { + .name = "slv_gpu_cfg", + .id = QNOC_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 11, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_usb_hs2 = { + .name = "slv_usb_hs2", + .id = QNOC_SLAVE_USB_HS2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 79, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = QNOC_SLAVE_BLSP_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 37, +}; + +static const u16 slv_pcnoc_snoc_links[] = { + QNOC_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node slv_pcnoc_snoc = { + .name = "slv_pcnoc_snoc", + .id = QNOC_PNOC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(slv_pcnoc_snoc_links), + .links = slv_pcnoc_snoc_links, +}; + +static struct qcom_icc_node slv_kpss_ahb = { + .name = "slv_kpss_ahb", + .id = QNOC_SLAVE_APPSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 20, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static const u16 slv_smmnoc_bimc_links[] = { + QNOC_MNOC_BIMC_MAS +}; + +static struct qcom_icc_node slv_smmnoc_bimc = { + .name = "slv_smmnoc_bimc", + .id = QNOC_MNOC_BIMC_SLV, + .channels = 2, + .buswidth = 16, + .ib_coeff = 200, + .mas_rpm_id = -1, + .slv_rpm_id = 198, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_smmnoc_bimc_links), + .links = slv_smmnoc_bimc_links, +}; + +static const u16 slv_snoc_bimc_links[] = { + QNOC_SNOC_BIMC_MAS +}; + +static struct qcom_icc_node slv_snoc_bimc = { + .name = "slv_snoc_bimc", + .id = QNOC_SNOC_BIMC_SLV, + .channels = 2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_snoc_bimc_links), + .links = slv_snoc_bimc_links, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = QNOC_SLAVE_SYSTEM_IMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static const u16 slv_snoc_pcnoc_links[] = { + QNOC_SNOC_PNOC_MAS +}; + +static struct qcom_icc_node slv_snoc_pcnoc = { + .name = "slv_snoc_pcnoc", + .id = QNOC_SNOC_PNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(slv_snoc_pcnoc_links), + .links = slv_snoc_pcnoc_links, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = QNOC_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_cats_0 = { + .name = "slv_cats_0", + .id = QNOC_SLAVE_CATS_128, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 106, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_cats_1 = { + .name = "slv_cats_1", + .id = QNOC_SLAVE_OCMEM_64, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 107, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = QNOC_SLAVE_LPASS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 21, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, +}; + +static struct qcom_icc_node * const msm8976_bimc_nodes[] = { + [MAS_APPS_PROC] = &mas_apps_proc, + [MAS_SMMNOC_BIMC] = &mas_smmnoc_bimc, + [MAS_SNOC_BIMC] = &mas_snoc_bimc, + [MAS_TCU_0] = &mas_tcu_0, + [SLV_EBI] = &slv_ebi, + [SLV_BIMC_SNOC] = &slv_bimc_snoc, +}; + +static const struct regmap_config msm8976_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x62000, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8976_bimc = { + .type = QCOM_ICC_BIMC, + .nodes = msm8976_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8976_bimc_nodes), + .bus_clk_desc = &bimc_clk, + .regmap_cfg = &msm8976_bimc_regmap_config, + .qos_offset = 0x8000, + .ab_coeff = 154, +}; + +static struct qcom_icc_node * const msm8976_pcnoc_nodes[] = { + [MAS_USB_HS2] = &mas_usb_hs2, + [MAS_BLSP_1] = &mas_blsp_1, + [MAS_USB_HS1] = &mas_usb_hs1, + [MAS_BLSP_2] = &mas_blsp_2, + [MAS_CRYPTO] = &mas_crypto, + [MAS_SDCC_1] = &mas_sdcc_1, + [MAS_SDCC_2] = &mas_sdcc_2, + [MAS_SDCC_3] = &mas_sdcc_3, + [MAS_SNOC_PCNOC] = &mas_snoc_pcnoc, + [MAS_LPASS_AHB] = &mas_lpass_ahb, + [MAS_SPDM] = &mas_spdm, + [MAS_DEHR] = &mas_dehr, + [MAS_XM_USB_HS1] = &mas_xm_usb_hs1, + [PCNOC_M_0] = &pcnoc_m_0, + [PCNOC_M_1] = &pcnoc_m_1, + [PCNOC_INT_0] = &pcnoc_int_0, + [PCNOC_INT_1] = &pcnoc_int_1, + [PCNOC_INT_2] = &pcnoc_int_2, + [PCNOC_S_1] = &pcnoc_s_1, + [PCNOC_S_2] = &pcnoc_s_2, + [PCNOC_S_3] = &pcnoc_s_3, + [PCNOC_S_4] = &pcnoc_s_4, + [PCNOC_S_8] = &pcnoc_s_8, + [PCNOC_S_9] = &pcnoc_s_9, + [SLV_TCSR] = &slv_tcsr, + [SLV_TLMM] = &slv_tlmm, + [SLV_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [SLV_MESSAGE_RAM] = &slv_message_ram, + [SLV_PDM] = &slv_pdm, + [SLV_PRNG] = &slv_prng, + [SLV_PMIC_ARB] = &slv_pmic_arb, + [SLV_SNOC_CFG] = &slv_snoc_cfg, + [SLV_DCC_CFG] = &slv_dcc_cfg, + [SLV_CAMERA_SS_CFG] = &slv_camera_ss_cfg, + [SLV_DISP_SS_CFG] = &slv_disp_ss_cfg, + [SLV_VENUS_CFG] = &slv_venus_cfg, + [SLV_SDCC_1] = &slv_sdcc_1, + [SLV_BLSP_1] = &slv_blsp_1, + [SLV_USB_HS] = &slv_usb_hs, + [SLV_SDCC_3] = &slv_sdcc_3, + [SLV_SDCC_2] = &slv_sdcc_2, + [SLV_GPU_CFG] = &slv_gpu_cfg, + [SLV_USB_HS2] = &slv_usb_hs2, + [SLV_BLSP_2] = &slv_blsp_2, + [SLV_PCNOC_SNOC] = &slv_pcnoc_snoc, +}; + +static const struct regmap_config msm8976_pcnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14000, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8976_pcnoc = { + .type = QCOM_ICC_NOC, + .nodes = msm8976_pcnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8976_pcnoc_nodes), + .bus_clk_desc = &bus_0_clk, + .qos_offset = 0x7000, + .keep_alive = true, + .regmap_cfg = &msm8976_pcnoc_regmap_config, +}; + +static struct qcom_icc_node * const msm8976_snoc_nodes[] = { + [MAS_QDSS_BAM] = &mas_qdss_bam, + [MAS_BIMC_SNOC] = &mas_bimc_snoc, + [MAS_PCNOC_SNOC] = &mas_pcnoc_snoc, + [MAS_QDSS_ETR] = &mas_qdss_etr, + [MAS_LPASS_PROC] = &mas_lpass_proc, + [MAS_IPA] = &mas_ipa, + [QDSS_INT] = &qdss_int, + [SNOC_INT_0] = &snoc_int_0, + [SNOC_INT_1] = &snoc_int_1, + [SNOC_INT_2] = &snoc_int_2, + [SLV_KPSS_AHB] = &slv_kpss_ahb, + [SLV_SNOC_BIMC] = &slv_snoc_bimc, + [SLV_IMEM] = &slv_imem, + [SLV_SNOC_PCNOC] = &slv_snoc_pcnoc, + [SLV_QDSS_STM] = &slv_qdss_stm, + [SLV_CATS_0] = &slv_cats_0, + [SLV_CATS_1] = &slv_cats_1, + [SLV_LPASS] = &slv_lpass, +}; + +static const struct regmap_config msm8976_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1A000, + .fast_io = true, +}; + +static const struct qcom_icc_desc msm8976_snoc = { + .type = QCOM_ICC_NOC, + .nodes = msm8976_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8976_snoc_nodes), + .bus_clk_desc = &bus_1_clk, + .regmap_cfg = &msm8976_snoc_regmap_config, + .qos_offset = 0x7000, +}; + +static struct qcom_icc_node * const msm8976_snoc_mm_nodes[] = { + [MAS_JPEG] = &mas_jpeg, + [MAS_OXILI] = &mas_oxili, + [MAS_MDP0] = &mas_mdp0, + [MAS_MDP1] = &mas_mdp1, + [MAS_VENUS_0] = &mas_venus_0, + [MAS_VENUS_1] = &mas_venus_1, + [MAS_VFE_0] = &mas_vfe_0, + [MAS_VFE_1] = &mas_vfe_1, + [MAS_CPP] = &mas_cpp, + [MM_INT_0] = &mm_int_0, + [SLV_SMMNOC_BIMC] = &slv_smmnoc_bimc, +}; + +static const struct qcom_icc_desc msm8976_snoc_mm = { + .type = QCOM_ICC_NOC, + .nodes = msm8976_snoc_mm_nodes, + .num_nodes = ARRAY_SIZE(msm8976_snoc_mm_nodes), + .bus_clk_desc = &bus_2_clk, + .regmap_cfg = &msm8976_snoc_regmap_config, + .qos_offset = 0x7000, + .ab_coeff = 154, +}; + +static const struct of_device_id msm8976_noc_of_match[] = { + { .compatible = "qcom,msm8976-bimc", .data = &msm8976_bimc }, + { .compatible = "qcom,msm8976-pcnoc", .data = &msm8976_pcnoc }, + { .compatible = "qcom,msm8976-snoc", .data = &msm8976_snoc }, + { .compatible = "qcom,msm8976-snoc-mm", .data = &msm8976_snoc_mm }, + { } +}; +MODULE_DEVICE_TABLE(of, msm8976_noc_of_match); + +static struct platform_driver msm8976_noc_driver = { + .probe = qnoc_probe, + .remove_new = qnoc_remove, + .driver = { + .name = "qnoc-msm8976", + .of_match_table = msm8976_noc_of_match, + .sync_state = icc_sync_state, + }, +}; +module_platform_driver(msm8976_noc_driver); + +MODULE_DESCRIPTION("Qualcomm MSM8976 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 11b49a89c03d..63e9ff223ac4 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include "icc-rpm.h" @@ -101,6 +102,11 @@ static struct qcom_icc_node mas_apps_proc = { .buswidth = 8, .mas_rpm_id = 0, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, .num_links = ARRAY_SIZE(mas_apps_proc_links), .links = mas_apps_proc_links, }; @@ -116,6 +122,11 @@ static struct qcom_icc_node mas_oxili = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, .num_links = ARRAY_SIZE(mas_oxili_links), .links = mas_oxili_links, }; @@ -131,6 +142,11 @@ static struct qcom_icc_node mas_mdp = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 1, + .qos.qos_port = 4, .num_links = ARRAY_SIZE(mas_mdp_links), .links = mas_mdp_links, }; @@ -145,6 +161,10 @@ static struct qcom_icc_node mas_snoc_bimc_1 = { .buswidth = 8, .mas_rpm_id = 76, .slv_rpm_id = -1, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, .num_links = ARRAY_SIZE(mas_snoc_bimc_1_links), .links = mas_snoc_bimc_1_links, }; @@ -160,6 +180,11 @@ static struct qcom_icc_node mas_tcu_0 = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 2, + .qos.qos_port = 6, .num_links = ARRAY_SIZE(mas_tcu_0_links), .links = mas_tcu_0_links, }; @@ -174,6 +199,8 @@ static struct qcom_icc_node mas_spdm = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_spdm_links), .links = mas_spdm_links, }; @@ -231,6 +258,11 @@ static struct qcom_icc_node mas_crypto = { .buswidth = 8, .mas_rpm_id = 23, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, .num_links = ARRAY_SIZE(mas_crypto_links), .links = mas_crypto_links, }; @@ -287,6 +319,11 @@ static struct qcom_icc_node mas_qpic = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 14, .num_links = ARRAY_SIZE(mas_qpic_links), .links = mas_qpic_links, }; @@ -301,6 +338,11 @@ static struct qcom_icc_node mas_qdss_bam = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 1, .num_links = ARRAY_SIZE(mas_qdss_bam_links), .links = mas_qdss_bam_links, }; @@ -348,6 +390,11 @@ static struct qcom_icc_node mas_qdss_etr = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, .num_links = ARRAY_SIZE(mas_qdss_etr_links), .links = mas_qdss_etr_links, }; @@ -363,6 +410,11 @@ static struct qcom_icc_node mas_emac = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 17, .num_links = ARRAY_SIZE(mas_emac_links), .links = mas_emac_links, }; @@ -378,6 +430,11 @@ static struct qcom_icc_node mas_pcie = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 8, .num_links = ARRAY_SIZE(mas_pcie_links), .links = mas_pcie_links, }; @@ -393,6 +450,11 @@ static struct qcom_icc_node mas_usb3 = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 16, .num_links = ARRAY_SIZE(mas_usb3_links), .links = mas_usb3_links, }; @@ -491,6 +553,8 @@ static struct qcom_icc_node pcnoc_s_2 = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(pcnoc_s_2_links), .links = pcnoc_s_2_links, }; @@ -626,6 +690,8 @@ static struct qcom_icc_node qdss_int = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(qdss_int_links), .links = qdss_int_links, }; @@ -704,6 +770,8 @@ static struct qcom_icc_node slv_spdm = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_pdm = { @@ -752,6 +820,8 @@ static struct qcom_icc_node slv_disp_ss_cfg = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_gpu_cfg = { @@ -760,6 +830,8 @@ static struct qcom_icc_node slv_gpu_cfg = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_blsp_1 = { @@ -784,6 +856,8 @@ static struct qcom_icc_node slv_pcie = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_ethernet = { @@ -792,6 +866,8 @@ static struct qcom_icc_node slv_ethernet = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_blsp_2 = { @@ -816,6 +892,8 @@ static struct qcom_icc_node slv_tcu = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_pmic_arb = { @@ -894,6 +972,8 @@ static struct qcom_icc_node slv_kpss_ahb = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_wcss = { @@ -954,6 +1034,8 @@ static struct qcom_icc_node slv_cats_0 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_cats_1 = { @@ -962,6 +1044,8 @@ static struct qcom_icc_node slv_cats_1 = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_lpass = { @@ -970,6 +1054,8 @@ static struct qcom_icc_node slv_lpass = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node * const qcs404_bimc_nodes[] = { @@ -982,10 +1068,22 @@ static struct qcom_icc_node * const qcs404_bimc_nodes[] = { [SLAVE_BIMC_SNOC] = &slv_bimc_snoc, }; +static const struct regmap_config qcs404_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80000, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs404_bimc = { - .bus_clk_desc = &bimc_clk, + .type = QCOM_ICC_BIMC, .nodes = qcs404_bimc_nodes, .num_nodes = ARRAY_SIZE(qcs404_bimc_nodes), + .bus_clk_desc = &bimc_clk, + .regmap_cfg = &qcs404_bimc_regmap_config, + .qos_offset = 0x8000, + .ab_coeff = 153, }; static struct qcom_icc_node * const qcs404_pcnoc_nodes[] = { @@ -1037,10 +1135,22 @@ static struct qcom_icc_node * const qcs404_pcnoc_nodes[] = { [SLAVE_PCNOC_SNOC] = &slv_pcnoc_snoc, }; +static const struct regmap_config qcs404_pcnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x15080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs404_pcnoc = { - .bus_clk_desc = &bus_0_clk, + .type = QCOM_ICC_NOC, .nodes = qcs404_pcnoc_nodes, .num_nodes = ARRAY_SIZE(qcs404_pcnoc_nodes), + .bus_clk_desc = &bus_0_clk, + .qos_offset = 0x7000, + .keep_alive = true, + .regmap_cfg = &qcs404_pcnoc_regmap_config, }; static struct qcom_icc_node * const qcs404_snoc_nodes[] = { @@ -1066,10 +1176,21 @@ static struct qcom_icc_node * const qcs404_snoc_nodes[] = { [SLAVE_LPASS] = &slv_lpass, }; +static const struct regmap_config qcs404_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x23080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs404_snoc = { - .bus_clk_desc = &bus_1_clk, + .type = QCOM_ICC_NOC, .nodes = qcs404_snoc_nodes, .num_nodes = ARRAY_SIZE(qcs404_snoc_nodes), + .bus_clk_desc = &bus_1_clk, + .qos_offset = 0x11000, + .regmap_cfg = &qcs404_snoc_regmap_config, }; diff --git a/drivers/interconnect/qcom/sm8350.c b/drivers/interconnect/qcom/sm8350.c index b321c3009acb..4236a43dc256 100644 --- a/drivers/interconnect/qcom/sm8350.c +++ b/drivers/interconnect/qcom/sm8350.c @@ -628,60 +628,6 @@ static struct qcom_icc_node xm_gic = { .links = { SM8350_SLAVE_SNOC_GEM_NOC_GC }, }; -static struct qcom_icc_node qnm_mnoc_hf_disp = { - .name = "qnm_mnoc_hf_disp", - .id = SM8350_MASTER_MNOC_HF_MEM_NOC_DISP, - .channels = 2, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_SLAVE_LLCC_DISP }, -}; - -static struct qcom_icc_node qnm_mnoc_sf_disp = { - .name = "qnm_mnoc_sf_disp", - .id = SM8350_MASTER_MNOC_SF_MEM_NOC_DISP, - .channels = 2, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_SLAVE_LLCC_DISP }, -}; - -static struct qcom_icc_node llcc_mc_disp = { - .name = "llcc_mc_disp", - .id = SM8350_MASTER_LLCC_DISP, - .channels = 4, - .buswidth = 4, - .num_links = 1, - .links = { SM8350_SLAVE_EBI1_DISP }, -}; - -static struct qcom_icc_node qxm_mdp0_disp = { - .name = "qxm_mdp0_disp", - .id = SM8350_MASTER_MDP0_DISP, - .channels = 1, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_SLAVE_MNOC_HF_MEM_NOC_DISP }, -}; - -static struct qcom_icc_node qxm_mdp1_disp = { - .name = "qxm_mdp1_disp", - .id = SM8350_MASTER_MDP1_DISP, - .channels = 1, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_SLAVE_MNOC_HF_MEM_NOC_DISP }, -}; - -static struct qcom_icc_node qxm_rot_disp = { - .name = "qxm_rot_disp", - .id = SM8350_MASTER_ROTATOR_DISP, - .channels = 1, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_SLAVE_MNOC_SF_MEM_NOC_DISP }, -}; - static struct qcom_icc_node qns_a1noc_snoc = { .name = "qns_a1noc_snoc", .id = SM8350_SLAVE_A1NOC_SNOC, @@ -1320,40 +1266,6 @@ static struct qcom_icc_node srvc_snoc = { .buswidth = 4, }; -static struct qcom_icc_node qns_llcc_disp = { - .name = "qns_llcc_disp", - .id = SM8350_SLAVE_LLCC_DISP, - .channels = 4, - .buswidth = 16, - .num_links = 1, - .links = { SM8350_MASTER_LLCC_DISP }, -}; - -static struct qcom_icc_node ebi_disp = { - .name = "ebi_disp", - .id = SM8350_SLAVE_EBI1_DISP, - .channels = 4, - .buswidth = 4, -}; - -static struct qcom_icc_node qns_mem_noc_hf_disp = { - .name = "qns_mem_noc_hf_disp", - .id = SM8350_SLAVE_MNOC_HF_MEM_NOC_DISP, - .channels = 2, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_MASTER_MNOC_HF_MEM_NOC_DISP }, -}; - -static struct qcom_icc_node qns_mem_noc_sf_disp = { - .name = "qns_mem_noc_sf_disp", - .id = SM8350_SLAVE_MNOC_SF_MEM_NOC_DISP, - .channels = 2, - .buswidth = 32, - .num_links = 1, - .links = { SM8350_MASTER_MNOC_SF_MEM_NOC_DISP }, -}; - static struct qcom_icc_bcm bcm_acv = { .name = "ACV", .enable_mask = BIT(3), @@ -1583,55 +1495,6 @@ static struct qcom_icc_bcm bcm_sn14 = { .nodes = { &qns_pcie_mem_noc }, }; -static struct qcom_icc_bcm bcm_acv_disp = { - .name = "ACV", - .keepalive = false, - .num_nodes = 1, - .nodes = { &ebi_disp }, -}; - -static struct qcom_icc_bcm bcm_mc0_disp = { - .name = "MC0", - .keepalive = false, - .num_nodes = 1, - .nodes = { &ebi_disp }, -}; - -static struct qcom_icc_bcm bcm_mm0_disp = { - .name = "MM0", - .keepalive = false, - .num_nodes = 1, - .nodes = { &qns_mem_noc_hf_disp }, -}; - -static struct qcom_icc_bcm bcm_mm1_disp = { - .name = "MM1", - .keepalive = false, - .num_nodes = 2, - .nodes = { &qxm_mdp0_disp, &qxm_mdp1_disp }, -}; - -static struct qcom_icc_bcm bcm_mm4_disp = { - .name = "MM4", - .keepalive = false, - .num_nodes = 1, - .nodes = { &qns_mem_noc_sf_disp }, -}; - -static struct qcom_icc_bcm bcm_mm5_disp = { - .name = "MM5", - .keepalive = false, - .num_nodes = 1, - .nodes = { &qxm_rot_disp }, -}; - -static struct qcom_icc_bcm bcm_sh0_disp = { - .name = "SH0", - .keepalive = false, - .num_nodes = 1, - .nodes = { &qns_llcc_disp }, -}; - static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { }; @@ -1785,7 +1648,6 @@ static struct qcom_icc_bcm * const gem_noc_bcms[] = { &bcm_sh2, &bcm_sh3, &bcm_sh4, - &bcm_sh0_disp, }; static struct qcom_icc_node * const gem_noc_nodes[] = { @@ -1808,9 +1670,6 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC_1] = &srvc_even_gemnoc, [SLAVE_SERVICE_GEM_NOC_2] = &srvc_odd_gemnoc, [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, - [MASTER_MNOC_HF_MEM_NOC_DISP] = &qnm_mnoc_hf_disp, - [MASTER_MNOC_SF_MEM_NOC_DISP] = &qnm_mnoc_sf_disp, - [SLAVE_LLCC_DISP] = &qns_llcc_disp, }; static const struct qcom_icc_desc sm8350_gem_noc = { @@ -1843,15 +1702,11 @@ static const struct qcom_icc_desc sm8350_lpass_ag_noc = { static struct qcom_icc_bcm * const mc_virt_bcms[] = { &bcm_acv, &bcm_mc0, - &bcm_acv_disp, - &bcm_mc0_disp, }; static struct qcom_icc_node * const mc_virt_nodes[] = { [MASTER_LLCC] = &llcc_mc, [SLAVE_EBI1] = &ebi, - [MASTER_LLCC_DISP] = &llcc_mc_disp, - [SLAVE_EBI1_DISP] = &ebi_disp, }; static const struct qcom_icc_desc sm8350_mc_virt = { @@ -1866,10 +1721,6 @@ static struct qcom_icc_bcm * const mmss_noc_bcms[] = { &bcm_mm1, &bcm_mm4, &bcm_mm5, - &bcm_mm0_disp, - &bcm_mm1_disp, - &bcm_mm4_disp, - &bcm_mm5_disp, }; static struct qcom_icc_node * const mmss_noc_nodes[] = { @@ -1886,11 +1737,6 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, [SLAVE_SERVICE_MNOC] = &srvc_mnoc, - [MASTER_MDP0_DISP] = &qxm_mdp0_disp, - [MASTER_MDP1_DISP] = &qxm_mdp1_disp, - [MASTER_ROTATOR_DISP] = &qxm_rot_disp, - [SLAVE_MNOC_HF_MEM_NOC_DISP] = &qns_mem_noc_hf_disp, - [SLAVE_MNOC_SF_MEM_NOC_DISP] = &qns_mem_noc_sf_disp, }; static const struct qcom_icc_desc sm8350_mmss_noc = { @@ -1965,6 +1811,7 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8350", .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/interconnect/qcom/sm8350.h b/drivers/interconnect/qcom/sm8350.h index 328d15238a0d..074c6131ab36 100644 --- a/drivers/interconnect/qcom/sm8350.h +++ b/drivers/interconnect/qcom/sm8350.h @@ -154,15 +154,5 @@ #define SM8350_SLAVE_PCIE_1 143 #define SM8350_SLAVE_QDSS_STM 144 #define SM8350_SLAVE_TCU 145 -#define SM8350_MASTER_LLCC_DISP 146 -#define SM8350_MASTER_MNOC_HF_MEM_NOC_DISP 147 -#define SM8350_MASTER_MNOC_SF_MEM_NOC_DISP 148 -#define SM8350_MASTER_MDP0_DISP 149 -#define SM8350_MASTER_MDP1_DISP 150 -#define SM8350_MASTER_ROTATOR_DISP 151 -#define SM8350_SLAVE_EBI1_DISP 152 -#define SM8350_SLAVE_LLCC_DISP 153 -#define SM8350_SLAVE_MNOC_HF_MEM_NOC_DISP 154 -#define SM8350_SLAVE_MNOC_SF_MEM_NOC_DISP 155 #endif diff --git a/drivers/misc/cxl/of.c b/drivers/misc/cxl/of.c index bcc005dff1c0..03633cccd043 100644 --- a/drivers/misc/cxl/of.c +++ b/drivers/misc/cxl/of.c @@ -7,65 +7,12 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include "cxl.h" - -static const __be32 *read_prop_string(const struct device_node *np, - const char *prop_name) -{ - const __be32 *prop; - - prop = of_get_property(np, prop_name, NULL); - if (cxl_verbose && prop) - pr_info("%s: %s\n", prop_name, (char *) prop); - return prop; -} - -static const __be32 *read_prop_dword(const struct device_node *np, - const char *prop_name, u32 *val) -{ - const __be32 *prop; - - prop = of_get_property(np, prop_name, NULL); - if (prop) - *val = be32_to_cpu(prop[0]); - if (cxl_verbose && prop) - pr_info("%s: %#x (%u)\n", prop_name, *val, *val); - return prop; -} - -static const __be64 *read_prop64_dword(const struct device_node *np, - const char *prop_name, u64 *val) -{ - const __be64 *prop; - - prop = of_get_property(np, prop_name, NULL); - if (prop) - *val = be64_to_cpu(prop[0]); - if (cxl_verbose && prop) - pr_info("%s: %#llx (%llu)\n", prop_name, *val, *val); - return prop; -} - - -static int read_handle(struct device_node *np, u64 *handle) -{ - const __be32 *prop; - u64 size; - - /* Get address and size of the node */ - prop = of_get_address(np, 0, &size, NULL); - if (size) - return -EINVAL; - - /* Helper to read a big number; size is in cells (not bytes) */ - *handle = of_read_number(prop, of_n_addr_cells(np)); - return 0; -} - static int read_phys_addr(struct device_node *np, char *prop_name, struct cxl_afu *afu) { @@ -100,9 +47,6 @@ static int read_phys_addr(struct device_node *np, char *prop_name, type, prop_name); return -EINVAL; } - if (cxl_verbose) - pr_info("%s: %#x %#llx (size %#llx)\n", - prop_name, type, addr, size); } } return 0; @@ -130,36 +74,17 @@ static int read_vpd(struct cxl *adapter, struct cxl_afu *afu) int cxl_of_read_afu_handle(struct cxl_afu *afu, struct device_node *afu_np) { - if (read_handle(afu_np, &afu->guest->handle)) - return -EINVAL; - pr_devel("AFU handle: 0x%.16llx\n", afu->guest->handle); - - return 0; + return of_property_read_reg(afu_np, 0, &afu->guest->handle, NULL); } int cxl_of_read_afu_properties(struct cxl_afu *afu, struct device_node *np) { - int i, len, rc; - char *p; - const __be32 *prop; + int i, rc; u16 device_id, vendor_id; u32 val = 0, class_code; /* Properties are read in the same order as listed in PAPR */ - if (cxl_verbose) { - pr_info("Dump of the 'ibm,coherent-platform-function' node properties:\n"); - - prop = of_get_property(np, "compatible", &len); - i = 0; - while (i < len) { - p = (char *) prop + i; - pr_info("compatible: %s\n", p); - i += strlen(p) + 1; - } - read_prop_string(np, "name"); - } - rc = read_phys_addr(np, "reg", afu); if (rc) return rc; @@ -173,25 +98,15 @@ int cxl_of_read_afu_properties(struct cxl_afu *afu, struct device_node *np) else afu->psa = true; - if (cxl_verbose) { - read_prop_string(np, "ibm,loc-code"); - read_prop_string(np, "device_type"); - } + of_property_read_u32(np, "ibm,#processes", &afu->max_procs_virtualised); - read_prop_dword(np, "ibm,#processes", &afu->max_procs_virtualised); - - if (cxl_verbose) { - read_prop_dword(np, "ibm,scratchpad-size", &val); - read_prop_dword(np, "ibm,programmable", &val); - read_prop_string(np, "ibm,phandle"); + if (cxl_verbose) read_vpd(NULL, afu); - } - read_prop_dword(np, "ibm,max-ints-per-process", &afu->guest->max_ints); + of_property_read_u32(np, "ibm,max-ints-per-process", &afu->guest->max_ints); afu->irqs_max = afu->guest->max_ints; - prop = read_prop_dword(np, "ibm,min-ints-per-process", &afu->pp_irqs); - if (prop) { + if (!of_property_read_u32(np, "ibm,min-ints-per-process", &afu->pp_irqs)) { /* One extra interrupt for the PSL interrupt is already * included. Remove it now to keep only AFU interrupts and * match the native case. @@ -199,21 +114,13 @@ int cxl_of_read_afu_properties(struct cxl_afu *afu, struct device_node *np) afu->pp_irqs--; } - if (cxl_verbose) { - read_prop_dword(np, "ibm,max-ints", &val); - read_prop_dword(np, "ibm,vpd-size", &val); - } - - read_prop64_dword(np, "ibm,error-buffer-size", &afu->eb_len); + of_property_read_u64(np, "ibm,error-buffer-size", &afu->eb_len); afu->eb_offset = 0; - if (cxl_verbose) - read_prop_dword(np, "ibm,config-record-type", &val); - - read_prop64_dword(np, "ibm,config-record-size", &afu->crs_len); + of_property_read_u64(np, "ibm,config-record-size", &afu->crs_len); afu->crs_offset = 0; - read_prop_dword(np, "ibm,#config-records", &afu->crs_num); + of_property_read_u32(np, "ibm,#config-records", &afu->crs_num); if (cxl_verbose) { for (i = 0; i < afu->crs_num; i++) { @@ -235,35 +142,18 @@ int cxl_of_read_afu_properties(struct cxl_afu *afu, struct device_node *np) i, class_code); } } - - read_prop_dword(np, "ibm,function-number", &val); - read_prop_dword(np, "ibm,privileged-function", &val); - read_prop_dword(np, "vendor-id", &val); - read_prop_dword(np, "device-id", &val); - read_prop_dword(np, "revision-id", &val); - read_prop_dword(np, "class-code", &val); - read_prop_dword(np, "subsystem-vendor-id", &val); - read_prop_dword(np, "subsystem-id", &val); } /* * if "ibm,process-mmio" doesn't exist then per-process mmio is * not supported */ val = 0; - prop = read_prop_dword(np, "ibm,process-mmio", &val); - if (prop && val == 1) + if (!of_property_read_u32(np, "ibm,process-mmio", &val) && val == 1) afu->pp_psa = true; else afu->pp_psa = false; - if (cxl_verbose) { - read_prop_dword(np, "ibm,supports-aur", &val); - read_prop_dword(np, "ibm,supports-csrp", &val); - read_prop_dword(np, "ibm,supports-prr", &val); - } - - prop = read_prop_dword(np, "ibm,function-error-interrupt", &val); - if (prop) + if (!of_property_read_u32(np, "ibm,function-error-interrupt", &val)) afu->serr_hwirq = val; pr_devel("AFU handle: %#llx\n", afu->guest->handle); @@ -334,95 +224,44 @@ err: int cxl_of_read_adapter_handle(struct cxl *adapter, struct device_node *np) { - if (read_handle(np, &adapter->guest->handle)) - return -EINVAL; - pr_devel("Adapter handle: 0x%.16llx\n", adapter->guest->handle); - - return 0; + return of_property_read_reg(np, 0, &adapter->guest->handle, NULL); } int cxl_of_read_adapter_properties(struct cxl *adapter, struct device_node *np) { - int rc, len, naddr, i; - char *p; - const __be32 *prop; + int rc; + const char *p; u32 val = 0; /* Properties are read in the same order as listed in PAPR */ - naddr = of_n_addr_cells(np); - - if (cxl_verbose) { - pr_info("Dump of the 'ibm,coherent-platform-facility' node properties:\n"); - - read_prop_dword(np, "#address-cells", &val); - read_prop_dword(np, "#size-cells", &val); - - prop = of_get_property(np, "compatible", &len); - i = 0; - while (i < len) { - p = (char *) prop + i; - pr_info("compatible: %s\n", p); - i += strlen(p) + 1; - } - read_prop_string(np, "name"); - read_prop_string(np, "model"); - - prop = of_get_property(np, "reg", NULL); - if (prop) { - pr_info("reg: addr:%#llx size:%#x\n", - of_read_number(prop, naddr), - be32_to_cpu(prop[naddr])); - } - - read_prop_string(np, "ibm,loc-code"); - } - if ((rc = read_adapter_irq_config(adapter, np))) return rc; - if (cxl_verbose) { - read_prop_string(np, "device_type"); - read_prop_string(np, "ibm,phandle"); - } - - prop = read_prop_dword(np, "ibm,caia-version", &val); - if (prop) { + if (!of_property_read_u32(np, "ibm,caia-version", &val)) { adapter->caia_major = (val & 0xFF00) >> 8; adapter->caia_minor = val & 0xFF; } - prop = read_prop_dword(np, "ibm,psl-revision", &val); - if (prop) + if (!of_property_read_u32(np, "ibm,psl-revision", &val)) adapter->psl_rev = val; - prop = read_prop_string(np, "status"); - if (prop) { - adapter->guest->status = kasprintf(GFP_KERNEL, "%s", (char *) prop); + if (!of_property_read_string(np, "status", &p)) { + adapter->guest->status = kasprintf(GFP_KERNEL, "%s", p); if (adapter->guest->status == NULL) return -ENOMEM; } - prop = read_prop_dword(np, "vendor-id", &val); - if (prop) + if (!of_property_read_u32(np, "vendor-id", &val)) adapter->guest->vendor = val; - prop = read_prop_dword(np, "device-id", &val); - if (prop) + if (!of_property_read_u32(np, "device-id", &val)) adapter->guest->device = val; - if (cxl_verbose) { - read_prop_dword(np, "ibm,privileged-facility", &val); - read_prop_dword(np, "revision-id", &val); - read_prop_dword(np, "class-code", &val); - } - - prop = read_prop_dword(np, "subsystem-vendor-id", &val); - if (prop) + if (!of_property_read_u32(np, "subsystem-vendor-id", &val)) adapter->guest->subsystem_vendor = val; - prop = read_prop_dword(np, "subsystem-id", &val); - if (prop) + if (!of_property_read_u32(np, "subsystem-id", &val)) adapter->guest->subsystem = val; if (cxl_verbose) diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 4cf9e7c42a24..3d52f9b92d0d 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -363,17 +363,17 @@ int cxl_calc_capp_routing(struct pci_dev *dev, u64 *chipid, { int rc; struct device_node *np; - const __be32 *prop; + u32 id; if (!(np = pnv_pci_get_phb_node(dev))) return -ENODEV; - while (np && !(prop = of_get_property(np, "ibm,chip-id", NULL))) + while (np && of_property_read_u32(np, "ibm,chip-id", &id)) np = of_get_next_parent(np); if (!np) return -ENODEV; - *chipid = be32_to_cpup(prop); + *chipid = id; rc = get_phb_index(np, phb_index); if (rc) { @@ -398,32 +398,26 @@ static DEFINE_MUTEX(indications_mutex); static int get_phb_indications(struct pci_dev *dev, u64 *capiind, u64 *asnind, u64 *nbwind) { - static u64 nbw, asn, capi = 0; + static u32 val[3]; struct device_node *np; - const __be32 *prop; mutex_lock(&indications_mutex); - if (!capi) { + if (!val[0]) { if (!(np = pnv_pci_get_phb_node(dev))) { mutex_unlock(&indications_mutex); return -ENODEV; } - prop = of_get_property(np, "ibm,phb-indications", NULL); - if (!prop) { - nbw = 0x0300UL; /* legacy values */ - asn = 0x0400UL; - capi = 0x0200UL; - } else { - nbw = (u64)be32_to_cpu(prop[2]); - asn = (u64)be32_to_cpu(prop[1]); - capi = (u64)be32_to_cpu(prop[0]); + if (of_property_read_u32_array(np, "ibm,phb-indications", val, 3)) { + val[2] = 0x0300UL; /* legacy values */ + val[1] = 0x0400UL; + val[0] = 0x0200UL; } of_node_put(np); } - *capiind = capi; - *asnind = asn; - *nbwind = nbw; + *capiind = val[0]; + *asnind = val[1]; + *nbwind = val[2]; mutex_unlock(&indications_mutex); return 0; } @@ -605,7 +599,7 @@ static void cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev) /* Do not fail when CAPP timebase sync is not supported by OPAL */ of_node_get(np); - if (! of_get_property(np, "ibm,capp-timebase-sync", NULL)) { + if (!of_property_present(np, "ibm,capp-timebase-sync")) { of_node_put(np); dev_info(&dev->dev, "PSL timebase inactive: OPAL support missing\n"); return; diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 315c43f17dd3..409bd1c39663 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -579,7 +579,7 @@ static void release_afu_config_record(struct kobject *kobj) kfree(cr); } -static struct kobj_type afu_config_record_type = { +static const struct kobj_type afu_config_record_type = { .sysfs_ops = &kobj_sysfs_ops, .release = release_afu_config_record, .default_groups = afu_cr_groups, diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index da87abe93daf..74181b8c386b 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -27,7 +27,8 @@ #define MDSP_DOMAIN_ID (1) #define SDSP_DOMAIN_ID (2) #define CDSP_DOMAIN_ID (3) -#define FASTRPC_DEV_MAX 4 /* adsp, mdsp, slpi, cdsp*/ +#define CDSP1_DOMAIN_ID (4) +#define FASTRPC_DEV_MAX 5 /* adsp, mdsp, slpi, cdsp, cdsp1 */ #define FASTRPC_MAX_SESSIONS 14 #define FASTRPC_MAX_VMIDS 16 #define FASTRPC_ALIGN 128 @@ -106,7 +107,7 @@ #define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev) static const char *domains[FASTRPC_DEV_MAX] = { "adsp", "mdsp", - "sdsp", "cdsp"}; + "sdsp", "cdsp", "cdsp1" }; struct fastrpc_phy_page { u64 addr; /* physical address */ u64 size; /* size of contiguous region */ @@ -2269,7 +2270,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) return err; } - for (i = 0; i <= CDSP_DOMAIN_ID; i++) { + for (i = 0; i < FASTRPC_DEV_MAX; i++) { if (!strcmp(domains[i], domain)) { domain_id = i; break; @@ -2327,13 +2328,14 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) case ADSP_DOMAIN_ID: case MDSP_DOMAIN_ID: case SDSP_DOMAIN_ID: - /* Unsigned PD offloading is only supported on CDSP*/ + /* Unsigned PD offloading is only supported on CDSP and CDSP1 */ data->unsigned_support = false; err = fastrpc_device_register(rdev, data, secure_dsp, domains[domain_id]); if (err) goto fdev_error; break; case CDSP_DOMAIN_ID: + case CDSP1_DOMAIN_ID: data->unsigned_support = true; /* Create both device nodes so that we can allow both Signed and Unsigned PD */ err = fastrpc_device_register(rdev, data, true, domains[domain_id]); diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 88b91ad8e541..0cf31164b470 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -95,6 +95,7 @@ #include <linux/kallsyms.h> #include <asm/sections.h> +#include <asm/rwonce.h> #define v1printk(a...) do { \ if (verbose) \ @@ -126,7 +127,6 @@ static int final_ack; static int force_hwbrks; static int hwbreaks_ok; static int hw_break_val; -static int hw_break_val2; static int cont_instead_of_sstep; static unsigned long cont_thread_id; static unsigned long sstep_thread_id; @@ -284,7 +284,7 @@ static void hw_rem_access_break(char *arg) static void hw_break_val_access(void) { - hw_break_val2 = hw_break_val; + READ_ONCE(hw_break_val); } static void hw_break_val_write(void) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 49868a45c0ad..1fc635a27568 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -1038,7 +1038,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO; if (of_property_read_bool(np, "st,wakeup-z-hi")) pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI; - if (of_get_property(np, "st,wakeup-threshold", &val)) + if (!of_property_read_u32(np, "st,wakeup-threshold", &val)) pdata->wakeup_thresh = val; if (of_property_read_bool(np, "st,wakeup2-x-lo")) @@ -1053,7 +1053,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_LO; if (of_property_read_bool(np, "st,wakeup2-z-hi")) pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_HI; - if (of_get_property(np, "st,wakeup2-threshold", &val)) + if (!of_property_read_u32(np, "st,wakeup2-threshold", &val)) pdata->wakeup_thresh2 = val; if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) { diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h index 10125a22d5a5..d2028d6c6f08 100644 --- a/drivers/misc/ocxl/ocxl_internal.h +++ b/drivers/misc/ocxl/ocxl_internal.h @@ -97,8 +97,6 @@ struct ocxl_process_element { __be32 software_state; }; -int ocxl_create_cdev(struct ocxl_afu *afu); -void ocxl_destroy_cdev(struct ocxl_afu *afu); int ocxl_file_register_afu(struct ocxl_afu *afu); void ocxl_file_unregister_afu(struct ocxl_afu *afu); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 2ad4387c9837..1a7796ab3fad 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -185,10 +185,10 @@ static ssize_t tsl2550_store_power_state(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct tsl2550_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int ret; - if (val > 1) + if (kstrtoul(buf, 10, &val) || val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -217,10 +217,10 @@ static ssize_t tsl2550_store_operating_mode(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct tsl2550_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int ret; - if (val > 1) + if (kstrtoul(buf, 10, &val) || val > 1) return -EINVAL; if (data->power_state == 0) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 283134498fbc..d2c384f58028 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID config NVMEM_U_BOOT_ENV tristate "U-Boot environment variables support" depends on OF && MTD - select CRC32 - select GENERIC_NET_UTILS + select NVMEM_LAYOUT_U_BOOT_ENV help U-Boot stores its setup as environment variables. This driver adds support for verifying & exporting such data. It also exposes variables diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c index cf920542f939..1ba494497698 100644 --- a/drivers/nvmem/imx-ocotp-ele.c +++ b/drivers/nvmem/imx-ocotp-ele.c @@ -14,8 +14,9 @@ #include <linux/slab.h> enum fuse_type { - FUSE_FSB = 1, - FUSE_ELE = 2, + FUSE_FSB = BIT(0), + FUSE_ELE = BIT(1), + FUSE_ECC = BIT(2), FUSE_INVALID = -1 }; @@ -93,7 +94,10 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz continue; } - *buf++ = readl_relaxed(reg + (i << 2)); + if (type & FUSE_ECC) + *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0); + else + *buf++ = readl_relaxed(reg + (i << 2)); } memcpy(val, (u8 *)p, bytes); @@ -155,8 +159,30 @@ static const struct ocotp_devtype_data imx93_ocotp_data = { }, }; +static const struct ocotp_devtype_data imx95_ocotp_data = { + .reg_off = 0x8000, + .reg_read = imx_ocotp_reg_read, + .size = 2048, + .num_entry = 12, + .entry = { + { 0, 1, FUSE_FSB | FUSE_ECC }, + { 7, 1, FUSE_FSB | FUSE_ECC }, + { 9, 3, FUSE_FSB | FUSE_ECC }, + { 12, 24, FUSE_FSB }, + { 36, 2, FUSE_FSB | FUSE_ECC }, + { 38, 14, FUSE_FSB }, + { 63, 1, FUSE_ELE }, + { 128, 16, FUSE_ELE }, + { 188, 1, FUSE_ELE }, + { 317, 2, FUSE_FSB | FUSE_ECC }, + { 320, 7, FUSE_FSB }, + { 328, 184, FUSE_FSB } + }, +}; + static const struct of_device_id imx_ele_ocotp_dt_ids[] = { { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, }, + { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, }, {}, }; MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids); diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig index 9c6e672fc350..5e586dfebe47 100644 --- a/drivers/nvmem/layouts/Kconfig +++ b/drivers/nvmem/layouts/Kconfig @@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV If unsure, say N. +config NVMEM_LAYOUT_U_BOOT_ENV + tristate "U-Boot environment variables layout" + select CRC32 + select GENERIC_NET_UTILS + help + U-Boot stores its setup as environment variables. This driver adds + support for verifying & exporting such data. It also exposes variables + as NVMEM cells so they can be referenced by other drivers. + + If unsure, say N. + endmenu endif diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile index 2974bd7d33ed..4940c9db0665 100644 --- a/drivers/nvmem/layouts/Makefile +++ b/drivers/nvmem/layouts/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o +obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o diff --git a/drivers/nvmem/layouts/u-boot-env.c b/drivers/nvmem/layouts/u-boot-env.c new file mode 100644 index 000000000000..731e6f4f12b2 --- /dev/null +++ b/drivers/nvmem/layouts/u-boot-env.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl> + */ + +#include <linux/crc32.h> +#include <linux/etherdevice.h> +#include <linux/export.h> +#include <linux/if_ether.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include "u-boot-env.h" + +struct u_boot_env_image_single { + __le32 crc32; + uint8_t data[]; +} __packed; + +struct u_boot_env_image_redundant { + __le32 crc32; + u8 mark; + uint8_t data[]; +} __packed; + +struct u_boot_env_image_broadcom { + __le32 magic; + __le32 len; + __le32 crc32; + DECLARE_FLEX_ARRAY(uint8_t, data); +} __packed; + +static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index, + unsigned int offset, void *buf, size_t bytes) +{ + u8 mac[ETH_ALEN]; + + if (bytes != 3 * ETH_ALEN - 1) + return -EINVAL; + + if (!mac_pton(buf, mac)) + return -EINVAL; + + if (index) + eth_addr_add(mac, index); + + ether_addr_copy(buf, mac); + + return 0; +} + +static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf, + size_t data_offset, size_t data_len) +{ + char *data = buf + data_offset; + char *var, *value, *eq; + + for (var = data; + var < data + data_len && *var; + var = value + strlen(value) + 1) { + struct nvmem_cell_info info = {}; + + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + + info.name = devm_kstrdup(dev, var, GFP_KERNEL); + if (!info.name) + return -ENOMEM; + info.offset = data_offset + value - data; + info.bytes = strlen(value); + info.np = of_get_child_by_name(dev->of_node, info.name); + if (!strcmp(var, "ethaddr")) { + info.raw_len = strlen(value); + info.bytes = ETH_ALEN; + info.read_post_process = u_boot_env_read_post_process_ethaddr; + } + + nvmem_add_one_cell(nvmem, &info); + } + + return 0; +} + +int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem, + enum u_boot_env_format format) +{ + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; + __le32 *crc32_addr; + size_t data_offset; + size_t data_len; + size_t dev_size; + uint32_t crc32; + uint32_t calc; + uint8_t *buf; + int bytes; + int err; + + dev_size = nvmem_dev_size(nvmem); + + buf = kzalloc(dev_size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + + bytes = nvmem_device_read(nvmem, 0, dev_size, buf); + if (bytes < 0) { + err = bytes; + goto err_kfree; + } else if (bytes != dev_size) { + err = -EIO; + goto err_kfree; + } + + switch (format) { + case U_BOOT_FORMAT_SINGLE: + crc32_offset = offsetof(struct u_boot_env_image_single, crc32); + crc32_data_offset = offsetof(struct u_boot_env_image_single, data); + data_offset = offsetof(struct u_boot_env_image_single, data); + break; + case U_BOOT_FORMAT_REDUNDANT: + crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32); + crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data); + data_offset = offsetof(struct u_boot_env_image_redundant, data); + break; + case U_BOOT_FORMAT_BROADCOM: + crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32); + crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data); + data_offset = offsetof(struct u_boot_env_image_broadcom, data); + break; + } + + if (dev_size < data_offset) { + dev_err(dev, "Device too small for u-boot-env\n"); + err = -EIO; + goto err_kfree; + } + + crc32_addr = (__le32 *)(buf + crc32_offset); + crc32 = le32_to_cpu(*crc32_addr); + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; + + calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; + if (calc != crc32) { + dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32); + err = -EINVAL; + goto err_kfree; + } + + buf[dev_size - 1] = '\0'; + err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len); + +err_kfree: + kfree(buf); +err_out: + return err; +} +EXPORT_SYMBOL_GPL(u_boot_env_parse); + +static int u_boot_env_add_cells(struct nvmem_layout *layout) +{ + struct device *dev = &layout->dev; + enum u_boot_env_format format; + + format = (uintptr_t)device_get_match_data(dev); + + return u_boot_env_parse(dev, layout->nvmem, format); +} + +static int u_boot_env_probe(struct nvmem_layout *layout) +{ + layout->add_cells = u_boot_env_add_cells; + + return nvmem_layout_register(layout); +} + +static void u_boot_env_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + +static const struct of_device_id u_boot_env_of_match_table[] = { + { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, }, + { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, + { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, + { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, }, + {}, +}; + +static struct nvmem_layout_driver u_boot_env_layout = { + .driver = { + .name = "u-boot-env-layout", + .of_match_table = u_boot_env_of_match_table, + }, + .probe = u_boot_env_probe, + .remove = u_boot_env_remove, +}; +module_nvmem_layout_driver(u_boot_env_layout); + +MODULE_AUTHOR("Rafał Miłecki"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table); +MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables"); diff --git a/drivers/nvmem/layouts/u-boot-env.h b/drivers/nvmem/layouts/u-boot-env.h new file mode 100644 index 000000000000..dd5f280ac047 --- /dev/null +++ b/drivers/nvmem/layouts/u-boot-env.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H +#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H + +enum u_boot_env_format { + U_BOOT_FORMAT_SINGLE, + U_BOOT_FORMAT_REDUNDANT, + U_BOOT_FORMAT_BROADCOM, +}; + +int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem, + enum u_boot_env_format format); + +#endif /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */ diff --git a/drivers/nvmem/sunplus-ocotp.c b/drivers/nvmem/sunplus-ocotp.c index 38f5d9df39cd..30d55b111a64 100644 --- a/drivers/nvmem/sunplus-ocotp.c +++ b/drivers/nvmem/sunplus-ocotp.c @@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct nvmem_device *nvmem; struct sp_ocotp_priv *otp; - struct resource *res; int ret; otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); @@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platform_device *pdev) otp->dev = dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio"); - otp->base[HB_GPIO] = devm_ioremap_resource(dev, res); + otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio"); if (IS_ERR(otp->base[HB_GPIO])) return PTR_ERR(otp->base[HB_GPIO]); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx"); - otp->base[OTPRX] = devm_ioremap_resource(dev, res); + otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx"); if (IS_ERR(otp->base[OTPRX])) return PTR_ERR(otp->base[OTPRX]); diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c index 593f0bf4a395..ced414fc9e60 100644 --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c @@ -3,23 +3,15 @@ * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl> */ -#include <linux/crc32.h> -#include <linux/etherdevice.h> -#include <linux/if_ether.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> -enum u_boot_env_format { - U_BOOT_FORMAT_SINGLE, - U_BOOT_FORMAT_REDUNDANT, - U_BOOT_FORMAT_BROADCOM, -}; +#include "layouts/u-boot-env.h" struct u_boot_env { struct device *dev; @@ -29,24 +21,6 @@ struct u_boot_env { struct mtd_info *mtd; }; -struct u_boot_env_image_single { - __le32 crc32; - uint8_t data[]; -} __packed; - -struct u_boot_env_image_redundant { - __le32 crc32; - u8 mark; - uint8_t data[]; -} __packed; - -struct u_boot_env_image_broadcom { - __le32 magic; - __le32 len; - __le32 crc32; - DECLARE_FLEX_ARRAY(uint8_t, data); -} __packed; - static int u_boot_env_read(void *context, unsigned int offset, void *val, size_t bytes) { @@ -69,141 +43,6 @@ static int u_boot_env_read(void *context, unsigned int offset, void *val, return 0; } -static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index, - unsigned int offset, void *buf, size_t bytes) -{ - u8 mac[ETH_ALEN]; - - if (bytes != 3 * ETH_ALEN - 1) - return -EINVAL; - - if (!mac_pton(buf, mac)) - return -EINVAL; - - if (index) - eth_addr_add(mac, index); - - ether_addr_copy(buf, mac); - - return 0; -} - -static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, - size_t data_offset, size_t data_len) -{ - struct nvmem_device *nvmem = priv->nvmem; - struct device *dev = priv->dev; - char *data = buf + data_offset; - char *var, *value, *eq; - - for (var = data; - var < data + data_len && *var; - var = value + strlen(value) + 1) { - struct nvmem_cell_info info = {}; - - eq = strchr(var, '='); - if (!eq) - break; - *eq = '\0'; - value = eq + 1; - - info.name = devm_kstrdup(dev, var, GFP_KERNEL); - if (!info.name) - return -ENOMEM; - info.offset = data_offset + value - data; - info.bytes = strlen(value); - info.np = of_get_child_by_name(dev->of_node, info.name); - if (!strcmp(var, "ethaddr")) { - info.raw_len = strlen(value); - info.bytes = ETH_ALEN; - info.read_post_process = u_boot_env_read_post_process_ethaddr; - } - - nvmem_add_one_cell(nvmem, &info); - } - - return 0; -} - -static int u_boot_env_parse(struct u_boot_env *priv) -{ - struct nvmem_device *nvmem = priv->nvmem; - struct device *dev = priv->dev; - size_t crc32_data_offset; - size_t crc32_data_len; - size_t crc32_offset; - __le32 *crc32_addr; - size_t data_offset; - size_t data_len; - size_t dev_size; - uint32_t crc32; - uint32_t calc; - uint8_t *buf; - int bytes; - int err; - - dev_size = nvmem_dev_size(nvmem); - - buf = kzalloc(dev_size, GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto err_out; - } - - bytes = nvmem_device_read(nvmem, 0, dev_size, buf); - if (bytes < 0) { - err = bytes; - goto err_kfree; - } else if (bytes != dev_size) { - err = -EIO; - goto err_kfree; - } - - switch (priv->format) { - case U_BOOT_FORMAT_SINGLE: - crc32_offset = offsetof(struct u_boot_env_image_single, crc32); - crc32_data_offset = offsetof(struct u_boot_env_image_single, data); - data_offset = offsetof(struct u_boot_env_image_single, data); - break; - case U_BOOT_FORMAT_REDUNDANT: - crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32); - crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data); - data_offset = offsetof(struct u_boot_env_image_redundant, data); - break; - case U_BOOT_FORMAT_BROADCOM: - crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32); - crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data); - data_offset = offsetof(struct u_boot_env_image_broadcom, data); - break; - } - - if (dev_size < data_offset) { - dev_err(dev, "Device too small for u-boot-env\n"); - err = -EIO; - goto err_kfree; - } - - crc32_addr = (__le32 *)(buf + crc32_offset); - crc32 = le32_to_cpu(*crc32_addr); - crc32_data_len = dev_size - crc32_data_offset; - data_len = dev_size - data_offset; - - calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; - if (calc != crc32) { - dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32); - err = -EINVAL; - goto err_kfree; - } - - buf[dev_size - 1] = '\0'; - err = u_boot_env_add_cells(priv, buf, data_offset, data_len); - -err_kfree: - kfree(buf); -err_out: - return err; -} - static int u_boot_env_probe(struct platform_device *pdev) { struct nvmem_config config = { @@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platform_device *pdev) if (IS_ERR(priv->nvmem)) return PTR_ERR(priv->nvmem); - return u_boot_env_parse(priv); + return u_boot_env_parse(dev, priv->nvmem, priv->format); } static const struct of_device_id u_boot_env_of_match_table[] = { diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c index 63d03a0df5cc..abaffb4e1c1c 100644 --- a/drivers/pps/clients/pps_parport.c +++ b/drivers/pps/clients/pps_parport.c @@ -149,6 +149,9 @@ static void parport_attach(struct parport *port) } index = ida_alloc(&pps_client_index, GFP_KERNEL); + if (index < 0) + goto err_free_device; + memset(&pps_client_cb, 0, sizeof(pps_client_cb)); pps_client_cb.private = device; pps_client_cb.irq_func = parport_irq; @@ -159,7 +162,7 @@ static void parport_attach(struct parport *port) index); if (!device->pardev) { pr_err("couldn't register with %s\n", port->name); - goto err_free; + goto err_free_ida; } if (parport_claim_or_block(device->pardev) < 0) { @@ -187,8 +190,9 @@ err_release_dev: parport_release(device->pardev); err_unregister_dev: parport_unregister_device(device->pardev); -err_free: +err_free_ida: ida_free(&pps_client_index, index); +err_free_device: kfree(device); } diff --git a/drivers/slimbus/messaging.c b/drivers/slimbus/messaging.c index 4ce0cb61e481..242570a5e565 100644 --- a/drivers/slimbus/messaging.c +++ b/drivers/slimbus/messaging.c @@ -111,7 +111,8 @@ int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn) { DECLARE_COMPLETION_ONSTACK(done); bool need_tid = false, clk_pause_msg = false; - int ret, timeout; + int ret; + unsigned long time_left; /* * do not vote for runtime-PM if the transactions are part of clock @@ -151,9 +152,9 @@ int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn) if (!ret && need_tid && !txn->msg->comp) { unsigned long ms = txn->rl + HZ; - timeout = wait_for_completion_timeout(txn->comp, - msecs_to_jiffies(ms)); - if (!timeout) { + time_left = wait_for_completion_timeout(txn->comp, + msecs_to_jiffies(ms)); + if (!time_left) { ret = -ETIMEDOUT; slim_free_txn_tid(ctrl, txn); } diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c index 0274bc285b60..e25f9bdd9b23 100644 --- a/drivers/slimbus/qcom-ctrl.c +++ b/drivers/slimbus/qcom-ctrl.c @@ -330,7 +330,8 @@ static int qcom_xfer_msg(struct slim_controller *sctrl, void *pbuf = slim_alloc_txbuf(ctrl, txn, &done); unsigned long ms = txn->rl + HZ; u8 *puc; - int ret = 0, timeout, retries = QCOM_BUF_ALLOC_RETRIES; + int ret = 0, retries = QCOM_BUF_ALLOC_RETRIES; + unsigned long time_left; u8 la = txn->la; u32 *head; /* HW expects length field to be excluded */ @@ -374,9 +375,9 @@ static int qcom_xfer_msg(struct slim_controller *sctrl, memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); qcom_slim_queue_tx(ctrl, head, txn->rl, MGR_TX_MSG); - timeout = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); + time_left = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); - if (!timeout) { + if (!time_left) { dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, txn->mt); ret = -ETIMEDOUT; diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index e0b21f0f79c1..1ac8e2912fd1 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -788,7 +788,8 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl, struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(sctrl->dev); DECLARE_COMPLETION_ONSTACK(tx_sent); DECLARE_COMPLETION_ONSTACK(done); - int ret, timeout, i; + int ret, i; + unsigned long time_left; u8 wbuf[SLIM_MSGQ_BUF_LEN]; u8 rbuf[SLIM_MSGQ_BUF_LEN]; u32 *pbuf; @@ -890,8 +891,8 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl, return ret; } - timeout = wait_for_completion_timeout(&tx_sent, HZ); - if (!timeout) { + time_left = wait_for_completion_timeout(&tx_sent, HZ); + if (!time_left) { dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, txn->mt); mutex_unlock(&ctrl->tx_lock); @@ -899,8 +900,8 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl, } if (usr_msg) { - timeout = wait_for_completion_timeout(&done, HZ); - if (!timeout) { + time_left = wait_for_completion_timeout(&done, HZ); + if (!time_left) { dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, txn->mt); mutex_unlock(&ctrl->tx_lock); @@ -916,7 +917,8 @@ static int qcom_slim_ngd_xfer_msg_sync(struct slim_controller *ctrl, struct slim_msg_txn *txn) { DECLARE_COMPLETION_ONSTACK(done); - int ret, timeout; + int ret; + unsigned long time_left; ret = pm_runtime_get_sync(ctrl->dev); if (ret < 0) @@ -928,8 +930,8 @@ static int qcom_slim_ngd_xfer_msg_sync(struct slim_controller *ctrl, if (ret) goto pm_put; - timeout = wait_for_completion_timeout(&done, HZ); - if (!timeout) { + time_left = wait_for_completion_timeout(&done, HZ); + if (!time_left) { dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, txn->mt); ret = -ETIMEDOUT; @@ -1168,11 +1170,12 @@ static int qcom_slim_ngd_power_up(struct qcom_slim_ngd_ctrl *ctrl) enum qcom_slim_ngd_state cur_state = ctrl->state; struct qcom_slim_ngd *ngd = ctrl->ngd; u32 laddr, rx_msgq; - int timeout, ret = 0; + int ret = 0; + unsigned long time_left; if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) { - timeout = wait_for_completion_timeout(&ctrl->qmi.qmi_comp, HZ); - if (!timeout) + time_left = wait_for_completion_timeout(&ctrl->qmi.qmi_comp, HZ); + if (!time_left) return -EREMOTEIO; } @@ -1217,8 +1220,8 @@ static int qcom_slim_ngd_power_up(struct qcom_slim_ngd_ctrl *ctrl) ngd->base + NGD_RX_MSGQ_CFG); qcom_slim_ngd_setup(ctrl); - timeout = wait_for_completion_timeout(&ctrl->reconf, HZ); - if (!timeout) { + time_left = wait_for_completion_timeout(&ctrl->reconf, HZ); + if (!time_left) { dev_err(ctrl->dev, "capability exchange timed-out\n"); return -ETIMEDOUT; } diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index cd00d9607565..4ae1a7039418 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -547,7 +547,8 @@ static int ad5933_ring_preenable(struct iio_dev *indio_dev) struct ad5933_state *st = iio_priv(indio_dev); int ret; - if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) + if (bitmap_empty(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev))) return -EINVAL; ret = ad5933_reset(st); @@ -625,7 +626,7 @@ static void ad5933_work(struct work_struct *work) if (status & AD5933_STAT_DATA_VALID) { int scan_count = bitmap_weight(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ad5933_i2c_read(st->client, test_bit(1, indio_dev->active_scan_mask) ? AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 20d2a55cb40b..004a549c6c7d 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -118,7 +118,7 @@ static const struct sysfs_ops map_sysfs_ops = { .show = map_type_show, }; -static struct kobj_type map_attr_type = { +static const struct kobj_type map_attr_type = { .release = map_release, .sysfs_ops = &map_sysfs_ops, .default_groups = map_groups, @@ -207,7 +207,7 @@ static const struct sysfs_ops portio_sysfs_ops = { .show = portio_type_show, }; -static struct kobj_type portio_attr_type = { +static const struct kobj_type portio_attr_type = { .release = portio_release, .sysfs_ops = &portio_sysfs_ops, .default_groups = portio_groups, diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index b2d76c1784bd..a2ecbb863c57 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -541,8 +541,8 @@ static void ds2482_remove(struct i2c_client *client) * Driver data (common to all clients) */ static const struct i2c_device_id ds2482_id[] = { - { "ds2482", 0 }, - { "ds2484", 0 }, + { "ds2482" }, + { "ds2484" }, { } }; MODULE_DEVICE_TABLE(i2c, ds2482_id); diff --git a/include/dt-bindings/iio/adi,ad4695.h b/include/dt-bindings/iio/adi,ad4695.h new file mode 100644 index 000000000000..9fbef542bf67 --- /dev/null +++ b/include/dt-bindings/iio/adi,ad4695.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_ADI_AD4695_H +#define _DT_BINDINGS_ADI_AD4695_H + +#define AD4695_COMMON_MODE_REFGND 0xFF +#define AD4695_COMMON_MODE_COM 0xFE + +#endif /* _DT_BINDINGS_ADI_AD4695_H */ diff --git a/include/dt-bindings/interconnect/qcom,msm8937.h b/include/dt-bindings/interconnect/qcom,msm8937.h new file mode 100644 index 000000000000..98b8a4637aab --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8937.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Qualcomm MSM8937 interconnect IDs + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8937_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8937_H + +/* BIMC fabric */ +#define MAS_APPS_PROC 0 +#define MAS_OXILI 1 +#define MAS_SNOC_BIMC_0 2 +#define MAS_SNOC_BIMC_2 3 +#define MAS_SNOC_BIMC_1 4 +#define MAS_TCU_0 5 +#define SLV_EBI 6 +#define SLV_BIMC_SNOC 7 + +/* PCNOC fabric */ +#define MAS_SPDM 0 +#define MAS_BLSP_1 1 +#define MAS_BLSP_2 2 +#define MAS_USB_HS1 3 +#define MAS_XI_USB_HS1 4 +#define MAS_CRYPTO 5 +#define MAS_SDCC_1 6 +#define MAS_SDCC_2 7 +#define MAS_SNOC_PCNOC 8 +#define PCNOC_M_0 9 +#define PCNOC_M_1 10 +#define PCNOC_INT_0 11 +#define PCNOC_INT_1 12 +#define PCNOC_INT_2 13 +#define PCNOC_INT_3 14 +#define PCNOC_S_0 15 +#define PCNOC_S_1 16 +#define PCNOC_S_2 17 +#define PCNOC_S_3 18 +#define PCNOC_S_4 19 +#define PCNOC_S_6 20 +#define PCNOC_S_7 21 +#define PCNOC_S_8 22 +#define SLV_SDCC_2 23 +#define SLV_SPDM 24 +#define SLV_PDM 25 +#define SLV_PRNG 26 +#define SLV_TCSR 27 +#define SLV_SNOC_CFG 28 +#define SLV_MESSAGE_RAM 29 +#define SLV_CAMERA_SS_CFG 30 +#define SLV_DISP_SS_CFG 31 +#define SLV_VENUS_CFG 32 +#define SLV_GPU_CFG 33 +#define SLV_TLMM 34 +#define SLV_BLSP_1 35 +#define SLV_BLSP_2 36 +#define SLV_PMIC_ARB 37 +#define SLV_SDCC_1 38 +#define SLV_CRYPTO_0_CFG 39 +#define SLV_USB_HS 40 +#define SLV_TCU 41 +#define SLV_PCNOC_SNOC 42 + +/* SNOC fabric */ +#define MAS_QDSS_BAM 0 +#define MAS_BIMC_SNOC 1 +#define MAS_PCNOC_SNOC 2 +#define MAS_QDSS_ETR 3 +#define QDSS_INT 4 +#define SNOC_INT_0 5 +#define SNOC_INT_1 6 +#define SNOC_INT_2 7 +#define SLV_KPSS_AHB 8 +#define SLV_WCSS 9 +#define SLV_SNOC_BIMC_1 10 +#define SLV_IMEM 11 +#define SLV_SNOC_PCNOC 12 +#define SLV_QDSS_STM 13 +#define SLV_CATS_1 14 +#define SLV_LPASS 15 + +/* SNOC-MM fabric */ +#define MAS_JPEG 0 +#define MAS_MDP 1 +#define MAS_VENUS 2 +#define MAS_VFE0 3 +#define MAS_VFE1 4 +#define MAS_CPP 5 +#define SLV_SNOC_BIMC_0 6 +#define SLV_SNOC_BIMC_2 7 +#define SLV_CATS_0 8 + +#endif /* __DT_BINDINGS_INTERCONNECT_QCOM_MSM8937_H */ diff --git a/include/dt-bindings/interconnect/qcom,msm8976.h b/include/dt-bindings/interconnect/qcom,msm8976.h new file mode 100644 index 000000000000..4ea90f22320e --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8976.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Qualcomm MSM8976 interconnect IDs + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8976_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8976_H + +/* BIMC fabric */ +#define MAS_APPS_PROC 0 +#define MAS_SMMNOC_BIMC 1 +#define MAS_SNOC_BIMC 2 +#define MAS_TCU_0 3 +#define SLV_EBI 4 +#define SLV_BIMC_SNOC 5 + +/* PCNOC fabric */ +#define MAS_USB_HS2 0 +#define MAS_BLSP_1 1 +#define MAS_USB_HS1 2 +#define MAS_BLSP_2 3 +#define MAS_CRYPTO 4 +#define MAS_SDCC_1 5 +#define MAS_SDCC_2 6 +#define MAS_SDCC_3 7 +#define MAS_SNOC_PCNOC 8 +#define MAS_LPASS_AHB 9 +#define MAS_SPDM 10 +#define MAS_DEHR 11 +#define MAS_XM_USB_HS1 12 +#define PCNOC_M_0 13 +#define PCNOC_M_1 14 +#define PCNOC_INT_0 15 +#define PCNOC_INT_1 16 +#define PCNOC_INT_2 17 +#define PCNOC_S_1 18 +#define PCNOC_S_2 19 +#define PCNOC_S_3 20 +#define PCNOC_S_4 21 +#define PCNOC_S_8 22 +#define PCNOC_S_9 23 +#define SLV_TCSR 24 +#define SLV_TLMM 25 +#define SLV_CRYPTO_0_CFG 26 +#define SLV_MESSAGE_RAM 27 +#define SLV_PDM 28 +#define SLV_PRNG 29 +#define SLV_PMIC_ARB 30 +#define SLV_SNOC_CFG 31 +#define SLV_DCC_CFG 32 +#define SLV_CAMERA_SS_CFG 33 +#define SLV_DISP_SS_CFG 34 +#define SLV_VENUS_CFG 35 +#define SLV_SDCC_1 36 +#define SLV_BLSP_1 37 +#define SLV_USB_HS 38 +#define SLV_SDCC_3 39 +#define SLV_SDCC_2 40 +#define SLV_GPU_CFG 41 +#define SLV_USB_HS2 42 +#define SLV_BLSP_2 43 +#define SLV_PCNOC_SNOC 44 + +/* SNOC fabric */ +#define MAS_QDSS_BAM 0 +#define MAS_BIMC_SNOC 1 +#define MAS_PCNOC_SNOC 2 +#define MAS_QDSS_ETR 3 +#define MAS_LPASS_PROC 4 +#define MAS_IPA 5 +#define QDSS_INT 6 +#define SNOC_INT_0 7 +#define SNOC_INT_1 8 +#define SNOC_INT_2 9 +#define SLV_KPSS_AHB 10 +#define SLV_SNOC_BIMC 11 +#define SLV_IMEM 12 +#define SLV_SNOC_PCNOC 13 +#define SLV_QDSS_STM 14 +#define SLV_CATS_0 15 +#define SLV_CATS_1 16 +#define SLV_LPASS 17 + +/* SNOC-MM fabric */ +#define MAS_JPEG 0 +#define MAS_OXILI 1 +#define MAS_MDP0 2 +#define MAS_MDP1 3 +#define MAS_VENUS_0 4 +#define MAS_VENUS_1 5 +#define MAS_VFE_0 6 +#define MAS_VFE_1 7 +#define MAS_CPP 8 +#define MM_INT_0 9 +#define SLV_SMMNOC_BIMC 10 + +#endif /* __DT_BINDINGS_INTERCONNECT_QCOM_MSM8976_H */ diff --git a/include/dt-bindings/interconnect/qcom,sm8350.h b/include/dt-bindings/interconnect/qcom,sm8350.h index c7f7ed315aeb..2282f93607bc 100644 --- a/include/dt-bindings/interconnect/qcom,sm8350.h +++ b/include/dt-bindings/interconnect/qcom,sm8350.h @@ -119,9 +119,6 @@ #define SLAVE_SERVICE_GEM_NOC_1 16 #define SLAVE_SERVICE_GEM_NOC_2 17 #define SLAVE_SERVICE_GEM_NOC 18 -#define MASTER_MNOC_HF_MEM_NOC_DISP 19 -#define MASTER_MNOC_SF_MEM_NOC_DISP 20 -#define SLAVE_LLCC_DISP 21 #define MASTER_CNOC_LPASS_AG_NOC 0 #define SLAVE_LPASS_CORE_CFG 1 @@ -133,8 +130,6 @@ #define MASTER_LLCC 0 #define SLAVE_EBI1 1 -#define MASTER_LLCC_DISP 2 -#define SLAVE_EBI1_DISP 3 #define MASTER_CAMNOC_HF 0 #define MASTER_CAMNOC_ICP 1 @@ -149,11 +144,6 @@ #define SLAVE_MNOC_HF_MEM_NOC 10 #define SLAVE_MNOC_SF_MEM_NOC 11 #define SLAVE_SERVICE_MNOC 12 -#define MASTER_MDP0_DISP 13 -#define MASTER_MDP1_DISP 14 -#define MASTER_ROTATOR_DISP 15 -#define SLAVE_MNOC_HF_MEM_NOC_DISP 16 -#define SLAVE_MNOC_SF_MEM_NOC_DISP 17 #define MASTER_CDSP_NOC_CFG 0 #define MASTER_CDSP_PROC 1 diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 51ac441a37c3..89b0ac0014b0 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -49,12 +49,21 @@ * Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload. * Used to associate a CPU with the CoreSight Trace ID. * [07:00] - Trace ID - uses 8 bits to make value easy to read in file. - * [59:08] - Unused (SBZ) - * [63:60] - Version + * [39:08] - Sink ID - as reported in /sys/bus/event_source/devices/cs_etm/sinks/ + * Added in minor version 1. + * [55:40] - Unused (SBZ) + * [59:56] - Minor Version - previously existing fields are compatible with + * all minor versions. + * [63:60] - Major Version - previously existing fields mean different things + * in new major versions. */ #define CS_AUX_HW_ID_TRACE_ID_MASK GENMASK_ULL(7, 0) -#define CS_AUX_HW_ID_VERSION_MASK GENMASK_ULL(63, 60) +#define CS_AUX_HW_ID_SINK_ID_MASK GENMASK_ULL(39, 8) -#define CS_AUX_HW_ID_CURR_VERSION 0 +#define CS_AUX_HW_ID_MINOR_VERSION_MASK GENMASK_ULL(59, 56) +#define CS_AUX_HW_ID_MAJOR_VERSION_MASK GENMASK_ULL(63, 60) + +#define CS_AUX_HW_ID_MAJOR_VERSION 0 +#define CS_AUX_HW_ID_MINOR_VERSION 1 #endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index f09ace92176e..c13342594278 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -218,6 +218,24 @@ struct coresight_sysfs_link { const char *target_name; }; +/* architecturally we have 128 IDs some of which are reserved */ +#define CORESIGHT_TRACE_IDS_MAX 128 + +/** + * Trace ID map. + * + * @used_ids: Bitmap to register available (bit = 0) and in use (bit = 1) IDs. + * Initialised so that the reserved IDs are permanently marked as + * in use. + * @perf_cs_etm_session_active: Number of Perf sessions using this ID map. + */ +struct coresight_trace_id_map { + DECLARE_BITMAP(used_ids, CORESIGHT_TRACE_IDS_MAX); + atomic_t __percpu *cpu_map; + atomic_t perf_cs_etm_session_active; + spinlock_t lock; +}; + /** * struct coresight_device - representation of a device as used by the framework * @pdata: Platform data with device connections associated to this device. @@ -271,6 +289,7 @@ struct coresight_device { bool sysfs_sink_activated; struct dev_ext_attribute *ea; struct coresight_device *def_sink; + struct coresight_trace_id_map perf_sink_id_map; /* sysfs links between components */ int nr_links; bool has_conns_grp; @@ -365,7 +384,7 @@ struct coresight_ops_link { struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev, struct perf_event *event, - enum cs_mode mode); + enum cs_mode mode, struct coresight_trace_id_map *id_map); void (*disable)(struct coresight_device *csdev, struct perf_event *event); }; diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 8099759d7242..37d56914d485 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -3,6 +3,7 @@ #define _IIO_BACKEND_H_ #include <linux/types.h> +#include <linux/iio/iio.h> struct iio_chan_spec; struct fwnode_handle; @@ -17,11 +18,13 @@ enum iio_backend_data_type { }; enum iio_backend_data_source { - IIO_BACKEND_INTERNAL_CONTINUOS_WAVE, + IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE, IIO_BACKEND_EXTERNAL, IIO_BACKEND_DATA_SOURCE_MAX }; +#define iio_backend_debugfs_ptr(ptr) PTR_IF(IS_ENABLED(CONFIG_DEBUG_FS), ptr) + /** * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute * @_name: Attribute name @@ -54,6 +57,8 @@ enum iio_backend_test_pattern { IIO_BACKEND_NO_TEST_PATTERN, /* modified prbs9 */ IIO_BACKEND_ADI_PRBS_9A = 32, + /* modified prbs23 */ + IIO_BACKEND_ADI_PRBS_23A, IIO_BACKEND_TEST_PATTERN_MAX }; @@ -81,6 +86,9 @@ enum iio_backend_sample_trigger { * @extend_chan_spec: Extend an IIO channel. * @ext_info_set: Extended info setter. * @ext_info_get: Extended info getter. + * @read_raw: Read a channel attribute from a backend device + * @debugfs_print_chan_status: Print channel status into a buffer. + * @debugfs_reg_access: Read or write register value of backend. **/ struct iio_backend_ops { int (*enable)(struct iio_backend *back); @@ -113,11 +121,31 @@ struct iio_backend_ops { const char *buf, size_t len); int (*ext_info_get)(struct iio_backend *back, uintptr_t private, const struct iio_chan_spec *chan, char *buf); + int (*read_raw)(struct iio_backend *back, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask); + int (*debugfs_print_chan_status)(struct iio_backend *back, + unsigned int chan, char *buf, + size_t len); + int (*debugfs_reg_access)(struct iio_backend *back, unsigned int reg, + unsigned int writeval, unsigned int *readval); +}; + +/** + * struct iio_backend_info - info structure for an iio_backend + * @name: Backend name. + * @ops: Backend operations. + */ +struct iio_backend_info { + const char *name; + const struct iio_backend_ops *ops; }; int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan); int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan); int devm_iio_backend_enable(struct device *dev, struct iio_backend *back); +int iio_backend_enable(struct iio_backend *back); +void iio_backend_disable(struct iio_backend *back); int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, const struct iio_backend_data_fmt *data); int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan, @@ -141,17 +169,41 @@ ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, const char *buf, size_t len); ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf); - -int iio_backend_extend_chan_spec(struct iio_dev *indio_dev, - struct iio_backend *back, +int iio_backend_read_raw(struct iio_backend *back, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask); +int iio_backend_extend_chan_spec(struct iio_backend *back, struct iio_chan_spec *chan); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); +struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, + const char *name, + struct fwnode_handle *fwnode); struct iio_backend * __devm_iio_backend_get_from_fwnode_lookup(struct device *dev, struct fwnode_handle *fwnode); int devm_iio_backend_register(struct device *dev, - const struct iio_backend_ops *ops, void *priv); + const struct iio_backend_info *info, void *priv); + +static inline int iio_backend_read_scale(struct iio_backend *back, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + return iio_backend_read_raw(back, chan, val, val2, IIO_CHAN_INFO_SCALE); +} + +static inline int iio_backend_read_offset(struct iio_backend *back, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + return iio_backend_read_raw(back, chan, val, val2, + IIO_CHAN_INFO_OFFSET); +} +ssize_t iio_backend_debugfs_print_chan_status(struct iio_backend *back, + unsigned int chan, char *buf, + size_t len); +void iio_backend_debugfs_add(struct iio_backend *back, + struct iio_dev *indio_dev); #endif diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 894309294182..18779b631e90 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -609,7 +609,7 @@ struct iio_dev { int scan_bytes; const unsigned long *available_scan_masks; - unsigned masklength; + unsigned __private masklength; const unsigned long *active_scan_mask; bool scan_timestamp; struct iio_trigger *trig; @@ -810,6 +810,23 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) } #endif +/** + * iio_device_suspend_triggering() - suspend trigger attached to an iio_dev + * @indio_dev: iio_dev associated with the device that will have triggers suspended + * + * Return 0 if successful, negative otherwise + **/ +int iio_device_suspend_triggering(struct iio_dev *indio_dev); + +/** + * iio_device_resume_triggering() - resume trigger attached to an iio_dev + * that was previously suspended with iio_device_suspend_triggering() + * @indio_dev: iio_dev associated with the device that will have triggers resumed + * + * Return 0 if successful, negative otherwise + **/ +int iio_device_resume_triggering(struct iio_dev *indio_dev); + #ifdef CONFIG_ACPI bool iio_read_acpi_mount_matrix(struct device *dev, struct iio_mount_matrix *orientation, @@ -855,6 +872,26 @@ static inline const struct iio_scan_type return &chan->scan_type; } +/** + * iio_get_masklength - Get length of the channels mask + * @indio_dev: the IIO device to get the masklength for + */ +static inline unsigned int iio_get_masklength(const struct iio_dev *indio_dev) +{ + return ACCESS_PRIVATE(indio_dev, masklength); +} + +int iio_active_scan_mask_index(struct iio_dev *indio_dev); + +/** + * iio_for_each_active_channel - Iterated over active channels + * @indio_dev: the IIO device + * @chan: Holds the index of the enabled channel + */ +#define iio_for_each_active_channel(indio_dev, chan) \ + for_each_set_bit((chan), (indio_dev)->active_scan_mask, \ + iio_get_masklength(indio_dev)) + ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, diff --git a/include/linux/platform_data/ad5449.h b/include/linux/platform_data/ad5449.h deleted file mode 100644 index d687ef5726c2..000000000000 --- a/include/linux/platform_data/ad5449.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog - * Converter driver. - * - * Copyright 2012 Analog Devices Inc. - * Author: Lars-Peter Clausen <lars@metafoo.de> - */ - -#ifndef __LINUX_PLATFORM_DATA_AD5449_H__ -#define __LINUX_PLATFORM_DATA_AD5449_H__ - -/** - * enum ad5449_sdo_mode - AD5449 SDO pin configuration - * @AD5449_SDO_DRIVE_FULL: Drive the SDO pin with full strength. - * @AD5449_SDO_DRIVE_WEAK: Drive the SDO pin with not full strength. - * @AD5449_SDO_OPEN_DRAIN: Operate the SDO pin in open-drain mode. - * @AD5449_SDO_DISABLED: Disable the SDO pin, in this mode it is not possible to - * read back from the device. - */ -enum ad5449_sdo_mode { - AD5449_SDO_DRIVE_FULL = 0x0, - AD5449_SDO_DRIVE_WEAK = 0x1, - AD5449_SDO_OPEN_DRAIN = 0x2, - AD5449_SDO_DISABLED = 0x3, -}; - -/** - * struct ad5449_platform_data - Platform data for the ad5449 DAC driver - * @sdo_mode: SDO pin mode - * @hardware_clear_to_midscale: Whether asserting the hardware CLR pin sets the - * outputs to midscale (true) or to zero scale(false). - */ -struct ad5449_platform_data { - enum ad5449_sdo_mode sdo_mode; - bool hardware_clear_to_midscale; -}; - -#endif diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index d44a8118b2ed..1fd92021a573 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -236,6 +236,12 @@ struct binder_frozen_status_info { __u32 async_recv; }; +struct binder_frozen_state_info { + binder_uintptr_t cookie; + __u32 is_frozen; + __u32 reserved; +}; + /* struct binder_extened_error - extended error information * @id: identifier for the failed operation * @command: command as defined by binder_driver_return_protocol @@ -467,6 +473,17 @@ enum binder_driver_return_protocol { /* * The target of the last async transaction is frozen. No parameters. */ + + BR_FROZEN_BINDER = _IOR('r', 21, struct binder_frozen_state_info), + /* + * The cookie and a boolean (is_frozen) that indicates whether the process + * transitioned into a frozen or an unfrozen state. + */ + + BR_CLEAR_FREEZE_NOTIFICATION_DONE = _IOR('r', 22, binder_uintptr_t), + /* + * void *: cookie + */ }; enum binder_driver_command_protocol { @@ -550,6 +567,25 @@ enum binder_driver_command_protocol { /* * binder_transaction_data_sg: the sent command. */ + + BC_REQUEST_FREEZE_NOTIFICATION = + _IOW('c', 19, struct binder_handle_cookie), + /* + * int: handle + * void *: cookie + */ + + BC_CLEAR_FREEZE_NOTIFICATION = _IOW('c', 20, + struct binder_handle_cookie), + /* + * int: handle + * void *: cookie + */ + + BC_FREEZE_NOTIFICATION_DONE = _IOW('c', 21, binder_uintptr_t), + /* + * void *: cookie + */ }; #endif /* _UAPI_LINUX_BINDER_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 518200813d4e..9c7b404defbd 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -153,6 +153,10 @@ int main(void) DEVID_FIELD(i3c_device_id, part_id); DEVID_FIELD(i3c_device_id, extra_info); + DEVID(slim_device_id); + DEVID_FIELD(slim_device_id, manf_id); + DEVID_FIELD(slim_device_id, prod_code); + DEVID(spi_device_id); DEVID_FIELD(spi_device_id, name); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5d1c61fa5a55..99dce93a4188 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -960,6 +960,16 @@ static int do_i3c_entry(const char *filename, void *symval, return 1; } +static int do_slim_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, slim_device_id, manf_id); + DEF_FIELD(symval, slim_device_id, prod_code); + + sprintf(alias, "slim:%x:%x:*", manf_id, prod_code); + + return 1; +} + /* Looks like: spi:S */ static int do_spi_entry(const char *filename, void *symval, char *alias) @@ -1555,6 +1565,7 @@ static const struct devtable devtable[] = { {"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry}, {"i2c", SIZE_i2c_device_id, do_i2c_entry}, {"i3c", SIZE_i3c_device_id, do_i3c_entry}, + {"slim", SIZE_slim_device_id, do_slim_entry}, {"spi", SIZE_spi_device_id, do_spi_entry}, {"dmi", SIZE_dmi_system_id, do_dmi_entry}, {"platform", SIZE_platform_device_id, do_platform_entry}, diff --git a/tools/iio/Makefile b/tools/iio/Makefile index fa720f062229..3bcce0b7d10f 100644 --- a/tools/iio/Makefile +++ b/tools/iio/Makefile @@ -58,7 +58,7 @@ $(OUTPUT)iio_generic_buffer: $(IIO_GENERIC_BUFFER_IN) clean: rm -f $(ALL_PROGRAMS) rm -rf $(OUTPUT)include/linux/iio - find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.cmd' -delete install: $(ALL_PROGRAMS) install -d -m 755 $(DESTDIR)$(bindir); \ diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 0d0a7a19d6f9..9ef5ee087eda 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -498,6 +498,10 @@ int main(int argc, char **argv) return -ENOMEM; } trigger_name = malloc(IIO_MAX_NAME_LENGTH); + if (!trigger_name) { + ret = -ENOMEM; + goto error; + } ret = read_sysfs_string("name", trig_dev_name, trigger_name); free(trig_dev_name); if (ret < 0) { diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c index 5f362c0fd890..319567f0fae1 100644 --- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c @@ -65,6 +65,7 @@ static int __do_binderfs_test(struct __test_metadata *_metadata) static const char * const binder_features[] = { "oneway_spam_detection", "extended_error", + "freeze_notification", }; change_mountns(_metadata); |