diff --git a/.mailmap b/.mailmap index 968b8f508f56..8fb9f3898d7a 100644 --- a/.mailmap +++ b/.mailmap @@ -611,6 +611,7 @@ Mauro Carvalho Chehab Maxime Ripard Maxime Ripard Maxime Ripard +Maxwell Doose Maya Erez Mayuresh Janorkar Md Sadre Alam @@ -899,6 +900,7 @@ Tobias Klauser Tobias Klauser Tobias Klauser Todor Tomov +Tomasz Duszynski Tomasz Jeznach Tony Luck Trilok Soni diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index d8d6d85235b0..a4f5595722ad 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -162,10 +162,11 @@ Contact: linux-iio@vger.kernel.org Description: Raw (unscaled) differential voltage measurement equivalent to channel Y - channel Z where these channel numbers apply to the - physically equivalent inputs when non differential readings are - separately available. In differential only parts, then all that - is required is a consistent labeling. Units after application - of scale and offset are millivolts. + physically equivalent inputs when non-differential readings are + separately available. Even though differential-only parts had + been merely required to have consistent channel labeling, this + ABI is preferred over the non-differential one for those chips. + Units after application of scale and offset are millivolts. What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw KernelVersion: 6.18 @@ -507,6 +508,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -770,6 +773,7 @@ Description: 3.85kohm_to_gnd: connected to ground via a 3.85kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor, + 10kohm_to_gnd: connected to ground via a 10kOhm resistor, 16kohm_to_gnd: connected to ground via a 16kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, 32kohm_to_gnd: connected to ground via a 32kOhm resistor, @@ -917,7 +921,7 @@ Description: Note the driver will assume the last p events requested are to be enabled where p is how many it supports (which may vary - depending on the exact set requested. So if you want to be + depending on the exact set requested). So if you want to be sure you have set what you think you have, check the contents of these attributes after everything is configured. Drivers may have to buffer any parameters so that they are consistent when @@ -973,7 +977,7 @@ Description: Note the driver will assume the last p events requested are to be enabled where p is however many it supports (which may - vary depending on the exact set requested. So if you want to be + vary depending on the exact set requested). So if you want to be sure you have set what you think you have, check the contents of these attributes after everything is configured. Drivers may have to buffer any parameters so that they are consistent when @@ -1528,9 +1532,9 @@ Description: the unused bits, so to get a clean value the bits value must be used to mask the buffer output value appropriately. The storagebits value also specifies the data alignment. So s48/64>>2 will be a - signed 48 bit integer stored in a 64 bit location aligned to a 64 - bit boundary. To obtain the clean value, shift right 2 and apply a - mask to zero the top 16 bits of the result. + signed 48-bit integer stored in a 64-bit location aligned to a + 64-bit boundary. To obtain the clean value, shift right 2 and apply + a mask to zero the top 16 bits of the result. For other storage combinations this attribute will be extended appropriately. @@ -2496,3 +2500,12 @@ Description: seconds, expressed as: - a range specified as "[min step max]" + +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_raw +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) volumetric flow rate reading from the channel. + To convert to standard units (cubic metres per second, m^3/s) + apply the channel's _scale (and _offset, when present). diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 index 28be1cabf112..d44eb5f8728b 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 @@ -16,7 +16,7 @@ Description: In bridge applications, such as strain gauges and load cells, the bridge itself consumes the majority of the current in the system. To minimize the current consumption of the system, - the bridge can be disconnected (when it is not being used + the bridge can be disconnected (when it is not being used) using the bridge_switch_en attribute. What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw diff --git a/Documentation/ABI/testing/sysfs-bus-iio-flow b/Documentation/ABI/testing/sysfs-bus-iio-flow new file mode 100644 index 000000000000..fece2ecfa994 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-flow @@ -0,0 +1,21 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + The calibration medium the flow sensor uses to convert its + raw reading into a volumetric flow rate. Liquid flow sensors + are factory-calibrated per medium, so the selected medium has + to match the fluid actually flowing through the sensor for the + reported flow rate to be correct. + + Reading returns the currently active medium; writing one of the + strings listed in in_volumeflow_medium_available selects it. + +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium_available +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium_available +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Space separated list of the calibration media supported by the + device, e.g. "water ipa". diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency b/Documentation/ABI/testing/sysfs-bus-iio-frequency new file mode 100644 index 000000000000..46075aee7804 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency @@ -0,0 +1,11 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Stores channel Y frequency resolution/channel spacing in Hz for PLL + devices. The given value directly influences the operating mode when + fractional-N synthesis is required, as it derives values for + configurable modulus parameters used in the calculation of the output + frequency. It is assumed that the algorithm used to compute the various + dividers is able to generate proper values for multiples of the channel + spacing. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 index 1254457a726e..76987a119feb 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 @@ -1,13 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution -KernelVersion: 3.4.0 -Contact: linux-iio@vger.kernel.org -Description: - Stores channel Y frequency resolution/channel spacing in Hz. - The value given directly influences the MODULUS used by - the fractional-N PLL. It is assumed that the algorithm - that is used to compute the various dividers, is able to - generate proper values for multiples of channel spacing. - What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org diff --git a/Documentation/ABI/testing/sysfs-class-issei b/Documentation/ABI/testing/sysfs-class-issei new file mode 100644 index 000000000000..73a01f4627cb --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-issei @@ -0,0 +1,73 @@ +What: /sys/class/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The issei/ class sub-directory belongs to issei device class + +What: /sys/class/issei/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN directory is created for + each probed issei device + +What: /sys/class/issei/issei/fw_ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Display the ISSE firmware version. + + The version of the ISSE firmware is in format: + .... + +What: /sys/class/issei/issei/fw_clients +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The fw_clients directory stores all firmware clients on the + probed issei device + +What: /sys/class/issei/issei/fw_clients/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN/fw_client/M directory is created for + each firmware client on the probed issei device where M is the + id of firmware client. + +What: /sys/class/issei/issei/fw_clients//id +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays id of the firmware client + + The id of firmware client is it's number in client enumeration order, + starting from 1. + +What: /sys/class/issei/issei/fw_clients//uuid +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays uuid of the firmware client + + The universally unique identifier of the firmware client + +What: /sys/class/issei/issei/fw_clients//mtu +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays maximum transmission unit of the firmware client + + The maximum transmission unit (in bytes) used by the firmware client. + +What: /sys/class/issei/issei/fw_clients//ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays version of the firmware client + + The version of the firmware client diff --git a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu index ee253b033280..b93341e4471d 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu +++ b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu @@ -172,3 +172,61 @@ Contact: Richard Gong Description: (RO) max retry parameter is stored in the firmware decision IO section, as a byte located at offset 0x18c. + +What: /sys/devices/platform/stratix10-rsu.0/size0 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Flash size in bytes for QSPI device 0 (32-bit hex, one + line). EIO if unavailable. + +What: /sys/devices/platform/stratix10-rsu.0/size1 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 1. + +What: /sys/devices/platform/stratix10-rsu.0/size2 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 2. + +What: /sys/devices/platform/stratix10-rsu.0/size3 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 3. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size0 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Erase size in bytes for QSPI device 0 (32-bit hex, one + line). EIO if unavailable. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size1 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 1. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size2 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 2. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size3 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 3. diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-port b/Documentation/ABI/testing/sysfs-platform-dfl-port index 65658267fcc0..40e555abca20 100644 --- a/Documentation/ABI/testing/sysfs-platform-dfl-port +++ b/Documentation/ABI/testing/sysfs-platform-dfl-port @@ -84,7 +84,7 @@ Description: Read-Write. Read this file to get errors detected on port and to clear errors. Write fails with -EINVAL if input parsing fails or input error code doesn't match. Write fails with -EBUSY or -ETIMEDOUT if error can't be cleared as hardware - in low power state (-EBUSY) or not respoding (-ETIMEDOUT). + in low power state (-EBUSY) or not responding (-ETIMEDOUT). What: /sys/bus/platform/devices/dfl-port.0/errors/first_error Date: August 2019 diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index b9bc02b5b07a..b673eaa5d072 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -73,6 +73,47 @@ properties: device design and can interact with other characteristics such as settling time. + reference-sources: + $ref: /schemas/types.yaml#/definitions/string-array + description: + Indicates the voltage reference source or sources for this channel. Some + ADCs usually allow choosing between internal reference sources or a pair + of external pins. + + input-chopping: + type: boolean + description: + If set, the positive and negative input channels are swapped on every + other conversion to reduce offset error. This generally comes at the + expense of added settling time between conversions, so should be omitted + on systems with high-frequency signals or high data rate requirements. + + excitation-channels: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Excitation current sources provide current to resistive temperature + devices (RTDs), thermistors, diodes and other resistive sensors that + require constant current biasing. + + This array describes the mux configuration of the excitation current + sources. The values correspond to pins similar to the diff-channels and + single-channel properties. + + excitation-current-nanoamp: + description: + Excitation current sources provide current to resistive temperature + devices (RTDs), thermistors, diodes and other resistive sensors that + require constant current biasing. + + This array describes the current configuration of the excitation current + sources. The index in the array corresponds to the same index in the + excitation-channels array. + + burn-out-current-nanoamp: + description: + Burn-out current sources provide current to the channel's input pins for + open-circuit or short-circuit detection. + anyOf: - oneOf: - required: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index 4a3f7d3e05c3..db8467b98e67 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -39,6 +39,7 @@ properties: - adi,ad4087 - adi,ad4088 - adi,ad4880 + - adi,ad4883 - adi,ad4884 reg: @@ -102,6 +103,7 @@ allOf: contains: enum: - adi,ad4880 + - adi,ad4883 - adi,ad4884 then: properties: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file mode 100644 index 000000000000..33de1dc4c256 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2378 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt + +description: | + Analog Devices LTC2378 series of ADCs. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + oneOf: + # Single compatible string match. + - enum: + - adi,ltc2338-18 + - adi,ltc2364-16 + - adi,ltc2364-18 + - adi,ltc2376-16 + - adi,ltc2376-18 + - adi,ltc2376-20 + + # Low sample rate fallback for 16-bit unipolar sensors. + - items: + - enum: + - adi,ltc2370-16 # 2 MSPS + - adi,ltc2368-16 # 1 MSPS + - adi,ltc2367-16 # 500 kSPS + - const: adi,ltc2364-16 # fallback (250 kSPS) + + # Low sample rate fallback for 18-bit unipolar sensors. + - items: + - enum: + - adi,ltc2369-18 # 1.6 MSPS + - adi,ltc2368-18 # 1 MSPS + - adi,ltc2367-18 # 500 kSPS + - const: adi,ltc2364-18 # fallback (250 kSPS) + + # Low sample rate fallback for 16-bit bipolar sensors. + - items: + - enum: + - adi,ltc2380-16 # 2 MSPS + - adi,ltc2378-16 # 1 MSPS + - adi,ltc2377-16 # 500 kSPS + - const: adi,ltc2376-16 # fallback (250 kSPS) + + # Low sample rate fallback for 18-bit bipolar sensors. + - items: + - enum: + - adi,ltc2379-18 # 1.6 MSPS + - adi,ltc2378-18 # 1 MSPS + - adi,ltc2377-18 # 500 kSPS + - const: adi,ltc2376-18 # fallback (250 kSPS) + + # Low sample rate fallback for 20-bit bipolar sensors. + - items: + - enum: + - adi,ltc2378-20 # 1 MSPS + - adi,ltc2377-20 # 500 kSPS + - const: adi,ltc2376-20 # fallback (250 kSPS) + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 100000000 + + vdd-supply: + description: A 2.5V supply that powers the chip (VDD). + + ovdd-supply: + description: + A 1.71V to 5.25V supply that sets the logic level for digital interface. + + ref-supply: + description: + Voltage reference input that determines the scale of ADC conversions. + + refin-supply: + description: + Alternative voltage reference input. + + cnv-gpios: + description: + When provided, this property indicates the GPIO that is connected to the + CNV pin. + maxItems: 1 + + pwms: + description: PWM signal connected to the CNV pin. + maxItems: 1 + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The active + low signal provided on the BUSY pin asserts when ADC conversions finish. + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + - ovdd-supply + +allOf: + # Except for LTC2338, all designs require a voltage reference input + - if: + properties: + compatible: + not: + contains: + enum: + - adi,ltc2338-18 + then: + required: + - ref-supply + properties: + refin-supply: false + else: + properties: + ref-supply: false + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "adi,ltc2378-20", "adi,ltc2376-20"; + reg = <0>; + spi-max-frequency = <71000000>; + vdd-supply = <&supply_2_5V>; + ovdd-supply = <&supply_3_3V>; + ref-supply = <&supply_5V>; + cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml index 1ea60dff98d5..099235b5fd54 100644 --- a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml @@ -33,6 +33,26 @@ properties: description: Analog supply voltage (AVDD). + vsup-supply: + description: + Supply voltage for the on-chip regulator (VSUP). + + dvdd-supply: + description: + Digital supply voltage (DVDD). On the HX711, DVDD is a + board-supplied digital rail; it was missing from the original + binding. On the HX710B, DVDD must be greater than or equal to + AVDD; when DVDD is a battery rail and AVDD is a regulated supply, + one channel monitors the DVDD-AVDD difference for battery level + detection. + + rate-gpios: + description: + GPIO connected to the RATE pin. When driven low the output data + rate is 10 SPS; when driven high it is 80 SPS. If omitted the + RATE pin state is determined by the board wiring. + maxItems: 1 + clock-frequency: description: Controls the SCK bit-bang timing. The value is used to derive the @@ -57,6 +77,7 @@ examples: compatible = "avia,hx711"; sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>; dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + rate-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; avdd-supply = <&avdd>; clock-frequency = <100000>; }; diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml index 9936aa605c7b..c2f7387e4bfc 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/adc/mediatek,mt6359-auxadc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: MediaTek MT6350 series PMIC AUXADC +title: MediaTek MT6350 series and similar PMIC AUXADC maintainers: - AngeloGioacchino Del Regno @@ -20,6 +20,7 @@ properties: compatible: oneOf: - enum: + - mediatek,mt6323-auxadc - mediatek,mt6357-auxadc - mediatek,mt6358-auxadc - mediatek,mt6359-auxadc diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml index 6769d679c907..f0551d665916 100644 --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml @@ -22,7 +22,9 @@ properties: - const: rockchip,rk3562-saradc - const: rockchip,rk3588-saradc - items: - - const: rockchip,rk3576-saradc + - enum: + - rockchip,rk3576-saradc + - rockchip,rv1106-saradc - const: rockchip,rk3588-saradc - items: - enum: diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml new file mode 100644 index 000000000000..facfeee61330 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml @@ -0,0 +1,219 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads112c14.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments' ADS112C14 and similar ADC chips + +description: | + Supports the following Texas Instruments' ADC chips: + - ADS112C14 (16-bit) + - ADS122C14 (24-bit) + + https://www.ti.com/lit/ds/symlink/ads122c14.pdf + + These chips are primarily designed for use with resistive sensors such as + RTDs, thermocouples, Wheatstone bridges, etc. The channel bindings reflect + this in that each channel represents the conditions required to make a + measurement rather than strictly just the physical input channels. + +maintainers: + - David Lechner + +properties: + compatible: + enum: + - ti,ads112c14 + - ti,ads122c14 + + reg: + items: + - minimum: 0x40 + maximum: 0x47 + + clocks: + maxItems: 1 + description: Optional external clock connected to GPIO3 pin. + + avdd-supply: true + dvdd-supply: true + + refp-supply: true + refn-supply: true + + ti,refp-refn-resistor-ohms: + description: + The resistance of the external resistor between REFP and REFN when using + resistor bridge driven by current outputs for RTD measurements. + + interrupts: + minItems: 1 + items: + - description: /FAULT interrupt (GPIO2 pin) + - description: /DRDY interrupt (GPIO3 pin) + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: [fault, drdy] + + gpio-controller: true + '#gpio-cells': + const: 2 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + ^channel@[0-9a-f]$: + $ref: adc.yaml + + unevaluatedProperties: false + + properties: + reg: + items: + - maximum: 15 # arbitrary limit, channel@ can be any combination of AIN0-AIN7 + + single-channel: + maximum: 7 + + diff-channels: + items: + maximum: 7 + + bipolar: true + + input-chopping: true + + excitation-channels: + maxItems: 2 + items: + maximum: 7 + + excitation-current-nanoamp: + maxItems: 2 + items: + enum: [1000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, + 90000, 100000, 200000, 300000, 400000, 500000, 600000, 700000, + 800000, 900000, 1000000] + + burn-out-current-nanoamp: + items: + - enum: [200, 1000, 10000] + + reference-sources: + items: + - enum: [internal-2.5v, internal-1.25v, external, avdd] + default: internal-2.5v + + dependencies: + excitation-channels: [ excitation-current-nanoamp ] + excitation-current-nanoamp: [ excitation-channels ] + + oneOf: + - required: [ single-channel ] + - required: [ diff-channels ] + +unevaluatedProperties: false + +required: + - compatible + - reg + - avdd-supply + - dvdd-supply + +dependencies: + refn-supply: [ refp-supply ] + +oneOf: + - required: [ refp-supply ] + - required: [ "ti,refp-refn-resistor-ohms" ] + - properties: + refp-supply: false + refn-supply: false + ti,refp-refn-resistor-ohms: false + +# /DRDY out and CLK in use the same AIN7/GPIO3 pin. +if: + required: + - interrupt-names + properties: + interrupt-names: + contains: + const: drdy +then: + properties: + clocks: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@40 { + compatible = "ti,ads112c14"; + reg = <0x40>; + + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + + /* 3-Wire RTD: Two IDACs, One Measurement (AIN1-AIN2) */ + + ti,refp-refn-resistor-ohms = <500>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + diff-channels = <1>, <2>; + input-chopping; + excitation-channels = <0>, <3>; + excitation-current-nanoamp = <500000>, <500000>; + reference-sources = "external"; + label = "rtd"; + }; + }; + }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@40 { + compatible = "ti,ads112c14"; + reg = <0x40>; + + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + + /* Resistive Bridge Measurement With a Thermistor for Temperature Compensation */ + + refp-supply = <&avdd>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + diff-channels = <6>, <7>; + bipolar; + reference-sources = "external"; + label = "bridge"; + }; + + channel@1 { + reg = <1>; + diff-channels = <1>, <2>; + reference-sources = "internal-2.5v"; + label = "thermistor"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml new file mode 100644 index 000000000000..1ad58e3d6163 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/xlnx,versal-sysmon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD/Xilinx Versal System Monitor + +maintainers: + - Salih Erim + +description: + The AMD/Xilinx Versal System Monitor (SysMon) is the successor to the + Zynq UltraScale+ AMS block. It provides on-chip voltage and temperature + monitoring with up to 160 voltage measurement points and up to + 64 temperature satellites distributed across the SoC. The hardware + supports configurable threshold alarms and oversampling. The device + can be accessed via memory-mapped I/O or via an I2C interface. + +properties: + compatible: + const: xlnx,versal-sysmon + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + '#io-channel-cells': + const: 1 + + voltage-channels: + type: object + description: + Container for voltage measurement channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^channel@([0-9a-f]|[1-9][0-9a-f])$': + $ref: adc.yaml + + description: + Measures a voltage rail. The register index and rail + name are assigned by the hardware design tool (Vivado). + + properties: + reg: + minimum: 0 + maximum: 159 + description: + Voltage measurement register index assigned by the hardware + design tool. + + required: + - reg + - label + + unevaluatedProperties: false + + required: + - '#address-cells' + - '#size-cells' + + additionalProperties: false + + temperature-channels: + type: object + description: + Container for temperature satellite measurement channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^channel@([1-9a-f]|[1-3][0-9a-f]|40)$': + $ref: adc.yaml + + description: + Reads a temperature satellite sensor. Each satellite monitors + a specific region of the SoC die. + + properties: + reg: + minimum: 1 + maximum: 64 + description: + Temperature satellite number (1-based hardware index). + + required: + - reg + - label + + unevaluatedProperties: false + + required: + - '#address-cells' + - '#size-cells' + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + sysmon@f1270000 { + compatible = "xlnx,versal-sysmon"; + reg = <0xf1270000 0x4000>; + interrupts = ; + #io-channel-cells = <1>; + + voltage-channels { + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + label = "vccaux"; + }; + + channel@3 { + reg = <3>; + label = "vcc_ram"; + bipolar; + }; + }; + + temperature-channels { + #address-cells = <1>; + #size-cells = <0>; + + channel@a { + reg = <10>; + label = "aie-temp-ch1"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml index a5b0debe85b1..ea0e72f2a690 100644 --- a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml @@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Sensirion SCD30 carbon dioxide sensor maintainers: - - Maxwell Doose + - Maxwell Doose + - Tomasz Duszynski description: | Air quality sensor capable of measuring co2 concentration, temperature diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml index a355d52a9d64..2fe098619772 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml @@ -10,15 +10,17 @@ maintainers: - Kim Seer Paller description: | - The AD3530/AD3530R (8-channel) and AD3531/AD3531R (4-channel) are low-power, - 16-bit, buffered voltage output digital-to-analog converters (DACs) with - software-programmable gain controls, providing full-scale output spans of 2.5V - or 5V for reference voltages of 2.5V. These devices operate from a single 2.7V - to 5.5V supply and are guaranteed monotonic by design. The "R" variants - include a 2.5V, 5ppm/°C internal reference, which is disabled by default. + The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), and AD3532/AD3532R + (16-channel) are low-power, 16-bit, buffered voltage output digital-to-analog + converters (DACs) with software-programmable gain controls, providing + full-scale output spans of 2.5V or 5V for reference voltages of 2.5V. These + devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic + by design. The "R" variants include a 2.5V, 5ppm/°C internal reference, which + is disabled by default. Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3530_ad530r.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/ad3531-ad3531r.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad3532r.pdf properties: compatible: @@ -27,6 +29,8 @@ properties: - adi,ad3530r - adi,ad3531 - adi,ad3531r + - adi,ad3532 + - adi,ad3532r reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index 713f535bb33a..d1bb09054440 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/dac/adi,ad5686.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD5360 and similar SPI DACs +title: Analog Devices AD5686 and similar SPI DACs maintainers: - Michael Hennerich @@ -14,10 +14,14 @@ properties: compatible: enum: - adi,ad5310r + - adi,ad5313r + - adi,ad5317r - adi,ad5672r + - adi,ad5674 - adi,ad5674r - adi,ad5676 - adi,ad5676r + - adi,ad5679 - adi,ad5679r - adi,ad5681r - adi,ad5682r @@ -28,31 +32,107 @@ properties: - adi,ad5685r - adi,ad5686 - adi,ad5686r + - adi,ad5687 + - adi,ad5687r + - adi,ad5689 + - adi,ad5689r reg: maxItems: 1 + vdd-supply: + description: Input power supply. + + vlogic-supply: + description: + Digital power supply. On some tiny package variants for single-channel + devices, this supply is internally connected to vdd; in that case, specify + this property with the same regulator as vdd. + + vref-supply: + description: + Reference voltage supply. If not supplied the internal reference is used. + vcc-supply: - description: If not supplied the internal reference is used. + deprecated: true + description: Use vref-supply instead. + + reset-gpios: + description: Active-low RESET pin to reset the device. + maxItems: 1 + + ldac-gpios: + description: + Active-low LDAC pin used to asynchronously update the DAC channels. + maxItems: 1 + + gain-gpios: + description: + GAIN pin that sets a multiplier for the DAC output voltage. When high, + the DAC output voltage is multiplied by 2, otherwise it is unchanged. + maxItems: 1 + + adi,range-double: + description: + Sets the initial voltage output range from 0 to 2xVREF. On devices that + have a GAIN pin and no gain-gpios property is set, this indicates the pin + is hardwired high. + type: boolean required: - compatible - reg + - vdd-supply + - vlogic-supply allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - adi,ad5674 + - adi,ad5676 + - adi,ad5679 + - adi,ad5683 + - adi,ad5684 + - adi,ad5686 + - adi,ad5687 + - adi,ad5689 + then: + required: + - vref-supply + - if: + properties: + compatible: + contains: + enum: + - adi,ad5310r + - adi,ad5681r + - adi,ad5682r + - adi,ad5683 + - adi,ad5683r + then: + properties: + gain-gpios: false unevaluatedProperties: false examples: - | + #include spi { #address-cells = <1>; #size-cells = <0>; dac@0 { reg = <0>; compatible = "adi,ad5310r"; - vcc-supply = <&dac_vref0>; + vdd-supply = <&dac_vdd>; + vlogic-supply = <&dac_vlogic>; + vref-supply = <&dac_vref>; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml index b5a88b03dc2f..835fa21c474e 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml @@ -16,10 +16,14 @@ properties: compatible: enum: - adi,ad5311r + - adi,ad5316r - adi,ad5337r - adi,ad5338r - adi,ad5671r + - adi,ad5673r + - adi,ad5675 - adi,ad5675r + - adi,ad5677r - adi,ad5691r - adi,ad5692r - adi,ad5693 @@ -29,22 +33,88 @@ properties: - adi,ad5695r - adi,ad5696 - adi,ad5696r + - adi,ad5697r reg: maxItems: 1 + vdd-supply: + description: Input power supply. + + vlogic-supply: + description: + Digital power supply. On some tiny package variants for single-channel + devices, this supply is internally connected to vdd; in that case, specify + this property with the same regulator as vdd. + + vref-supply: + description: + Reference voltage supply. If not supplied the internal reference is used. + vcc-supply: - description: | - The regulator supply for DAC reference voltage. + deprecated: true + description: Use vref-supply instead. + + reset-gpios: + description: Active-low RESET pin to reset the device. + maxItems: 1 + + ldac-gpios: + description: + Active-low LDAC pin used to asynchronously update the DAC channels. + maxItems: 1 + + gain-gpios: + description: + GAIN pin that sets a multiplier for the DAC output voltage. When high, + the DAC output voltage is multiplied by 2, otherwise it is unchanged. + maxItems: 1 + + adi,range-double: + description: + Sets the initial voltage output range from 0 to 2xVREF. On devices that + have a GAIN pin and no gain-gpios property is set, this indicates the pin + is hardwired high. + type: boolean required: - compatible - reg + - vdd-supply + - vlogic-supply + +allOf: + - if: + properties: + compatible: + contains: + enum: + - adi,ad5675 + - adi,ad5693 + - adi,ad5694 + - adi,ad5696 + then: + required: + - vref-supply + - if: + properties: + compatible: + contains: + enum: + - adi,ad5311r + - adi,ad5691r + - adi,ad5692r + - adi,ad5693 + - adi,ad5693r + then: + properties: + gain-gpios: false additionalProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -52,7 +122,10 @@ examples: ad5696: dac@0 { compatible = "adi,ad5696"; reg = <0>; - vcc-supply = <&dac_vref>; + vdd-supply = <&dac_vdd>; + vlogic-supply = <&dac_vlogic>; + vref-supply = <&dac_vref>; + ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml index 2bd89e0aa46b..80fb98035c6f 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml @@ -36,11 +36,11 @@ properties: vrefp-supply: description: - Positive referance input voltage range. From 5v to (vdd - 2.5) + Positive reference input voltage range. From 5v to (vdd - 2.5) vrefn-supply: description: - Negative referance input voltage range. From (vss + 2.5) to 0. + Negative reference input voltage range. From (vss + 2.5) to 0. adi,rbuf-gain2-en: description: Specify to allow an external amplifier to be connected in a diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml new file mode 100644 index 000000000000..b181fe73dce8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.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/microchip,mcp47a1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP47A1 DAC + +maintainers: + - Joshua Crofts + +description: | + The Microchip MCP47A1 is a 6-bit single output volatile DAC. + This device can have different IDs (0x2e and 0x3e). + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf + +properties: + compatible: + const: microchip,mcp47a1 + + reg: + items: + - enum: [0x2e, 0x3e] + + vref-supply: true + + vdd-supply: true + +required: + - compatible + - reg + - vref-supply + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + dac@2e { + compatible = "microchip,mcp47a1"; + reg = <0x2e>; + vref-supply = <&vref_regulator>; + vdd-supply = <&vdd_regulator>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml new file mode 100644 index 000000000000..79718f4f98bd --- /dev/null +++ b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/flow/sensirion,slf3s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sensirion SLF3S liquid flow sensor + +maintainers: + - Wadim Mueller + +description: + Family of digital liquid-flow sensors from Sensirion with I2C interface. All + family members share the same register map; sub-types differ only in the flow + scale factor and the calibrated measurement range. The sub-type can be + identified from the product-information register. + +properties: + compatible: + enum: + - sensirion,slf3s-0600f + - sensirion,slf3s-1300f + - sensirion,slf3s-4000b + + reg: + maxItems: 1 + + vdd-supply: true + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + flow-sensor@8 { + compatible = "sensirion,slf3s-0600f"; + reg = <0x08>; + vdd-supply = <®_3v3>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_EDGE_RISING>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml new file mode 100644 index 000000000000..f4fae9210382 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADF41513 PLL Frequency Synthesizer + +maintainers: + - Rodrigo Alencar + +description: + The ADF41513 is an ultralow noise frequency synthesizer that can be used to + implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and + downconversion sections of wireless receivers and transmitters. The ADF41510 + supports frequencies up to 10 GHz. + + https://www.analog.com/en/products/adf41510.html + https://www.analog.com/en/products/adf41513.html + +properties: + compatible: + enum: + - adi,adf41510 + - adi,adf41513 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + + clocks: + maxItems: 1 + description: Clock that provides the reference input frequency. + + avdd1-supply: + description: PFD and Up and Down Digital Driver Power Supply (3.3 V) + + avdd2-supply: + description: RF Buffer and Prescaler Power Supply (3.3 V) + + avdd3-supply: + description: N Divider Power Supply (3.3 V) + + avdd4-supply: + description: R Divider and Lock Detector Power Supply (3.3 V) + + avdd5-supply: + description: Sigma-Delta Modulator and SPI Power Supply (3.3 V) + + vp-supply: + description: Charge Pump Power Supply (3.3 V) + + enable-gpios: + description: + GPIO that controls the chip enable pin. A logic low on this pin + powers down the device and puts the charge pump output into + three-state mode. + maxItems: 1 + + lock-detect-gpios: + description: + GPIO for lock detect functionality. When configured for digital lock + detect, this pin will output a logic high when the PLL is locked. + maxItems: 1 + + adi,power-up-frequency-mhz: + minimum: 1000 + maximum: 26500 + default: 10000 + description: + The PLL tunes to this frequency during the initialization sequence. + This property should be set to a frequency supported by the loop filter + and VCO used in the design. Range is 1 GHz to 26.5 GHz for ADF41513, + and 1 GHz to 10 GHz for ADF41510. + + adi,reference-div-factor: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + default: 1 + description: + Value for the reference division factor (R Counter). The driver will + increment R Counter as needed to achieve a PFD frequency within the + allowed range. High R counter values will reduce the PFD frequency, which + lowers the frequency resolution, and affects phase noise performance. + As it affects the PFD frequency, this value depends on the loop filter + design. + + adi,reference-doubler-enable: + description: + Enables the reference doubler when deriving the PFD frequency. + The maximum reference frequency when the doubler is enabled is 225 MHz. + As it affects the PFD frequency, this value depends on the loop filter + design. + type: boolean + + adi,reference-div2-enable: + description: + Enables the reference divide-by-2 function when deriving the PFD + frequency. As it affects the PFD frequency, this value depends on the + loop filter design. + type: boolean + + adi,charge-pump-resistor-ohms: + minimum: 1800 + maximum: 10000 + default: 2700 + description: + External charge pump resistor (R_SET) value in ohms. This sets the maximum + charge pump current along with the charge pump current setting. + + adi,charge-pump-current-microamp: + minimum: 81 + maximum: 7200 + description: + Charge pump current (I_CP) in microamps. The value will be rounded to the + nearest supported value. Range of acceptable values depends on the + charge pump resistor value, such that 810 mV <= I_CP * R_SET <= 12960 mV. + This value depends on the loop filter and the VCO design. + + adi,logic-level-1v8-enable: + description: + Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V. + type: boolean + + adi,phase-detector-polarity-positive-enable: + description: + Set phase detector polarity to positive. Default is negative. + Use positive polarity with non-inverting loop filter and VCO with + positive tuning slope, or with inverting loop filter and VCO with + negative tuning slope. + type: boolean + + adi,lock-detector-count: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 64 + description: + Sets the value for Lock Detector count of the PLL, which determines the + number of consecutive phase detector cycles that must be within the lock + detector window before lock is declared. Lower values increase the lock + detection sensitivity, while higher values provides a more stable lock + detection. Applications that consume the lock detect signal may require + different settings based on system requirements. + enum: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192] + + adi,phase-resync-period-ns: + default: 0 + description: + When this value is non-zero, enable phase resync functionality, which + produces a consistent output phase offset with respect to the input + reference. The value specifies the resync period in nanoseconds, used + to configure clock dividers with respect to the PFD frequency. This value + should be set to a value that is at least as long as the worst case lock + time, i.e., it depends mostly on the loop filter design. + + adi,le-sync-enable: + description: + Synchronizes Load Enable (LE) transitions with the reference signal to + avoid asynchronous glitches in the output. This is recommended when using + the PLL as a frequency synthesizer, where the reference signal will always + be present while the device is being configured. When using the PLL as a + frequency tracker, where the reference signal may be absent, LE sync + should be left disabled. + type: boolean + +dependencies: + adi,charge-pump-resistor-ohms: [ 'adi,charge-pump-current-microamp' ] + +required: + - compatible + - reg + - clocks + - avdd1-supply + - avdd2-supply + - avdd3-supply + - avdd4-supply + - avdd5-supply + - vp-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: adi,adf41510 + then: + properties: + adi,power-up-frequency-mhz: + maximum: 10000 + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + pll@0 { + compatible = "adi,adf41513"; + reg = <0>; + spi-max-frequency = <25000000>; + clocks = <&ref_clk>; + avdd1-supply = <&avdd1_3v3>; + avdd2-supply = <&avdd2_3v3>; + avdd3-supply = <&avdd3_3v3>; + avdd4-supply = <&avdd4_3v3>; + avdd5-supply = <&avdd5_3v3>; + vp-supply = <&vp_3v3>; + enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; + lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + + adi,power-up-frequency-mhz = <15500>; + adi,charge-pump-current-microamp = <3600>; + adi,charge-pump-resistor-ohms = <2700>; + adi,reference-doubler-enable; + adi,lock-detector-count = <64>; + adi,phase-resync-period-ns = <0>; + adi,phase-detector-polarity-positive-enable; + adi,le-sync-enable; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 119e28a833fd..81b6e85decd5 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -30,6 +30,8 @@ properties: - invensense,icm42600 - invensense,icm42602 - invensense,icm42605 + - invensense,icm42607 + - invensense,icm42607p - invensense,icm42622 - invensense,icm42631 - invensense,icm42686 @@ -53,6 +55,8 @@ properties: drive-open-drain: type: boolean + mount-matrix: true + vdd-supply: description: Regulator that provides power to the sensor @@ -65,10 +69,24 @@ properties: required: - compatible - reg - - interrupts allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - invensense,icm42600 + - invensense,icm42602 + - invensense,icm42605 + - invensense,icm42622 + - invensense,icm42631 + - invensense,icm42686 + - invensense,icm42688 + then: + required: + - interrupts unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml new file mode 100644 index 000000000000..38abd083a4fb --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883l.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QST QMC5883L 3-Axis Magnetic Sensor + +maintainers: + - Siratul Islam + +description: | + QST QMC5883L 3-Axis Magnetic Sensor on I2C bus. + https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf + +properties: + compatible: + enum: + - qstcorp,qmc5883l + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + + vddio-supply: true + +additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@d { + compatible = "qstcorp,qmc5883l"; + reg = <0x0d>; + vdd-supply = <&vdd_3v3_reg>; + vddio-supply = <&vdd_3v3_reg>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml new file mode 100644 index 000000000000..b7ab6decdfbc --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc6308.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QST QMC6308 3-Axis Magnetic Sensor + +maintainers: + - Jorijn van der Graaf + +description: + QST QMC6308 3-axis anisotropic magnetoresistive (AMR) magnetic sensor + on I2C bus, with a single supply and no interrupt pin. + https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf + +properties: + compatible: + const: qstcorp,qmc6308 + + reg: + maxItems: 1 + + vdd-supply: true + + mount-matrix: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@2c { + compatible = "qstcorp,qmc6308"; + reg = <0x2c>; + vdd-supply = <&vdd_1v8_reg>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml index f7f8be1e379d..d23dc59505cc 100644 --- a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -8,7 +8,7 @@ title: ST VL53L0X/VL53L1X ToF ranging sensor maintainers: - Song Qiang - - Siratul Islam + - Siratul Islam properties: compatible: diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index fbba5e934861..7fb3e6820791 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -7,19 +7,25 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI TMP117 - Digital temperature sensor with integrated NV memory description: | - TI TMP116/117 - Digital temperature sensor with integrated NV memory that - supports I2C interface. + TI TMP116/117/119 - Digital temperature sensor with integrated NV memory + that supports I2C interface. https://www.ti.com/lit/gpn/tmp116 https://www.ti.com/lit/gpn/tmp117 + https://www.ti.com/lit/gpn/tmp119 maintainers: - Puranjay Mohan properties: compatible: - enum: - - ti,tmp116 - - ti,tmp117 + oneOf: + - enum: + - ti,tmp116 + - ti,tmp117 + - items: + - enum: + - ti,tmp119 + - const: ti,tmp117 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml new file mode 100644 index 000000000000..3db8d8b23219 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,maili-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Maili SoC + +maintainers: + - Raviteja Laggyshetty + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,maili-rpmh.h + +properties: + compatible: + enum: + - qcom,maili-aggre-noc + - qcom,maili-clk-virt + - qcom,maili-cnoc-main + - qcom,maili-gem-noc + - qcom,maili-llclpi-noc + - qcom,maili-lpass-ag-noc + - qcom,maili-lpass-lpiaon-noc + - qcom,maili-lpass-lpicx-noc + - qcom,maili-mc-virt + - qcom,maili-mmss-noc + - qcom,maili-nsp-noc + - qcom,maili-pcie-anoc + - qcom,maili-stdst-cfg + - qcom,maili-stdst-main + - qcom,maili-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 3 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-clk-virt + - qcom,maili-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + - qcom,maili-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@31100000 { + compatible = "qcom,maili-gem-noc"; + reg = <0x31100000 0x160200>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre_noc: interconnect@f00000 { + compatible = "qcom,maili-aggre-noc"; + reg = <0x00f00000 0x56200>; + #interconnect-cells = <2>; + clocks = <&gcc_phy_axi_clk>, + <&gcc_prim_axi_clk>, + <&rpmhcc_ipa_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index 41b9f758bf8b..3b8ebe17a976 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -17,6 +17,8 @@ description: properties: compatible: oneOf: + - enum: + - qcom,shikra-epss-l3 - items: - enum: - qcom,sc7180-osm-l3 diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml index 2a5a7594bafd..cd327a3bf3b9 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml @@ -32,18 +32,115 @@ properties: - qcom,sc8280xp-nspb-noc - qcom,sc8280xp-system-noc + reg: + maxItems: 1 + + clocks: + minItems: 7 + maxItems: 8 + required: - compatible allOf: - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-clk-virt + - qcom,sc8280xp-mc-virt + then: + properties: + reg: false + clocks: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: aggre USB3 SEC AXI clock + - description: aggre USB3 MP AXI clock + - description: aggre USB4 AXI clock + - description: aggre USB4 1 AXI clock + - description: aggre USB NOC SOUTH AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre2-noc + then: + properties: + clocks: + items: + - description: aggre NOC PCIE0 tunnel AXI clock + - description: aggre NOC PCIE1 tunnel AXI clock + - description: aggre NOC PCIE 4 AXI clock + - description: aggre NOC PCIE SOUTH SF AXI clock + - description: aggre UFS CARD AXI clock + - description: DDRSS GPU AXI clock + - description: DDRSS PCIE SF TBU clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre1-noc + - qcom,sc8280xp-aggre2-noc + then: + required: + - clocks + else: + properties: + clocks: false unevaluatedProperties: false examples: - | - interconnect-0 { - compatible = "qcom,sc8280xp-aggre1-noc"; + #include + #include + + interconnect { + compatible = "qcom,sc8280xp-clk-virt"; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; }; + + interconnect@9100000 { + compatible = "qcom,sc8280xp-gem-noc"; + reg = <0x9100000 0xb8400>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + interconnect@16c0000 { + compatible = "qcom,sc8280xp-aggre1-noc"; + reg = <0x16c0000 0x3af80>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_MP_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_1_AXI_CLK>, + <&gcc GCC_AGGRE_USB_NOC_SOUTH_AXI_CLK>, + <&rpmhcc RPMH_IPA_CLK>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml index 0840b0ec6e27..d863cddb21ac 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml @@ -46,6 +46,10 @@ properties: reg: maxItems: 1 + clocks: + minItems: 1 + maxItems: 6 + required: - compatible @@ -65,6 +69,73 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-usb-north-anoc + then: + properties: + clocks: + items: + - description: aggre USB2 PRIM AXI clock + - description: aggre USB3 MP AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-usb-south-anoc + then: + properties: + clocks: + items: + - description: aggre USB3 PRIM AXI clock + - description: aggre USB3 SEC AXI clock + - description: aggre USB3 TERT AXI clock + - description: aggre USB4_0 AXI clock + - description: aggre USB4_1 AXI clock + - description: aggre USB4_2 AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-aggre2-noc + - qcom,x1e80100-clk-virt + - qcom,x1e80100-cnoc-cfg + - qcom,x1e80100-cnoc-main + - qcom,x1e80100-gem-noc + - qcom,x1e80100-lpass-ag-noc + - qcom,x1e80100-lpass-lpiaon-noc + - qcom,x1e80100-lpass-lpicx-noc + - qcom,x1e80100-mc-virt + - qcom,x1e80100-mmss-noc + - qcom,x1e80100-nsp-noc + - qcom,x1e80100-pcie-center-anoc + - qcom,x1e80100-pcie-north-anoc + - qcom,x1e80100-pcie-south-anoc + - qcom,x1e80100-system-noc + - qcom,x1e80100-usb-center-anoc + then: + properties: + clocks: false + unevaluatedProperties: false examples: @@ -80,4 +151,5 @@ examples: reg = <0x016e0000 0x14400>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc_aggre_ufs_phy_axi_clk>; }; diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index 2876fdd7c6e6..49d2a5e8ba39 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -26,6 +26,8 @@ properties: - enum: - qcom,glymur-fastrpc - qcom,hawi-fastrpc + - qcom,maili-fastrpc + - qcom,nord-fastrpc - const: qcom,kaanapali-fastrpc label: @@ -35,6 +37,8 @@ properties: - sdsp - cdsp - cdsp1 + - cdsp2 + - cdsp3 - gdsp0 - gdsp1 diff --git a/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml b/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml new file mode 100644 index 000000000000..c52f8d4bec39 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/airoha,smc-efuses.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha SMC eFuses + +description: | + Airoha new SoC AN7581 expose banks of eFuse accessible + via specific SMC commands. + + 2 different bank of eFuse or 64 cells of 32 bit are exposed + read-only used to give information on HW Revision, PHY Calibration, + Device Model, Private Key... + +maintainers: + - Christian Marangi + +properties: + compatible: + enum: + - airoha,an7581-efuses + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + '^efuse-bank@[0-1]$': + type: object + + allOf: + - $ref: nvmem.yaml# + + properties: + reg: + description: Identify the eFuse bank. + enum: [0, 1] + + required: + - reg + + unevaluatedProperties: false + +required: + - compatible + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + efuse { + compatible = "airoha,an7581-efuses"; + #address-cells = <1>; + #size-cells = <0>; + + efuse-bank@0 { + reg = <0>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml index f97c6beb4766..c03e96afe564 100644 --- a/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml +++ b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml @@ -25,6 +25,7 @@ properties: - const: microchip,lan9662-otpc - enum: - microchip,lan9662-otpc + - microchip,lan9691-otpc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 2ab047f2bb69..bc0a0898b9e4 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -19,14 +19,19 @@ properties: - enum: - qcom,apq8064-qfprom - qcom,apq8084-qfprom + - qcom,glymur-qfprom + - qcom,hawi-qfprom - qcom,ipq5018-qfprom + - qcom,ipq5210-qfprom - qcom,ipq5332-qfprom - qcom,ipq5424-qfprom - qcom,ipq6018-qfprom - qcom,ipq8064-qfprom - qcom,ipq8074-qfprom - qcom,ipq9574-qfprom + - qcom,ipq9650-qfprom - qcom,kaanapali-qfprom + - qcom,milos-qfprom - qcom,msm8226-qfprom - qcom,msm8916-qfprom - qcom,msm8917-qfprom @@ -48,6 +53,7 @@ properties: - qcom,sdm630-qfprom - qcom,sdm670-qfprom - qcom,sdm845-qfprom + - qcom,shikra-qfprom - qcom,sm6115-qfprom - qcom,sm6350-qfprom - qcom,sm6375-qfprom diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 6780b651acf8..7fc29a60465d 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1387,6 +1387,8 @@ patternProperties: description: Shenzhen QiShenglong Industrialist Co., Ltd. "^qnap,.*": description: QNAP Systems, Inc. + "^qstcorp,.*": + description: QST Corporation Limited "^quanta,.*": description: Quanta Computer Inc. "^radxa,.*": diff --git a/Documentation/devicetree/bindings/w1/omap-hdq.txt b/Documentation/devicetree/bindings/w1/omap-hdq.txt deleted file mode 100644 index 913c5f91a0f9..000000000000 --- a/Documentation/devicetree/bindings/w1/omap-hdq.txt +++ /dev/null @@ -1,22 +0,0 @@ -* OMAP HDQ One wire bus master controller - -Required properties: -- compatible : should be "ti,omap3-1w" or "ti,am4372-hdq" -- reg : Address and length of the register set for the device -- interrupts : interrupt line. -- ti,hwmods : "hdq1w" - -Optional properties: -- ti,mode: should be "hdq": HDQ mode "1w": one-wire mode. - If not specified HDQ mode is implied. - -Example: - -- From omap3.dtsi - hdqw1w: 1w@480b2000 { - compatible = "ti,omap3-1w"; - reg = <0x480b2000 0x1000>; - interrupts = <58>; - ti,hwmods = "hdq1w"; - ti,mode = "hdq"; - }; diff --git a/Documentation/devicetree/bindings/w1/ti,hdq.yaml b/Documentation/devicetree/bindings/w1/ti,hdq.yaml new file mode 100644 index 000000000000..fd402c0510db --- /dev/null +++ b/Documentation/devicetree/bindings/w1/ti,hdq.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/w1/ti,hdq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP HDQ One Wire Bus Master Controller + +maintainers: + - Eduard Bostina + +properties: + compatible: + enum: + - ti,omap3-1w + - ti,am4372-hdq + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: fck + + ti,hwmods: + description: Name of the hwmod associated to the hdq + $ref: /schemas/types.yaml#/definitions/string + const: hdq1w + + ti,mode: + description: | + Operation mode. "hdq" for HDQ mode, "1w" for One-Wire mode. + If not specified, HDQ mode is implied. + $ref: /schemas/types.yaml#/definitions/string + enum: + - hdq + - 1w + default: hdq + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + onewire@0 { + compatible = "ti,am4372-hdq"; + reg = <0x0 0x1000>; + interrupts = ; + clocks = <&func_12m_clk>; + clock-names = "fck"; + }; + - | + onewire@480b2000 { + compatible = "ti,omap3-1w"; + reg = <0x480b2000 0x1000>; + interrupts = <58>; + ti,hwmods = "hdq1w"; + ti,mode = "hdq"; + }; diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index eaf7161ff957..6601a258690f 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -105,6 +105,7 @@ Subsystem-specific APIs interconnect ipmb ipmi + issei/index libata mailbox md/index diff --git a/Documentation/driver-api/issei/index.rst b/Documentation/driver-api/issei/index.rst new file mode 100644 index 000000000000..604267463fd4 --- /dev/null +++ b/Documentation/driver-api/issei/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +========================================================= +The Intel Silicon Security Engine Interface (Intel SSEI) +========================================================= + +**Copyright** |copy| 2026 Intel Corporation + + +.. toctree:: + :caption: Table of Contents + :maxdepth: 3 + + issei diff --git a/Documentation/driver-api/issei/issei.rst b/Documentation/driver-api/issei/issei.rst new file mode 100644 index 000000000000..a5e99e92e095 --- /dev/null +++ b/Documentation/driver-api/issei/issei.rst @@ -0,0 +1,135 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Introduction +============ + +The Intel Silicon Security Engine (Intel SSE) is an isolated and +protected computing resource (Co-processor) residing inside +Intel client chipsets released in 2024 (Lunar Lake) or later. +The Intel SSE provide security support and platform boot orchestration. +The actual feature set depends on the Intel chipset SKU. + +The Intel Silicon Security Engine Interface (Intel SSEI) +is the interface between the Host and Intel SSE. +This interface is exposed to the host as one or more PCI devices. +The Intel SSEI Driver is in charge of the communication channel between +a host application and the Intel SSE features. + +Each Intel SSE feature, or Intel SSE Client is addressed by a unique UUID and +each client has its own protocol. The protocol is message-based with a +header and payload up to maximal number of bytes advertised by the client, +upon connection. + +Intel SSEI Driver +================= + +The driver exposes a character device with device nodes /dev/isseiX. + +An application maintains communication with an Intel SSE feature while +/dev/isseiX is open. The binding to a specific feature is performed by calling +:c:macro:`IOCTL_ISSEI_CONNECT_CLIENT`, which passes the desired UUID. +The number of instances of an Intel SSE feature that can be opened +at the same time is limited to single instance. + +The driver is transparent to data that are passed between firmware feature +and host application. + +Because some of the Intel SSE features can change the system +configuration, the driver by default allows only a privileged +user to access it. + +The connection termination is performed by calling +:c:macro:`IOCTL_ISSEI_DISCONNECT_CLIENT`. + +The session is terminated calling :c:expr:`close(fd)`. + +A code snippet for an application communicating with SPDM client: + +.. code-block:: C + + struct issei_connect_client_data data = {.in_client_uuid = + {0xe8, 0x51, 0x49, 0xdf, 0x94, 0x47, 0x4C, + 0x9A, 0x83, 0x67, 0xC4, 0xE3, 0x34, 0x64, 0xF1, 0xB4}}; + __u8 req_data[] = {0x10, 0x84, 0x00, 0x00}; /* SPDM Get Version */ + size_t req_data_len = sizeof(req_data); + __u8 res_data[256]; + size_t res_data_len = sizeof(res_data); + int fd = open("/dev/issei0", O_RDWR); + + ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &data); + + printf("Ver=%d, MaxLen=%u, Flags=0x%08X\n", + data.out_client_properties.protocol_version, + data.out_client_properties.max_msg_length, + data.out_client_properties.flags); + + [...] + + write(fd, req_data, req_data_len); + + [...] + + read(fd, res_data, res_data_len); + + printf("SPDM version count %u, version[0]=%02X%02X\n", + res_data[5], res_data[6], res_data[7]); + + [...] + + ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, &data); + + [...] + + close(fd); + + +User space API ioctl +==================== + +The Intel SSEI Driver supports the following ioctl commands: + +IOCTL_ISSEI_CONNECT_CLIENT +-------------------------- +Connect to firmware Feature/Client. + +.. code-block:: none + + Usage: + + struct issei_connect_client_data client_data; + + ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &client_data); + + struct issei_connect_client_data - contain the following + Inputs: + in_client_uuid - UUID of the FW Feature that needs to connect to. + Outputs: + out_client_properties - Client Properties: MTU, Protocol Version and Flags. + + Error returns: + ENOTTY No such client (i.e. wrong UUID) or connection is not allowed. + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EBUSY Connection Already Open + +:Note: + max_msg_length (MTU) in client properties describes the maximum + data that can be sent or received. (e.g. with MTU=2K, can send + requests up to bytes 2k and received responses up to 2k bytes). + +IOCTL_ISSEI_DISCONNECT_CLIENT +----------------------------- +Disconnect from firmware Feature/Client. + +.. code-block:: none + + Usage: + + ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, NULL); + + Error returns: + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + ENOTCONN Feature/Client is not connected. diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index d979e6d6e9f2..9955a525436a 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -254,6 +254,7 @@ Hardware Monitoring Kernel Drivers smsc47b397 smsc47m192 smsc47m1 + socfpga-hwmon sparx5-temp spd5118 sq24860 diff --git a/Documentation/hwmon/socfpga-hwmon.rst b/Documentation/hwmon/socfpga-hwmon.rst new file mode 100644 index 000000000000..e5da42556a62 --- /dev/null +++ b/Documentation/hwmon/socfpga-hwmon.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver socfpga-hwmon +============================= + +Supported chips: + + * Altera Stratix 10 SoC FPGA + * Altera Agilex SoC FPGA + +Authors: + - Nazim Amirul + - Tze Yee Ng + +Description +----------- + +This driver supports hardware monitoring for Altera SoC +FPGA devices through the Secure Device Manager and Stratix 10 service layer. + +The following sensor types are supported: + + * temperature + * voltage + +Usage Notes +----------- + +The stratix10-svc driver registers a socfpga-hwmon platform device when +hardware monitor support is enabled. Sensor channels are selected in the +driver based on the service layer compatible string: + + * intel,stratix10-svc + * intel,agilex-svc diff --git a/Documentation/iio/adf41513.rst b/Documentation/iio/adf41513.rst new file mode 100644 index 000000000000..244453cce6f6 --- /dev/null +++ b/Documentation/iio/adf41513.rst @@ -0,0 +1,199 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +ADF41513 driver +=============== + +This driver supports Analog Devices' ADF41513 and similar SPI PLL frequency +synthesizers. + +1. Supported devices +==================== + +* `ADF41510 `_ +* `ADF41513 `_ + +The ADF41513 is an ultralow noise frequency synthesizer that can be used to +implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and +downconversion sections of wireless receivers and transmitters. The ADF41510 +is a similar device that supports frequencies up to 10 GHz. + +Both devices support integer-N and fractional-N operation modes, providing +excellent phase noise performance and flexible frequency generation +capabilities. + +Key Features: + +- **ADF41510**: 1 GHz to 10 GHz frequency range +- **ADF41513**: 1 GHz to 26.5 GHz frequency range +- Integer-N and fractional-N operation modes +- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N) +- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N) +- 25-bit fixed modulus or 49-bit variable modulus fractional modes +- Programmable charge pump currents with 16x range +- Digital lock detect functionality +- Phase resync capability for consistent output phase + +2. Device attributes +==================== + +The ADF41513 driver provides the following IIO extended attributes for +frequency control and monitoring: + +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 that provide access to the synthesizer's functionality. + +The following table shows the ADF41513 related device files: + ++--------------------------------------+-------------------------------------------------------+ +| Device file | Description | ++======================================+=======================================================+ +| out_altvoltage0_frequency | RF output frequency control and readback (Hz) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_frequency_resolution | Target frequency resolution control (Hz) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_powerdown | Power management control (0=active, 1=power down) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_phase | RF output phase adjustment and readback (radians) | ++--------------------------------------+-------------------------------------------------------+ + +2.1 Frequency Control +---------------------- + +The ``out_altvoltage0_frequency`` attribute controls the RF output frequency +with sub-Hz precision. The driver automatically selects between integer-N and +fractional-N modes to achieve the requested frequency with the best possible +phase noise performance. + +**Supported ranges:** + +- **ADF41510**: 1,000,000,000 Hz to 10,000,000,000 Hz (1 GHz to 10 GHz) +- **ADF41513**: 1,000,000,000 Hz to 26,500,000,000 Hz (1 GHz to 26.5 GHz) + +The frequency is specified in Hz, for sub-Hz precision use decimal notation. +For example, 12.102 GHz would be written as "12102000000.000000". + +2.2 Frequency Resolution Control +-------------------------------- + +The ``out_altvoltage0_frequency_resolution`` attribute controls the target +frequency resolution that the driver attempts to achieve. This affects the +choice between integer-N and fractional-N modes, including fixed modulus +(25-bit) and variable modulus (49-bit) fractional-N modes: + +- **Integer-N**: Resolution = :math:`f_{PFD}` (same as PFD frequency) +- **Fixed modulus**: Resolution = :math:`f_{PFD} / 2^{25}` (~3 Hz with 100 MHz PFD) +- **Variable modulus**: Resolution = :math:`f_{PFD} / 2^{49}` (µHz resolution possible) + +Default resolution is 1 Hz (1,000,000 µHz). + +2.3 Phase adjustment +-------------------- + +The ``out_altvoltage0_phase`` attribute allows adjustment of the output phase +in radians. Setting this attribute enables phase adjustment. It can be set +from 0 to :math:`2\pi` radians. Reading this attribute returns the current +phase offset of the output signal. To create a consistent phase relationship +with the reference signal, the phase resync feature needs to be enabled by +setting a non-zero value to the ``adi,phase-resync-period-ns`` device property, +which triggers a phase resynchronization after locking is achieved. + +3. Operating modes +================== + +3.1 Integer-N Mode +------------------ + +When the requested frequency can be achieved as an integer multiple of the PFD +frequency (within the specified resolution tolerance), the driver automatically +selects integer-N mode for optimal phase noise performance. + +In integer-N mode: + +- Phase noise: -235 dBc/Hz normalized floor +- Frequency resolution: :math:`f_{PFD}` (same as PFD frequency) +- Maximum PFD frequency: 250 MHz +- Bleed current: Disabled + +3.2 Fractional-N Mode +--------------------- + +When sub-integer frequency steps are required, the driver automatically selects +fractional-N mode using either fixed or variable modulus. + +**Fixed Modulus (25-bit)**: + +- Used when variable modulus is not required +- Resolution: :math:`f_{PFD} / 2^{25}` +- Simpler implementation, faster settling + +**Variable Modulus (49-bit)**: + +- Used for maximum resolution requirements +- Resolution: :math:`f_{PFD} / 2^{49}` (theoretical) +- Exact frequency synthesis capability + +In fractional-N mode: + +- Phase noise: -231 dBc/Hz normalized floor +- Maximum PFD frequency: 125 MHz +- Bleed current: Automatically enabled and optimized +- Dithering: Enabled to reduce fractional spurs + +3.3 Automatic Mode Selection +---------------------------- + +The driver automatically selects the optimal operating mode based on: + +1. **Frequency accuracy requirements**: Determined by ``frequency_resolution`` setting +2. **Phase noise optimization**: Integer-N preferred when possible +3. **PFD frequency constraints**: Different limits for integer vs fractional modes +4. **Prescaler selection**: Automatic 4/5 vs 8/9 prescaler selection based on frequency + +4. Usage examples +================= + +4.1 Basic Frequency Setting +---------------------------- + +Set output frequency to 12.102 GHz: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency + +Read current frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.582767 + +4.2 High Resolution Frequency Control +------------------------------------- + +Configure for sub-Hz resolution and set a precise frequency: + +.. code-block:: bash + + # Set resolution to 0.1 Hz (100,000 µHz) + root:/sys/bus/iio/devices/iio:device0> echo 0.1 > out_altvoltage0_frequency_resolution + + # Set frequency to 12.102 GHz (1 µHz precision) + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.980131 + +4.3 Monitor Lock Status +----------------------- + +When lock detect GPIO is configured, check if PLL is locked: + +.. code-block:: bash + + # Read frequency - will return error if not locked + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + +If the PLL is not locked, the frequency read will return ``-EBUSY`` (Device or +resource busy). diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 007e0a1fcc5a..b02b879b053a 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers ad7625 ad7944 ade9000 + adf41513 adis16475 adis16480 adis16550 diff --git a/Documentation/misc-devices/amd-sbi.rst b/Documentation/misc-devices/amd-sbi.rst index f91ddadefe48..648d743903b7 100644 --- a/Documentation/misc-devices/amd-sbi.rst +++ b/Documentation/misc-devices/amd-sbi.rst @@ -48,6 +48,66 @@ Access restrictions: * APML Mailbox messages and Register xfer access are read-write, * CPUID and MCA_MSR access is read-only. +SBTSI device +============ + +sbtsi driver under the drivers/misc/amd-sbi creates miscdevice +/dev/sbtsi-* to let user space programs run APML TSI register transfer +commands. + +The driver supports both I2C and I3C transports for SB-TSI targets. +The transport is selected by the bus where the device is enumerated. + +Misc device: + * In 1P socket 0: /dev/sbtsi-4c + * In 2P socket 0: /dev/sbtsi-4c, socket 1: /dev/sbtsi-48 + +.. code-block:: bash + + $ ls -al /dev/sbtsi-4c + crw------- 1 root root 10, 116 Apr 2 05:22 /dev/sbtsi-4c + + +Access restrictions: + * Only root user is allowed to open the file. + * APML TSI Register transfer access is read-write. + +SBTSI hwmon interface +===================== + +The sbtsi_temp auxiliary driver binds to the auxiliary device published +by the core sbtsi driver on the auxiliary bus. The auxiliary device is +named amd-sbtsi.temp-sensor., where is the device's transfer +address: the client address for I2C, or the assigned-address for I3C. + +Note that the auxiliary bus formats in decimal, whereas the +/dev/sbtsi-* misc node formats its address in hex. The two therefore +differ for the same device: an I2C/I3C sensor at address 0x4c appears as the +misc node /dev/sbtsi-4c and the auxiliary device +amd-sbtsi.temp-sensor.76. + +It registers a hwmon device, providing a standard Linux hwmon interface +for reading CPU temperature and managing temperature limits. + +The hwmon device appears under ``/sys/class/hwmon/`` when both ``sbtsi.ko`` +and ``sbtsi_temp.ko`` are loaded. + +Verify auxiliary bus device:: + + ls /sys/bus/auxiliary/devices/ + # e.g. amd-sbtsi.temp-sensor.76 for an I2C/I3C sensor at address 0x4c + +Example usage:: + + # Read current temperature + cat /sys/class/hwmon/hwmon/temp1_input + + # Set high temperature limit to 70 °C + echo 70000 > /sys/class/hwmon/hwmon/temp1_max + + # Verify + cat /sys/class/hwmon/hwmon/temp1_max + Driver IOCTLs ============= @@ -63,6 +123,9 @@ Driver IOCTLs .. c:macro:: SBRMI_IOCTL_REG_XFER_CMD .. kernel-doc:: include/uapi/misc/amd-apml.h :doc: SBRMI_IOCTL_REG_XFER_CMD +.. c:macro:: SBTSI_IOCTL_REG_XFER_CMD +.. kernel-doc:: include/uapi/misc/amd-apml.h + :doc: SBTSI_IOCTL_REG_XFER_CMD User-space usage ================ @@ -85,6 +148,16 @@ Next thing, open the device file, as follows:: exit(1); } +To open SB-TSI device:: + + int file; + + file = open("/dev/sbtsi-4c", O_RDWR); + if (file < 0) { + /* ERROR HANDLING */ + exit(1); + } + The following IOCTLs are defined: ``#define SB_BASE_IOCTL_NR 0xF9`` @@ -92,6 +165,7 @@ The following IOCTLs are defined: ``#define SBRMI_IOCTL_CPUID_CMD _IOWR(SB_BASE_IOCTL_NR, 1, struct apml_cpuid_msg)`` ``#define SBRMI_IOCTL_MCAMSR_CMD _IOWR(SB_BASE_IOCTL_NR, 2, struct apml_mcamsr_msg)`` ``#define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg)`` +``#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)`` User space C-APIs are made available by esmi_oob_library, hosted at diff --git a/Documentation/userspace-api/ntsync.rst b/Documentation/userspace-api/ntsync.rst index 25e7c4aef968..a778016feb87 100644 --- a/Documentation/userspace-api/ntsync.rst +++ b/Documentation/userspace-api/ntsync.rst @@ -83,18 +83,18 @@ structures used in ioctl calls:: }; struct ntsync_event_args { - __u32 signaled; __u32 manual; + __u32 signaled; }; struct ntsync_wait_args { __u64 timeout; __u64 objs; __u32 count; - __u32 owner; __u32 index; - __u32 alert; __u32 flags; + __u32 owner; + __u32 alert; __u32 pad; }; @@ -152,7 +152,7 @@ The ioctls on the device file are as follows: The ioctls on the individual objects are as follows: -.. c:macro:: NTSYNC_IOC_SEM_POST +.. c:macro:: NTSYNC_IOC_SEM_RELEASE Post to a semaphore object. Takes a pointer to a 32-bit integer, which on input holds the count to be added to the semaphore, and on @@ -186,7 +186,7 @@ The ioctls on the individual objects are as follows: unowned and signaled, and eligible threads waiting on it will be woken as appropriate. -.. c:macro:: NTSYNC_IOC_SET_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_SET Signal an event object. Takes a pointer to a 32-bit integer, which on output contains the previous state of the event. @@ -194,12 +194,12 @@ The ioctls on the individual objects are as follows: Eligible threads will be woken, and auto-reset events will be designaled appropriately. -.. c:macro:: NTSYNC_IOC_RESET_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_RESET Designal an event object. Takes a pointer to a 32-bit integer, which on output contains the previous state of the event. -.. c:macro:: NTSYNC_IOC_PULSE_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_PULSE Wake threads waiting on an event object while leaving it in an unsignaled state. Takes a pointer to a 32-bit integer, which on @@ -213,7 +213,7 @@ The ioctls on the individual objects are as follows: afterwards, and a simultaneous read operation will always report the event as unsignaled. -.. c:macro:: NTSYNC_IOC_READ_SEM +.. c:macro:: NTSYNC_IOC_SEM_READ Read the current state of a semaphore object. Takes a pointer to struct :c:type:`ntsync_sem_args`, which is used as follows: @@ -225,7 +225,7 @@ The ioctls on the individual objects are as follows: * - ``max`` - On output, contains the maximum count of the semaphore. -.. c:macro:: NTSYNC_IOC_READ_MUTEX +.. c:macro:: NTSYNC_IOC_MUTEX_READ Read the current state of a mutex object. Takes a pointer to struct :c:type:`ntsync_mutex_args`, which is used as follows: @@ -242,7 +242,7 @@ The ioctls on the individual objects are as follows: ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to zero. -.. c:macro:: NTSYNC_IOC_READ_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_READ Read the current state of an event object. Takes a pointer to struct :c:type:`ntsync_event_args`, which is used as follows: @@ -255,7 +255,7 @@ The ioctls on the individual objects are as follows: - On output, contains 1 if the event is a manual-reset event, and 0 otherwise. -.. c:macro:: NTSYNC_IOC_KILL_OWNER +.. c:macro:: NTSYNC_IOC_MUTEX_KILL Mark a mutex as unowned and abandoned if it is owned by the given owner. Takes an input-only pointer to a 32-bit integer denoting the @@ -276,11 +276,12 @@ The ioctls on the individual objects are as follows: * - ``timeout`` - Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME`` is set, the timeout is measured against the REALTIME clock; - otherwise it is measured against the MONOTONIC clock. If the - timeout is equal to or earlier than the current time, the - function returns immediately without sleeping. If ``timeout`` - is U64_MAX, the function will sleep until an object is - signaled, and will not fail with ``ETIMEDOUT``. + otherwise it is measured against the MONOTONIC clock in the + caller's time namespace. If the timeout is equal to or earlier + than the current time, the function returns immediately + without sleeping. If ``timeout`` is U64_MAX, the function will + sleep until an object is signaled, and will not fail with + ``ETIMEDOUT``. * - ``objs`` - Pointer to an array of ``count`` file descriptors (specified as an integer so that the structure has the same diff --git a/Documentation/w1/masters/omap-hdq.rst b/Documentation/w1/masters/omap-hdq.rst index 5347b5d9e90a..2ecf572be1a4 100644 --- a/Documentation/w1/masters/omap-hdq.rst +++ b/Documentation/w1/masters/omap-hdq.rst @@ -50,5 +50,5 @@ The driver also supports 1-wire mode. In this mode, there is no need to pass slave ID as parameter. The driver will auto-detect slaves connected to the bus using SEARCH_ROM procedure. 1-wire mode can be selected by setting "ti,mode" property to "1w" in DT (see -Documentation/devicetree/bindings/w1/omap-hdq.txt for more details). +Documentation/devicetree/bindings/w1/ti,hdq.yaml for more details). By default driver is in HDQ mode. diff --git a/MAINTAINERS b/MAINTAINERS index d6045c1d7cb3..3bec7701e733 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -964,6 +964,14 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-altera.c +ALTERA SoC FPGA HWMON DRIVER +M: Nazim Amirul +M: Tze Yee Ng +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/socfpga-hwmon.rst +F: drivers/hwmon/socfpga-hwmon.c + ALTERA TRIPLE SPEED ETHERNET DRIVER M: Boon Khai Ng L: netdev@vger.kernel.org @@ -1718,6 +1726,15 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml F: drivers/iio/adc/ade9000.c +ANALOG DEVICES INC ADF41513 DRIVER +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml +F: Documentation/iio/adf41513.rst +F: drivers/iio/frequency/adf41513.c + ANALOG DEVICES INC ADF4377 DRIVER M: Antoniu Miclaus L: linux-iio@vger.kernel.org @@ -4184,11 +4201,11 @@ F: include/linux/async_tx.h AT24 EEPROM DRIVER M: Bartosz Golaszewski -L: linux-i2c@vger.kernel.org +L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git F: Documentation/devicetree/bindings/eeprom/at24.yaml -F: drivers/misc/eeprom/at24.c +F: drivers/nvmem/at24.c ATA OVER ETHERNET (AOE) DRIVER M: "Justin Sanders" @@ -11713,12 +11730,6 @@ W: http://www.highpoint-tech.com F: Documentation/scsi/hptiop.rst F: drivers/scsi/hptiop.c -HIKEY960 ONBOARD USB GPIO HUB DRIVER -M: John Stultz -L: linux-kernel@vger.kernel.org -S: Maintained -F: drivers/misc/hisi_hikey_usb.c - HIMAX HX83112B TOUCHSCREEN SUPPORT M: Job Noorman L: linux-input@vger.kernel.org @@ -11832,8 +11843,7 @@ F: drivers/perf/hisilicon F: tools/perf/pmu-events/arch/arm64/hisilicon/ HISILICON PTT DRIVER -M: Yicong Yang -M: Jonathan Cameron +M: Sizhe Liu L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt @@ -13430,6 +13440,14 @@ F: drivers/platform/x86/intel/sdsi.c F: tools/arch/x86/intel_sdsi/ F: tools/testing/selftests/drivers/sdsi/ +INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI) +M: Alexander Usyskin +S: Supported +F: Documentation/ABI/testing/sysfs-class-issei +F: Documentation/driver-api/issei/issei.rst +F: drivers/misc/issei/ +F: include/uapi/linux/issei.h + INTEL SGX M: Jarkko Sakkinen R: Dave Hansen @@ -15435,6 +15453,14 @@ S: Maintained F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml F: drivers/iio/dac/ltc1660.c +LTC2378 IIO ADC DRIVER +M: Marcelo Schmitt +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml +F: drivers/iio/adc/ltc2378.c + LTC2664 IIO DAC DRIVER M: Michael Hennerich M: Kim Seer Paller @@ -16657,6 +16683,13 @@ S: Maintained F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml F: drivers/mmc/host/mtk-sd.c +MEDIATEK MT6323 PMIC AUXADC DRIVER +M: Roman Vivchar +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/adc/mt6323-auxadc.c +F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h + MEDIATEK MT6735 CLOCK & RESET DRIVERS M: Yassine Oudjana L: linux-clk@vger.kernel.org @@ -17567,6 +17600,7 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.* MHI BUS M: Manivannan Sadhasivam +R: Jeff Hugo L: mhi@lists.linux.dev L: linux-arm-msm@vger.kernel.org S: Maintained @@ -17788,6 +17822,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml F: drivers/iio/adc/mcp3911.c +MICROCHIP MCP47A1 DAC DRIVER +M: Joshua Crofts +S: Maintained +F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml +F: drivers/iio/dac/mcp47a1.c + MICROCHIP MCP9982 TEMPERATURE DRIVER M: Victor Duicu L: linux-hwmon@vger.kernel.org @@ -18982,6 +19022,8 @@ F: include/uapi/linux/sctp.h F: lib/net_utils.c F: lib/random32.c F: net/ +F: rust/helpers/net/ +F: rust/kernel/net/ F: samples/pktgen/ F: tools/net/ F: tools/testing/selftests/net/ @@ -22188,6 +22230,20 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst F: drivers/bus/fsl-mc/ F: include/uapi/linux/fsl_mc.h +QST QMC5883L 3-Axis Magnetic Sensor +M: Siratul Islam +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml +F: drivers/iio/magnetometer/qmc5883l.c + +QST QMC6308 3-AXIS MAGNETIC SENSOR +M: Jorijn van der Graaf +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml +F: drivers/iio/magnetometer/qmc6308.c + QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan @@ -22346,7 +22402,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c QUALCOMM FASTRPC DRIVER M: Srinivas Kandagatla -M: Amol Maheshwari +R: Ekansh Gupta L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Maintained @@ -24711,7 +24767,8 @@ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml F: drivers/iio/chemical/sunrise_co2.c SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER -M: Maxwell Doose +M: Maxwell Doose +M: Tomasz Duszynski S: Maintained F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml F: drivers/iio/chemical/scd30.h @@ -24737,6 +24794,14 @@ S: Maintained F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 F: drivers/iio/chemical/sgp40.c +SENSIRION SLF3S LIQUID FLOW SENSOR DRIVER +M: Wadim Mueller +R: Maxwell Doose +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml +F: drivers/iio/flow/slf3s.c + SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER M: Tomasz Duszynski S: Maintained @@ -24800,17 +24865,6 @@ F: include/linux/phylink.h F: include/linux/sfp.h K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate) -SGI GRU DRIVER -M: Dimitri Sivanich -S: Maintained -F: drivers/misc/sgi-gru/ - -SGI XP/XPC/XPNET DRIVER -M: Robin Holt -M: Steve Wahl -S: Maintained -F: drivers/misc/sgi-xp/ - SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS M: D. Wythe M: Dust Li @@ -25891,7 +25945,7 @@ F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml F: drivers/iio/proximity/vl53l0x-i2c.c ST VL53L1X ToF RANGER(I2C) IIO DRIVER -M: Siratul Islam +M: Siratul Islam L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -27148,6 +27202,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml F: drivers/iio/adc/ti-ads1119.c +TI ADS112C14 ADC DRIVER +M: David Lechner +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml +F: drivers/iio/adc/ti-ads112c14.c + TI ADS1018 ADC DRIVER M: Kurt Borja L: linux-iio@vger.kernel.org @@ -29738,7 +29799,7 @@ F: include/uapi/linux/dqblk_xfs.h F: include/uapi/linux/fsmap.h XILINX AMS DRIVER -M: Salih Erim +M: Sai Krishna Potthuri M: Conall O'Griofa L: linux-iio@vger.kernel.org S: Maintained @@ -29837,6 +29898,14 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.y F: drivers/edac/versalnet_edac.c F: include/linux/cdx/edac_cdx_pcol.h +XILINX VERSAL SYSMON DRIVER +M: Salih Erim +M: Sai Krishna Potthuri +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml +F: drivers/iio/adc/versal-sysmon* + XILINX WATCHDOG DRIVER M: Srinivas Neeli R: Shubhrajyoti Datta diff --git a/drivers/accessibility/speakup/fakekey.c b/drivers/accessibility/speakup/fakekey.c index 868c47b2a59b..9c0b2403876a 100644 --- a/drivers/accessibility/speakup/fakekey.c +++ b/drivers/accessibility/speakup/fakekey.c @@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void) input_sync(virt_keyboard); __this_cpu_write(reporting_keystroke, false); - /* reenable preemption */ + /* re-enable preemption */ preempt_enable(); - /* reenable keyboard interrupts */ + /* re-enable keyboard interrupts */ local_irq_restore(flags); } diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c index 0882bab10fb8..a9e308bdd0be 100644 --- a/drivers/accessibility/speakup/genmap.c +++ b/drivers/accessibility/speakup/genmap.c @@ -71,7 +71,6 @@ main(int argc, char *argv[]) open_input(NULL, argv[1]); while (fgets(buffer, sizeof(buffer), infile)) { - lc++; value = shift_state = 0; cp = strtok(buffer, delims); @@ -153,7 +152,7 @@ main(int argc, char *argv[]) continue; printf("\n\t%d,", lc); for (i = 0; i < max_states; i++) - printf(" %d,", (unsigned int)kp[i]); + printf(" %u,", (unsigned int)kp[i]); } printf("\n\t0, %d\n", map_ver); diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c index d62079b1661f..554bf81f2c1c 100644 --- a/drivers/accessibility/speakup/i18n.c +++ b/drivers/accessibility/speakup/i18n.c @@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = { [MSG_CURSORING_OFF] = "cursoring off", [MSG_CURSORING_ON] = "cursoring on", [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", - [MSG_READ_WINDOW] = "read windo", + [MSG_READ_WINDOW] = "read window", [MSG_READ_ALL] = "read all", [MSG_EDIT_DONE] = "edit done", [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c index 822ceac83068..0940f430ac0f 100644 --- a/drivers/accessibility/speakup/keyhelp.c +++ b/drivers/accessibility/speakup/keyhelp.c @@ -8,14 +8,15 @@ */ #include +#include #include "spk_priv.h" #include "speakup.h" #define MAXFUNCS 130 #define MAXKEYS 256 static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; -static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; -static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; +static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS]; +static u16 masks[] = { 32, 16, 8, 4, 2, 1 }; static short letter_offsets[26] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -49,7 +50,7 @@ static int cur_item, nstates; static void build_key_data(void) { u_char *kp, counters[MAXFUNCS], ch, ch1; - u_short *p_key, key; + u16 *p_key, key; int i, offset = 1; nstates = (int)(state_tbl[-1]); @@ -111,7 +112,7 @@ static void say_key(int key) spk_msg_get(MSG_KEYNAMES_START + (key - 1))); } -static int help_init(void) +static void help_init(void) { char start = SPACE; int i; @@ -120,21 +121,27 @@ static int help_init(void) state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2; for (i = 0; i < num_funcs; i++) { char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i); + char first_letter; - if (start == *cur_funcname) + first_letter = tolower(*cur_funcname); + + /* Accept only 'a'..'z' to index letter_offsets[] safely */ + if (first_letter < 'a' || first_letter > 'z') continue; - start = *cur_funcname; + + if (start == first_letter) + continue; + start = first_letter; letter_offsets[(start & 31) - 1] = i; } - return 0; } -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key) { int i, n; char *name; u_char func, *kp; - u_short *p_keys, val; + u16 *p_keys, val; if (letter_offsets[0] == -1) help_init(); @@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP)); return 1; } - ch |= 32; /* lower case */ + ch = tolower(ch); if (ch < 'a' || ch > 'z') return -1; if (letter_offsets[ch - 'a'] == -1) { diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c index 0dfdb6608e02..a73b478e06de 100644 --- a/drivers/accessibility/speakup/kobjects.c +++ b/drivers/accessibility/speakup/kobjects.c @@ -92,13 +92,13 @@ static void report_char_chartab_status(int reset, int received, int used, if (reset) { pr_info("%s reset to defaults\n", object_type[do_characters]); } else if (received) { - len = snprintf(buf, sizeof(buf), - " updated %d of %d %s\n", - used, received, object_type[do_characters]); + len = scnprintf(buf, sizeof(buf), + " updated %d of %d %s\n", + used, received, object_type[do_characters]); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } @@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj, ssize_t retval = count; unsigned long flags; unsigned long index = 0; - int charclass = 0; + u16 charclass = 0; int received = 0; int used = 0; int rejected = 0; @@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, struct st_var_header *p_header; struct punc_var_t *var; struct st_bits_data *pb; - short mask; + u16 mask; unsigned long flags; p_header = spk_var_header_by_name(attr->attr.name); @@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used, if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 78a77dd789a2..e9b7c2761f6f 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; int spk_no_intr, spk_spell_delay; int spk_key_echo, spk_say_word_ctl; int spk_say_ctrl, spk_bell_pos; -short spk_punc_mask; +u16 spk_punc_mask; int spk_punc_level, spk_reading_punc; int spk_cur_phonetic; char spk_str_caps_start[MAXVARLEN + 1] = "\0"; @@ -183,13 +183,13 @@ char *spk_default_chars[256] = { /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" }; -/* array of 256 u_short (one for each character) +/* array of 256 u16 (one for each character) * initialized to default_chartab and user selectable via * /sys/module/speakup/parameters/chartab */ -u_short spk_chartab[256]; +u16 spk_chartab[256]; -static u_short default_chartab[256] = { +static u16 default_chartab[256] = { B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ @@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc) spk_y = spk_cy = vc->state.y; spk_pos = spk_cp = vc->vc_pos; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); } -static void bleep(u_short val) +static void bleep(u16 val) { static const short vals[] = { 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 @@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc) if (!mark_cut_flag) { mark_cut_flag = 1; - spk_xs = (u_short)spk_x; - spk_ys = (u_short)spk_y; + spk_xs = (u16)spk_x; + spk_ys = (u16)spk_y; spk_sel_cons = vc; synth_printf("%s\n", spk_msg_get(MSG_MARK)); return; } - spk_xe = (u_short)spk_x; - spk_ye = (u_short)spk_y; + spk_xe = (u16)spk_x; + spk_ye = (u16)spk_y; mark_cut_flag = 0; synth_printf("%s\n", spk_msg_get(MSG_CUT)); @@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (spk_attr != spk_old_attr) { if (spk_attrib_bleep & 1) bleep(spk_y); @@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (ch <= 0x7f && isalpha(ch)) { ch &= 0x1f; synth_printf("%s\n", phonetic[--ch]); @@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc) u_char temp; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); /* decided to take out the sayword if on a space (mis-information */ if (spk_say_word_ctl && ch == SPACE) { @@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc) return 0; } else if (tmpx < vc->vc_cols - 2 && (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { + get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) { tmp_pos += 2; tmpx++; } else { while (tmpx > 0) { - ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); + ch = get_char(vc, (u16 *)tmp_pos - 1, &temp); if ((ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) + get_char(vc, (u16 *)tmp_pos, &temp) > SPACE) break; tmp_pos -= 2; tmpx--; } } - attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); + attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr); buf[cnt++] = attr_ch; while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) { tmp_pos += 2; tmpx++; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); if (ch == SPACE || ch == 0 || (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && ch > SPACE)) @@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc) static void say_word(struct vc_data *vc) { u_long cnt = get_word(vc); - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (cnt == 0) return; @@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 0, state = 0; + u16 last_state = 0, state = 0; spk_parked |= 0x01; @@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc) spk_x--; } spk_pos -= 2; - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 2, state = 0; + u16 last_state = 2, state = 0; spk_parked |= 0x01; if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { @@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc) return; } while (1) { - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc) u_char tmp2; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); for (i = 0; i < vc->vc_cols; i++) { - buf[i] = get_char(vc, (u_short *)tmp, &tmp2); + buf[i] = get_char(vc, (u16 *)tmp, &tmp2); tmp += 2; } for (--i; i >= 0; i--) @@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc) { int i = get_line(vc); u16 *cp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (i == 0) { synth_printf("%s\n", spk_msg_get(MSG_BLANK)); @@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to, { int i = 0; u_char tmp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)from); + spk_attr = get_attributes(vc, (u16 *)from); while (from < to) { - buf[i++] = get_char(vc, (u_short *)from, &tmp); + buf[i++] = get_char(vc, (u16 *)from, &tmp); from += 2; if (i >= vc->vc_size_row) break; @@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc) sentmarks[bn][0] = &sentbuf[bn][0]; i = 0; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)start); + spk_attr = get_attributes(vc, (u16 *)start); while (start < end) { - sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); + sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp); if (i > 0) { if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.' && @@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc) static void say_char_num(struct vc_data *vc) { u_char tmp; - u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); + u16 ch = get_char(vc, (u16 *)spk_pos, &tmp); synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); } @@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count) { static int rep_count; static u16 ch = '\0', old_ch = '\0'; - static u_short char_type, last_type; + static u16 char_type, last_type; int in_count = count; spk_keydown = 0; @@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void) static const struct st_bits_data *pb_edit; -static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key) { - short mask = pb_edit->mask, ch_type = spk_chartab[ch]; + u16 mask = pb_edit->mask, ch_type = spk_chartab[ch]; if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) return -1; @@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc) spk_special_handler = edit_bits; } -static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key) { static u_char goto_buf[8]; static int num; @@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value) static const char *pad_chars = "0123456789+-*/\015,.?()"; static int -speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, +speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym, int up_flag) { unsigned long flags; @@ -2444,6 +2444,7 @@ static int __init speakup_init(void) mutex_lock(&spk_mutex); synth_release(); mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); speakup_kobj_exit(); error_kobjects: diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c index 7df7afad5ab4..1713ce4e0ba5 100644 --- a/drivers/accessibility/speakup/selection.c +++ b/drivers/accessibility/speakup/selection.c @@ -13,7 +13,7 @@ #include "speakup.h" -unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ struct vc_data *spk_sel_cons; struct speakup_selection_work { diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h index 54f1226ea061..984a729fd82d 100644 --- a/drivers/accessibility/speakup/speakup.h +++ b/drivers/accessibility/speakup/speakup.h @@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how); int spk_set_string_var(const char *page, struct st_var_header *var, int len); int spk_set_mask_bits(const char *input, const int which, const int how); extern special_func spk_special_handler; -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key); int synth_init(char *name); void synth_release(void); @@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count); int synth_supports_indexing(void); extern struct vc_data *spk_sel_cons; -extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ extern wait_queue_head_t speakup_event; extern struct kobject *speakup_kobj; @@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[]; extern struct spk_synth *synth; extern char spk_pitch_buff[]; extern u_char *spk_our_keys[]; -extern short spk_punc_masks[]; +extern u16 spk_punc_masks[]; extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[]; extern bool spk_paused; extern const struct st_bits_data spk_punc_info[]; extern u_char spk_key_buf[600]; extern char *spk_characters[]; extern char *spk_default_chars[]; -extern u_short spk_chartab[]; +extern u16 spk_chartab[]; extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level; extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps; extern int spk_bleep_time, spk_bell_pos; extern int spk_spell_delay, spk_key_echo; extern int spk_cur_phonetic; -extern short spk_punc_mask; +extern u16 spk_punc_mask; extern short spk_pitch_shift, synth_flags; extern bool spk_quiet_boot; extern char *synth_name; diff --git a/drivers/accessibility/speakup/speakup_acnt.h b/drivers/accessibility/speakup/speakup_acnt.h index cffa938ae580..cea05d770f6d 100644 --- a/drivers/accessibility/speakup/speakup_acnt.h +++ b/drivers/accessibility/speakup/speakup_acnt.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_acntpc.h - header file for speakups Accent-PC driver. */ +#ifndef _SPEAKUP_ACNT_H +#define _SPEAKUP_ACNT_H #define SYNTH_IO_EXTENT 0x02 @@ -17,3 +19,4 @@ #define SYNTH_FULL 'F' /* synth is full. */ #define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */ #define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ +#endif diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c index 083ca9265805..1bf36d1e5477 100644 --- a/drivers/accessibility/speakup/speakup_decpc.c +++ b/drivers/accessibility/speakup/speakup_decpc.c @@ -41,7 +41,7 @@ #define STAT_new_index 0x0040 /* new last index ready */ #define STAT_new_status 0x0080 /* new status posted */ #define STAT_dma_state 0x0100 /* dma state toggle */ -#define STAT_index_valid 0x0200 /* indexs are valid */ +#define STAT_index_valid 0x0200 /* indexes are valid */ #define STAT_flushing 0x0400 /* flush in progress */ #define STAT_self_test 0x0800 /* module in self test */ #define MODE_ready 0xc000 /* module ready for next phase */ diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h index 9c378b58066e..9354e3382bba 100644 --- a/drivers/accessibility/speakup/speakup_dtlk.h +++ b/drivers/accessibility/speakup/speakup_dtlk.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ +#ifndef _SPEAKUP_DTLK_H +#define _SPEAKUP_DTLK_H #define SYNTH_IO_EXTENT 0x02 #define SYNTH_CLEAR 0x18 /* stops speech */ @@ -39,7 +41,7 @@ /* data returned by Interrogate command */ struct synth_settings { - u_short serial_number; /* 0-7Fh:0-7Fh */ + u16 serial_number; /* 0-7Fh:0-7Fh */ u_char rom_version[24]; /* null terminated string */ u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ u_char punc_level; /* nB; 0-7 */ @@ -61,3 +63,4 @@ struct synth_settings { */ u_char has_indexing; /* nonzero if indexing is implemented */ }; +#endif diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 6d446824677b..57d83b82f1d1 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -446,8 +446,9 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) if (var->var_id != PUNC_LEVEL) return 0; - /* We want to set the the speech synthesis punctuation level - * accordingly, so it properly tunes speaking A_PUNC characters */ + /* We want to set the speech synthesis punctuation level + * accordingly, so it properly tunes speaking A_PUNC characters + */ var_data = var->data; if (!var_data) return 0; diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h index 08011518a28a..a5762330e249 100644 --- a/drivers/accessibility/speakup/spk_types.h +++ b/drivers/accessibility/speakup/spk_types.h @@ -53,7 +53,7 @@ enum var_id_t { }; typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, - u_short key); + u16 key); #define COLOR_BUFFER_SIZE 160 diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index d8addbf3ad0d..11cba1fd8715 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth) /* restart */ synth->alive = 1; synth_printf("%s", synth->init); - return 2; /* reenabled */ + return 2; /* re-enabled */ } pr_warn("%s: can't restart synth\n", synth->long_name); return 0; @@ -574,4 +574,4 @@ struct spk_synth *synth_current(void) } EXPORT_SYMBOL_GPL(synth_current); -short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; +u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h index 4ce9a12f7664..db00c962f8e2 100644 --- a/drivers/accessibility/speakup/utils.h +++ b/drivers/accessibility/speakup/utils.h @@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name) else snprintf(filename, sizeof(filename), "%s", name); infile = fopen(filename, "r"); - if (infile == 0) { + if (!infile) { fprintf(stderr, "can't open %s\n", filename); exit(1); } diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index ea5846e4da16..165cb797eb1e 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -384,8 +384,8 @@ pub(crate) fn transfer_binder_object( BINDER_TYPE_WEAK_BINDER }; newobj.flags = obj.flags; - newobj.__bindgen_anon_1.binder = ptr as _; - newobj.cookie = cookie as _; + newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t; + newobj.cookie = cookie as uapi::binder_uintptr_t; self.write(offset, &newobj)?; // Increment the user ref count on the node. It will be decremented as part of the // destruction of the buffer, when we see a binder or weak-binder object. diff --git a/drivers/android/binder/debug.rs b/drivers/android/binder/debug.rs new file mode 100644 index 000000000000..824b10c004c3 --- /dev/null +++ b/drivers/android/binder/debug.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +use kernel::bits::bit_u32; +use kernel::sync::atomic::Atomic; + +kernel::impl_flags!( + /// Represents multiple debug mask flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DebugMasks(u32); + + /// Represents a single debug mask category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DebugMask { + UserError = bit_u32(0), + FailedTransaction = bit_u32(1), + DeadTransaction = bit_u32(2), + OpenClose = bit_u32(3), + DeadBinder = bit_u32(4), + DeathNotification = bit_u32(5), + ReadWrite = bit_u32(6), + UserRefs = bit_u32(7), + Threads = bit_u32(8), + Transaction = bit_u32(9), + TransactionComplete = bit_u32(10), + FreeBuffer = bit_u32(11), + InternalRefs = bit_u32(12), + PriorityCap = bit_u32(13), + Spinlocks = bit_u32(14), + } +); + +#[no_mangle] +pub(crate) static rust_binder_debug_mask: Atomic = Atomic::new( + (DebugMask::UserError as u32) + | (DebugMask::FailedTransaction as u32) + | (DebugMask::DeadTransaction as u32), +); + +/// Checks if the given debug logging category is enabled in the mask. +pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool { + let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed); + DebugMasks(current_mask).contains(mask) +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { + // Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + kernel::pr_info!( + "{}: {}\n", + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + + // Default rule (automatically prepends "PID:TID" of the current calling thread). + ($mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + let thread = kernel::current!(); + kernel::pr_info!( + "{}:{} {}\n", + thread.tgid(), + thread.pid(), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +} diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index 33f51b4139c7..8ac9bdd7a499 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -4,6 +4,8 @@ use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; +use core::ptr; + use kernel::{ transmute::{AsBytes, FromBytes}, uapi::{self, *}, @@ -146,7 +148,7 @@ fn default() -> Self { impl BinderVersion { pub(crate) fn current() -> Self { Self(MaybeUninit::new(uapi::binder_version { - protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _, + protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32, })) } } @@ -165,8 +167,8 @@ impl BinderTransactionDataSecctx { pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData { // SAFETY: Transparent wrapper is safe to transmute. unsafe { - &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data - as *mut BinderTransactionData) + &mut *(ptr::from_mut::(&mut self.transaction_data) + .cast::()) } } } diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index f4df14568b25..66912b4cb527 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -60,6 +60,7 @@ fn allow_duplicate(&self, node: &DArc) -> bool { /// Represents a notification that the freeze state has changed. pub(crate) struct FreezeMessage { cookie: FreezeCookie, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -73,8 +74,8 @@ fn new(flags: kernel::alloc::Flags) -> Result { UniqueArc::new_uninit(flags) } - fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc { - match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) { + fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc { + match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) { Ok(msg) => ListArc::from(msg), Err(err) => match err {}, } @@ -127,7 +128,7 @@ fn do_work( } let mut state_info = BinderFrozenStateInfo::default(); - state_info.is_frozen = is_frozen as u32; + state_info.is_frozen = u32::from(is_frozen); state_info.cookie = freeze.cookie.0; freeze.is_pending = true; freeze.last_is_frozen = Some(is_frozen); @@ -140,7 +141,14 @@ fn do_work( } } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered freeze notification, {:016x}", + self.cookie.0 + ); + } fn should_sync_wakeup(&self) -> bool { false @@ -180,36 +188,61 @@ pub(crate) fn request_freeze_notif( let msg = FreezeMessage::new(GFP_KERNEL)?; let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?; + let mut afl_vec_alloc = KVVec::new(); + let mut info; + let mut freeze_entry; let mut node_refs_guard = self.node_refs.lock(); - let node_refs = &mut *node_refs_guard; - let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); - return Err(EINVAL); - }; - if info.freeze().is_some() { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); - return Err(EINVAL); - } - let node_ref = info.node_ref(); - let freeze_entry = node_refs.freeze_listeners.entry(cookie); - - if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { - if !dupe.get().allow_duplicate(&node_ref.node) { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + loop { + let node_refs = &mut *node_refs_guard; + info = match node_refs.by_handle.get_mut(&handle) { + Some(info) => info, + None => { + binder_debug!( + UserError, + "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}" + ); + return Err(EINVAL); + } + }; + if info.freeze().is_some() { + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set"); return Err(EINVAL); } - } + let node_ref = info.node_ref(); + freeze_entry = node_refs.freeze_listeners.entry(cookie); - // All failure paths must come before this call, and all modifications must come after this - // call. - node_ref.node.add_freeze_listener(self, GFP_KERNEL)?; + if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { + if !dupe.get().allow_duplicate(&node_ref.node) { + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie"); + return Err(EINVAL); + } + } + + // Now we add to the node's freeze listener list, with retry and re-allocate if the + // vector is full. + // + // To ensure that the node is added atomically, this is the first time we modify any + // state. When this call succeeds, all other modifications must occur without the + // possibility for any failure paths. + match node_ref + .node + .add_freeze_listener(self, &mut afl_vec_alloc)? + { + Ok(()) => break, + Err(resize_target) => { + drop(node_refs_guard); + afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?; + node_refs_guard = self.node_refs.lock(); + } + } + } match freeze_entry { rbtree::Entry::Vacant(entry) => { entry.insert( FreezeListener { cookie, - node: node_ref.node.clone(), + node: info.node_ref().node.clone(), last_is_frozen: None, is_pending: false, is_clearing: false, @@ -233,7 +266,7 @@ pub(crate) fn request_freeze_notif( } *info.freeze() = Some(cookie); - let msg = FreezeMessage::init(msg, cookie); + let msg = FreezeMessage::init(msg, cookie, self.task.pid()); drop(node_refs_guard); let _ = self.push_work(msg); Ok(()) @@ -245,18 +278,23 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0); + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not found", + cookie.0 + ); return Err(EINVAL); }; let mut clear_msg = None; if freeze.num_pending_duplicates > 0 { - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); freeze.num_pending_duplicates -= 1; freeze.num_cleared_duplicates += 1; } else { if !freeze.is_pending { - pr_warn!( - "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n", + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending", cookie.0 ); return Err(EINVAL); @@ -264,7 +302,7 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen(); if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) { // Immediately send another FreezeMessage. - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } freeze.is_pending = false; } @@ -280,31 +318,44 @@ pub(crate) fn clear_freeze_notif(self: &Arc, reader: &mut UserSliceReader) let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); + let _to_free_fl; let alloc = FreezeMessage::new(GFP_KERNEL)?; let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}" + ); return Err(EINVAL); }; let Some(info_cookie) = info.freeze() else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active" + ); return Err(EINVAL); }; if *info_cookie != cookie { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch" + ); return Err(EINVAL); } let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}" + ); return Err(EINVAL); }; listener.is_clearing = true; - listener.node.remove_freeze_listener(self); + _to_free_fl = listener.node.remove_freeze_listener(self); *info.freeze() = None; let mut msg = None; if !listener.is_pending { - msg = Some(FreezeMessage::init(alloc, cookie)); + msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } drop(node_refs_guard); @@ -384,7 +435,7 @@ pub(crate) fn prepare_freeze_messages(&self) -> Result(), + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8; + let skb = NetlinkSkBuff::new(size, flags)?; + let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index c10148e9069f..0a82af14cda3 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -9,6 +9,7 @@ seq_print, sync::lock::{spinlock::SpinLockBackend, Guard}, sync::{Arc, LockedBy, SpinLock}, + uapi, }; use crate::{ @@ -21,6 +22,7 @@ }; use core::mem; +use core::ptr; mod wrapper; pub(crate) use self::wrapper::CritIncrWrapper; @@ -321,7 +323,7 @@ pub(crate) unsafe fn remove_node_info( /// An id that is unique across all binder nodes on the system. Used as the key in the /// `by_node` map. pub(crate) fn global_id(&self) -> usize { - self as *const Node as usize + ptr::from_ref(self).addr() } pub(crate) fn get_id(&self) -> (u64, u64) { @@ -333,6 +335,10 @@ pub(crate) fn add_death( death: ListArc, 1>, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) { + assert!( + core::ptr::eq(self, &**death.node), + "attempt to add NodeDeath to the wrong death list" + ); self.inner.access_mut(guard).death_list.push_back(death); } @@ -343,7 +349,7 @@ pub(crate) fn inc_ref_done_locked( ) -> Option> { let inner = self.inner.access_mut(owner_inner); if inner.active_inc_refs == 0 { - pr_err!("inc_ref_done called when no active inc_refs"); + binder_debug!(UserError, "inc_ref_done called when no active inc_refs"); return None; } @@ -464,7 +470,7 @@ pub(crate) fn incr_refcount_allow_zero2one_with_wrapper( owner_inner: &mut ProcessInner, ) -> Option> { match self.incr_refcount_allow_zero2one(strong, owner_inner) { - Ok(Some(node)) => Some(node as _), + Ok(Some(node)) => Some(node as DLArc), Ok(None) => None, Err(CouldNotDeliverCriticalIncrement) => { assert!(strong); @@ -489,8 +495,8 @@ pub(crate) fn populate_counts( guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { let inner = self.inner.access(guard); - out.strong_count = inner.strong.count as _; - out.weak_count = inner.weak.count as _; + out.strong_count = inner.strong.count as u32; + out.weak_count = inner.weak.count as u32; } pub(crate) fn populate_debug_info( @@ -498,8 +504,8 @@ pub(crate) fn populate_debug_info( out: &mut BinderNodeDebugInfo, guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { - out.ptr = self.ptr as _; - out.cookie = self.cookie as _; + out.ptr = self.ptr as uapi::binder_uintptr_t; + out.cookie = self.cookie as uapi::binder_uintptr_t; let inner = self.inner.access(guard); if inner.strong.has_count { out.has_strong_ref = 1; @@ -536,7 +542,7 @@ pub(crate) fn submit_oneway( inner.oneway_todo.push_back(transaction); } else { inner.has_oneway_transaction = true; - guard.push_work(transaction)?; + guard.push_work(&self.owner, transaction)?; } Ok(()) } @@ -568,7 +574,7 @@ pub(crate) fn pending_oneway_finished(&self) { let transaction = inner.oneway_todo.pop_front(); inner.has_oneway_transaction = transaction.is_some(); if let Some(transaction) = transaction { - match guard.push_work(transaction) { + match guard.push_work(&self.owner, transaction) { Ok(()) => {} Err((_err, work)) => { // Process is dead. @@ -657,29 +663,26 @@ fn do_work_locked( pub(crate) fn add_freeze_listener( &self, process: &Arc, - flags: kernel::alloc::Flags, - ) -> Result { - let mut vec_alloc = KVVec::>::new(); - loop { - let mut guard = self.owner.inner.lock(); - // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the - // listener, no the target. - let inner = self.inner.access_mut(&mut guard); - let len = inner.freeze_list.len(); - if len >= inner.freeze_list.capacity() { - if len >= vec_alloc.capacity() { - drop(guard); - vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?; - continue; - } - mem::swap(&mut inner.freeze_list, &mut vec_alloc); - for elem in vec_alloc.drain_all() { - inner.freeze_list.push_within_capacity(elem)?; - } + // If the vector needs to be resized, it's done via this argument. + vec_alloc: &mut KVVec>, + ) -> Result> { + let mut guard = self.owner.inner.lock(); + // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the + // listener, not the target. + let inner = self.inner.access_mut(&mut guard); + let len = inner.freeze_list.len(); + if len == inner.freeze_list.capacity() { + if len >= vec_alloc.capacity() { + // Request the caller to reallocate. + return Ok(Err((1 + len).next_power_of_two())); + } + mem::swap(&mut inner.freeze_list, vec_alloc); + for elem in vec_alloc.drain_all() { + inner.freeze_list.push_within_capacity(elem)?; } - inner.freeze_list.push_within_capacity(process.clone())?; - return Ok(()); } + inner.freeze_list.push_within_capacity(process.clone())?; + Ok(Ok(())) } pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec> { @@ -695,6 +698,8 @@ pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec> p.pid_in_current_ns() ); } + // If the vector is empty it needs to be freed. However, we can't free it here because that + // might sleep, so return it to the caller. if inner.freeze_list.is_empty() { return mem::take(&mut inner.freeze_list); } @@ -820,6 +825,7 @@ pub(crate) fn get_count(&self) -> (usize, usize) { pub(crate) fn clone(&self, strong: bool) -> Result { if strong && self.strong_count == 0 { + binder_debug!(UserError, "tried to use weak ref as strong ref"); return Err(EINVAL); } Ok(self @@ -860,9 +866,10 @@ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool { *count += 1; } else { if *count == 0 { - pr_warn!( - "pid {} performed invalid decrement on ref\n", - kernel::current!().pid() + binder_debug!( + UserError, + "performed invalid {} decrement on ref", + if strong { "strong" } else { "weak" } ); return false; } @@ -1104,6 +1111,11 @@ fn do_work( // We're still holding the inner lock, so it cannot be aborted while we insert it into // the delivered list. process_inner.death_delivered(self.clone()); + binder_debug!( + DeathNotification, + "sending death notification, cookie {:016x}", + cookie + ); BR_DEAD_BINDER }; @@ -1114,7 +1126,14 @@ fn do_work( Ok(cmd != BR_DEAD_BINDER) } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + binder_debug!( + pid = self.process.task.pid(), + DeadTransaction, + "undelivered death notification, {:016x}", + self.cookie + ); + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs index 43294c050502..6e4ca01c941a 100644 --- a/drivers/android/binder/node/wrapper.rs +++ b/drivers/android/binder/node/wrapper.rs @@ -21,7 +21,7 @@ pub(crate) fn new() -> Result { pub(super) fn init(self, node: DArc) -> DLArc { match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) { - Ok(initialized) => ListArc::from(initialized) as _, + Ok(initialized) => ListArc::from(initialized) as DLArc, Err(err) => match err {}, } } diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index e82a5523804f..52ffbf3504e7 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -312,7 +312,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result { // SAFETY: This just initializes the pages array. unsafe { - let self_ptr = self as *const ShrinkablePageRange; + let self_ptr = ptr::from_ref(self); for i in 0..num_pages { let info = pages.as_mut_ptr().add(i); (&raw mut (*info).range).write(self_ptr); @@ -571,7 +571,7 @@ pub(crate) unsafe fn read(&self, offset: usize) -> Result { unsafe { self.iterate(offset, size_of::(), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset); + let obj_ptr = out.as_mut_ptr().cast::().add(out_offset); // SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid. page.read_raw(obj_ptr, offset, to_copy)?; out_offset += to_copy; @@ -593,7 +593,7 @@ pub(crate) unsafe fn write(&self, offset: usize, obj: &T) -> Result { unsafe { self.iterate(offset, size_of_val(obj), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (obj as *const T as *const u8).add(obj_offset); + let obj_ptr = ptr::from_ref(obj).cast::().add(obj_offset); // SAFETY: We have a reference to the object, so the pointer is valid. page.write_raw(obj_ptr, offset, to_copy)?; obj_offset += to_copy; @@ -712,7 +712,7 @@ fn drop(self: Pin<&mut Self>) { { // CAST: The `list_head` field is first in `PageInfo`. - let info = item as *mut PageInfo; + let info = item.cast::(); // SAFETY: The `range` field of `PageInfo` is immutable. range_ptr = unsafe { (*info).range }; // SAFETY: The `range` outlives its `PageInfo` values. diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 5b8f73ec1931..5372bfbd93b3 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -16,6 +16,7 @@ use kernel::{ bindings, + bits::bit_u8, cred::Credential, error::Error, fs::file::{self, File}, @@ -30,9 +31,10 @@ sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, - Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, + poll::PollCondVarBox, + Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc, }, - task::Task, + task::{Pid, Task}, uaccess::{UserSlice, UserSliceReader}, uapi, workqueue::{self, Work}, @@ -70,9 +72,18 @@ fn new(address: usize, size: usize) -> Self { } } -// bitflags for defer_work. -const PROC_DEFER_FLUSH: u8 = 1; -const PROC_DEFER_RELEASE: u8 = 2; +kernel::impl_flags!( + /// Represents multiple deferred work flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DeferWorks(u8); + + /// Represents a single deferred work category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DeferWork { + Flush = bit_u8(0), + Release = bit_u8(1), + } +); #[derive(Copy, Clone)] pub(crate) enum IsFrozen { @@ -121,7 +132,7 @@ pub(crate) struct ProcessInner { started_thread_count: u32, /// Bitmap of deferred work to do. - defer_work: u8, + defer_work: DeferWorks, /// Number of transactions to be transmitted before processes in freeze_wait /// are woken up. @@ -151,7 +162,7 @@ fn new() -> Self { requested_thread_count: 0, max_threads: 0, started_thread_count: 0, - defer_work: 0, + defer_work: DeferWorks::default(), outstanding_txns: 0, is_frozen: IsFrozen::No, sync_recv: false, @@ -172,21 +183,26 @@ fn new() -> Self { /// taken while holding the inner process lock. pub(crate) fn push_work( &mut self, + proc: &Process, work: DLArc, ) -> Result<(), (BinderError, DLArc)> { + let sync = work.should_sync_wakeup(); + // Try to find a ready thread to which to push the work. if let Some(thread) = self.ready_threads.pop_front() { // Push to thread while holding state lock. This prevents the thread from giving up // (for example, because of a signal) when we're about to deliver work. - match thread.push_work(work) { + match thread.push_work_inner(work, sync) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + proc.notify_poll(sync); + Ok(()) + } PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)), } } else if self.is_dead { Err((BinderError::new_dead(), work)) } else { - let sync = work.should_sync_wakeup(); - // Didn't find a thread waiting for proc work; this can happen // in two scenarios: // 1. All threads are busy handling transactions @@ -194,17 +210,12 @@ pub(crate) fn push_work( // the kernel driver soon and pick up this work. // 2. Threads are using the (e)poll interface, in which case // they may be blocked on the waitqueue without having been - // added to waiting_threads. For this case, we just iterate - // over all threads not handling transaction work, and - // wake them all up. We wake all because we don't know whether - // a thread that called into (e)poll is handling non-binder - // work currently. + // added to waiting_threads. For this case, we wake it up + // directly. self.work.push_back(work); // Wake up polling threads, if any. - for thread in self.threads.values() { - thread.notify_if_poll_ready(sync); - } + proc.notify_poll(sync); Ok(()) } @@ -227,11 +238,11 @@ pub(crate) fn update_node_refcount( // If we decided that we need to push work, push either to the process or to a thread if // one is specified. - if let Some(node) = push { + if let Some(pnode) = push { if let Some(thread) = othread { - thread.push_work_deferred(node); + thread.push_work_deferred(pnode); } else { - let _ = self.push_work(node); + let _ = self.push_work(&node.owner, pnode); // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in // that case, it doesn't care about the notification. } @@ -259,7 +270,7 @@ pub(crate) fn new_node_ref_with_thread( let push = match wrapper { None => node .incr_refcount_allow_zero2one(strong, self)? - .map(|node| node as _), + .map(|node| node as DLArc), Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self), }; if let Some(node) = push { @@ -455,7 +466,13 @@ pub(crate) struct Process { // Node references are in a different lock to avoid recursive acquisition when // incrementing/decrementing a node in another process. #[pin] - node_refs: Mutex, + node_refs: SpinLock, + + // Synchronizes `register_wait` calls to the `PollCondVarBox`. + // + // The `PollCondVarBox` is not stored here because synchronization is + // done for `register_wait` only. Wakeups do not take this lock. + poll: SetOnce, // Work node for deferred work item. #[pin] @@ -489,13 +506,13 @@ fn run(me: Arc) { { let mut inner = me.inner.lock(); defer = inner.defer_work; - inner.defer_work = 0; + inner.defer_work = DeferWorks::default(); } - if defer & PROC_DEFER_FLUSH != 0 { + if defer.contains(DeferWork::Flush) { me.deferred_flush(); } - if defer & PROC_DEFER_RELEASE != 0 { + if defer.contains(DeferWork::Release) { me.deferred_release(); } } @@ -510,12 +527,13 @@ fn new(ctx: Arc, cred: ARef) -> Result> { cred, inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"), pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER), - node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"), + node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"), freeze_wait <- kernel::new_condvar!("Process::freeze_wait"), task: current.group_leader().into(), defer_work <- kernel::new_work!("Process::defer_work"), links <- ListLinks::new(), stats: BinderStats::new(), + poll: SetOnce::new(), }), GFP_KERNEL, )?; @@ -715,7 +733,7 @@ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result> { pub(crate) fn push_work(&self, work: DLArc) -> BinderResult { // If push_work fails, drop the work item outside the lock. - let res = self.inner.lock().push_work(work); + let res = self.inner.lock().push_work(self, work); match res { Ok(()) => Ok(()), Err((err, work)) => { @@ -741,7 +759,7 @@ fn set_as_manager( } else { (0, 0, 0) }; - let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?; + let node_ref = self.get_node(ptr, cookie, flags, true, thread)?; let node = node_ref.node.clone(); self.ctx.set_manager_node(node_ref)?; self.inner.lock().is_manager = true; @@ -861,14 +879,17 @@ pub(crate) fn insert_or_update_handle( let handle = unused_id.as_u32(); // Do a lookup again as node may have been inserted before the lock was reacquired. - if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) { - let handle = *handle_ref; - let info = refs.by_handle.get_mut(&handle).unwrap(); - info.node_ref().absorb(node_ref); - return Ok(handle); - } + let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) { + rbtree::Entry::Vacant(by_node_slot) => by_node_slot, + rbtree::Entry::Occupied(handle_ref) => { + // The node was inserted by another thread while we didn't hold the lock. + let handle = handle_ref.get(); + let info = refs.by_handle.get_mut(handle).unwrap(); + info.node_ref().absorb(node_ref); + return Ok(*handle); + } + }; - let gid = node_ref.node.global_id(); let (info_proc, info_node) = { let info_init = NodeRefInfo::new(node_ref, handle, self.into()); match info.pin_init_with(info_init) { @@ -884,6 +905,9 @@ pub(crate) fn insert_or_update_handle( // first thing in `deferred_release`, process cleanup will not miss the items inserted into // `refs` below. if self.inner.lock().is_dead { + // Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of + // the lock. + drop(refs_lock); return Err(ESRCH); } @@ -891,7 +915,7 @@ pub(crate) fn insert_or_update_handle( // `info_node` into the right node's `refs` list. unsafe { info_proc.node_ref2().node.insert_node_info(info_node) }; - refs.by_node.insert(reserve1.into_node(gid, handle)); + by_node_slot.insert(handle, reserve1); by_handle_slot.insert(info_proc, reserve2); unused_id.acquire(); Ok(handle) @@ -906,7 +930,13 @@ pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult } Ok(node_ref) } else { - Ok(self.get_node_from_handle(handle, true)?) + match self.get_node_from_handle(handle, true) { + Ok(node_ref) => Ok(node_ref), + Err(err) => { + binder_debug!(UserError, "got transaction to invalid handle {handle}"); + Err(err.into()) + } + } } } @@ -946,15 +976,19 @@ pub(crate) fn update_ref( // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_by_handle; + let _to_free_by_node; let _to_free_freeze_listener; let _to_free_freeze_listener_cleanup; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { // Clean up death if there is one attached to this node reference. - if let Some(death) = info.death().take() { + // + // We remove the entire `info` below, so no need to remove `death` from `info`. + if let Some(death) = info.death().as_ref() { death.set_cleared(true); - self.remove_from_delivered_deaths(&death); + self.remove_from_delivered_deaths(death); } // Remove reference from process tables, and from the node's `refs` list. @@ -971,8 +1005,8 @@ pub(crate) fn update_ref( } } - refs.by_handle.remove(&handle); - refs.by_node.remove(&id); + _to_free_by_handle = refs.by_handle.remove_node(&handle); + _to_free_by_node = refs.by_node.remove_node(&id); refs.handle_is_present.release_id(handle as usize); if let Some(shrink) = refs.handle_is_present.shrink_request() { @@ -987,7 +1021,7 @@ pub(crate) fn update_ref( } else { // All refs are cleared in process exit, so this warning is expected in that case. if !self.inner.lock().is_dead { - pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns()); + binder_debug!(UserError, "no such ref {handle}"); } } Ok(()) @@ -1008,7 +1042,7 @@ pub(crate) fn inc_ref_done(&self, reader: &mut UserSliceReader, strong: bool) -> if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) { if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) { // This only fails if the process is dead. - let _ = inner.push_work(node); + let _ = inner.push_work(self, node); } } Ok(()) @@ -1237,16 +1271,26 @@ pub(crate) fn request_death( // Queue BR_ERROR if we can't allocate memory for the death notification. let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| { thread.push_return_work(BR_ERROR); + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure" + ); })?; let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; // Nothing to do if there is already a death notification request for this handle. if info.death().is_some() { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION death notification already set" + ); return Ok(()); } @@ -1274,6 +1318,11 @@ pub(crate) fn request_death( info.node_ref().node.add_death(death, &mut owner_inner); } } + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } @@ -1283,28 +1332,45 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; let Some(death) = info.death().take() else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification not active" + ); return Ok(()); }; if death.cookie != cookie { *info.death() = Some(death); - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch" + ); return Ok(()); } // Update state and determine if we need to queue a work item. We only need to do it when // the node is not dead or if the user already completed the death notification. - if death.set_cleared(false) { + let should_schedule = death.set_cleared(false); + drop(refs); + + if should_schedule { if let Some(death) = ListArc::try_from_arc_or_drop(death) { let _ = thread.push_work_if_looper(death); } } + binder_debug!( + DeathNotification, + "BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } @@ -1328,6 +1394,7 @@ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> { } fn deferred_flush(&self) { + binder_debug!(pid = self.task.pid(), OpenClose, "flushing process"); let inner = self.inner.lock(); for thread in inner.threads.values() { thread.exit_looper(); @@ -1335,6 +1402,8 @@ fn deferred_flush(&self) { } fn deferred_release(self: Arc) { + binder_debug!(pid = self.task.pid(), OpenClose, "releasing process"); + let is_manager = { let mut inner = self.inner.lock(); inner.is_dead = true; @@ -1382,13 +1451,11 @@ fn deferred_release(self: Arc) { // SAFETY: We are removing the `NodeRefInfo` from the right node. unsafe { info.node_ref2().node.remove_node_info(info) }; - // Remove all death notifications from the nodes (that belong to a different process). - let death = if let Some(existing) = info.death().take() { - existing - } else { - continue; - }; - death.set_cleared(false); + // Clear death notifications from the nodes (that belong to a different process). + // No need to remove them from `info` as we clear info below. + if let Some(death) = info.death().as_ref() { + death.set_cleared(false); + } } // Clean up freeze listeners. @@ -1524,6 +1591,15 @@ pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result { } } } + + pub(crate) fn notify_poll(&self, sync: bool) { + if let Some(poll) = self.poll.as_ref() { + if sync { + poll.notify_sync(); + } + poll.notify_all(); + } + } } fn get_frozen_status(data: UserSlice) -> Result { @@ -1536,13 +1612,13 @@ fn get_frozen_status(data: UserSlice) -> Result { for ctx in crate::context::get_all_contexts()? { ctx.for_each_proc(|proc| { - if proc.task.pid() == info.pid as _ { + if proc.task.pid() == info.pid as Pid { found = true; let inner = proc.inner.lock(); let txns_pending = inner.txns_pending_locked(); - info.async_recv |= inner.async_recv as u32; - info.sync_recv |= inner.sync_recv as u32; - info.sync_recv |= (txns_pending as u32) << 1; + info.async_recv |= u32::from(inner.async_recv); + info.sync_recv |= u32::from(inner.sync_recv); + info.sync_recv |= u32::from(txns_pending) << 1; } }); } @@ -1634,7 +1710,9 @@ fn ioctl_write_read( /// The file operations supported by `Process`. impl Process { pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result> { - Self::new(ctx.into(), ARef::from(file.cred())) + let proc = Self::new(ctx.into(), ARef::from(file.cred()))?; + binder_debug!(OpenClose, "opened process"); + Ok(proc) } pub(crate) fn release(this: Arc, _file: &File) { @@ -1642,8 +1720,8 @@ pub(crate) fn release(this: Arc, _file: &File) { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_RELEASE; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Release; binderfs_file = inner.binderfs_file.take(); } @@ -1660,8 +1738,8 @@ pub(crate) fn flush(this: ArcBorrow<'_, Process>) -> Result { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_FLUSH; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Flush; } if should_schedule { @@ -1719,7 +1797,21 @@ pub(crate) fn poll( table: PollTable<'_>, ) -> Result { let thread = this.get_current_thread()?; - let (from_proc, mut mask) = thread.poll(file, table); + { + let poll = loop { + if let Some(poll) = this.poll.as_ref() { + break poll; + } + + let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?; + // Reuse our existing lock to synchronize callers initializing. + let _guard = this.node_refs.lock(); + this.poll.populate(poll); + }; + + table.register_wait(file, poll); + } + let (from_proc, mut mask) = thread.poll()?; if mask == 0 && from_proc && !this.inner.lock().work.is_empty() { mask |= bindings::POLLIN; } diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index d6ceebbd5f94..955c4c348f73 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,12 +6,6 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow( - clippy::as_underscore, - clippy::ref_as_ptr, - clippy::ptr_as_ptr, - clippy::cast_lossless -)] use kernel::{ bindings::{self, seq_file}, @@ -38,7 +32,10 @@ mod context; mod deferred_close; mod defs; +#[macro_use] +mod debug; mod error; +mod netlink; mod node; mod page_range; mod process; @@ -226,6 +223,7 @@ fn arc_pin_init(init: impl PinInit) -> Result, kernel::error::Error> struct DeliverCode { code: u32, skip: Atomic, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -233,10 +231,11 @@ struct DeliverCode { } impl DeliverCode { - fn new(code: u32) -> Self { + fn new(code: u32, pid: i32) -> Self { Self { code, skip: Atomic::new(false), + pid, } } @@ -261,7 +260,15 @@ fn do_work( Ok(true) } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + if !self.skip.load(Relaxed) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_COMPLETE" + ); + } + } fn should_sync_wakeup(&self) -> bool { false @@ -289,19 +296,22 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; -struct BinderModule {} +struct BinderModule { + _netlink: kernel::net::netlink::Registration, +} impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; + let netlink = crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } @@ -315,9 +325,6 @@ unsafe impl Sync for AssertSync {} #[no_mangle] #[used] pub static rust_binder_fops: AssertSync = { - // SAFETY: All zeroes is safe for the `file_operations` type. - let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let ops = kernel::bindings::file_operations { owner: this_module::().as_ptr(), poll: Some(rust_binder_poll), @@ -327,7 +334,7 @@ unsafe impl Sync for AssertSync {} open: Some(rust_binder_open), release: Some(rust_binder_release), flush: Some(rust_binder_flush), - ..zeroed_ops + ..pin_init::zeroed() }; AssertSync(ops) }; @@ -418,7 +425,7 @@ unsafe impl Sync for AssertSync {} // SAFETY: We previously set `private_data` in `rust_binder_open`. let f = unsafe { Arc::::borrow((*file).private_data) }; // SAFETY: The caller ensures that the file is valid. - match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) { + match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) { Ok(()) => 0, Err(err) => err.to_errno() as isize, } @@ -512,7 +519,7 @@ unsafe impl Sync for AssertSync {} _: *mut kernel::ffi::c_void, ) -> kernel::ffi::c_int { // SAFETY: Accessing the private field of `seq_file` is okay. - let pid = (unsafe { (*ptr).private }) as usize as Pid; + let pid = unsafe { (*ptr).private }.addr() as Pid; // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which // this method is called. let m = unsafe { SeqFile::from_raw(ptr) }; diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c index ade1c4d92499..300cc65562d1 100644 --- a/drivers/android/binder/rust_binderfs.c +++ b/drivers/android/binder/rust_binderfs.c @@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; module_param_named(rust_devices, rust_binder_devices_param, charp, 0444); +extern u32 rust_binder_debug_mask; +module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644); + static dev_t binderfs_dev; static DEFINE_MUTEX(binderfs_minors_mutex); static DEFINE_IDA(binderfs_minors); diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index bc0ef8927905..18a14aa8a835 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -9,15 +9,15 @@ use kernel::{ bindings, - fs::{File, LocalFile}, + bits::bit_u32, + fs::LocalFile, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, security, seq_file::SeqFile, seq_print, sync::atomic::{ordering::Relaxed, Atomic}, - sync::poll::{PollCondVar, PollTable}, - sync::{aref::ARef, Arc, SpinLock}, + sync::{aref::ARef, Arc, CondVar, SpinLock}, task::Task, uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, @@ -30,7 +30,7 @@ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::{Transaction, TransactionInfo}, + transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -225,8 +225,10 @@ fn claim_next(&mut self, size: usize) -> Result { } } +#[must_use] pub(crate) enum PushWorkRes { Ok, + OkNotifyPoll, FailedDead(DLArc), } @@ -234,6 +236,7 @@ impl PushWorkRes { fn is_ok(&self) -> bool { match self { PushWorkRes::Ok => true, + PushWorkRes::OkNotifyPoll => true, PushWorkRes::FailedDead(_) => false, } } @@ -243,7 +246,7 @@ fn is_ok(&self) -> bool { struct InnerThread { /// Determines the looper state of the thread. It is a bit-wise combination of the constants /// prefixed with `LOOPER_`. - looper_flags: u32, + looper_flags: LooperFlags, /// Determines whether the looper should return. looper_need_return: bool, @@ -270,28 +273,38 @@ struct InnerThread { extended_error: ExtendedError, } -const LOOPER_REGISTERED: u32 = 0x01; -const LOOPER_ENTERED: u32 = 0x02; -const LOOPER_EXITED: u32 = 0x04; -const LOOPER_INVALID: u32 = 0x08; -const LOOPER_WAITING: u32 = 0x10; -const LOOPER_WAITING_PROC: u32 = 0x20; -const LOOPER_POLL: u32 = 0x40; +kernel::impl_flags!( + /// Represents multiple looper flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct LooperFlags(u32); + + /// Represents a single looper flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum LooperFlag { + Registered = bit_u32(0), + Entered = bit_u32(1), + Exited = bit_u32(2), + Invalid = bit_u32(3), + Waiting = bit_u32(4), + WaitingProc = bit_u32(5), + Poll = bit_u32(6), + } +); impl InnerThread { - fn new() -> Result { + fn new(pid: i32) -> Result { fn next_err_id() -> u32 { static EE_ID: Atomic = Atomic::new(0); EE_ID.fetch_add(1, Relaxed) } Ok(Self { - looper_flags: 0, + looper_flags: LooperFlags::default(), looper_need_return: false, is_dead: false, process_work_list: false, - reply_work: ThreadError::try_new()?, - return_work: ThreadError::try_new()?, + reply_work: ThreadError::try_new(pid)?, + return_work: ThreadError::try_new(pid)?, work_list: List::new(), current_transaction: None, extended_error: ExtendedError::new(next_err_id(), BR_OK, 0), @@ -310,27 +323,32 @@ fn pop_work(&mut self) -> Option> { fn push_work(&mut self, work: DLArc) -> PushWorkRes { if self.is_dead { - PushWorkRes::FailedDead(work) + return PushWorkRes::FailedDead(work); + } + self.work_list.push_back(work); + self.process_work_list = true; + if self.looper_flags.contains(LooperFlag::Poll) { + PushWorkRes::OkNotifyPoll } else { - self.work_list.push_back(work); - self.process_work_list = true; PushWorkRes::Ok } } - fn push_reply_work(&mut self, code: u32) { + fn push_reply_work(&mut self, code: u32) -> PushWorkRes { if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) { work.set_error_code(code); - self.push_work(work); + self.push_work(work) } else { pr_warn!("Thread reply work is already in use."); + PushWorkRes::Ok } } fn push_return_work(&mut self, reply: u32) { if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) { work.set_error_code(reply); - self.push_work(work); + // Not notifying: Reply to current thread. + let _ = self.push_work(work); } else { pr_warn!("Thread return work is already in use."); } @@ -373,26 +391,27 @@ fn pop_transaction_replied(&mut self, transaction: &DArc) -> bool { } fn looper_enter(&mut self) { - self.looper_flags |= LOOPER_ENTERED; - if self.looper_flags & LOOPER_REGISTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Entered; + if self.looper_flags.contains(LooperFlag::Registered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_register(&mut self, valid: bool) { - self.looper_flags |= LOOPER_REGISTERED; - if !valid || self.looper_flags & LOOPER_ENTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Registered; + if !valid || self.looper_flags.contains(LooperFlag::Entered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_exit(&mut self) { - self.looper_flags |= LOOPER_EXITED; + self.looper_flags |= LooperFlag::Exited; } /// Determines whether the thread is part of a pool, i.e., if it is a looper. fn is_looper(&self) -> bool { - self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0 + self.looper_flags + .contains_any(LooperFlag::Entered | LooperFlag::Registered) } /// Determines whether the thread should attempt to fetch work items from the process queue. @@ -404,7 +423,7 @@ fn should_use_process_work_queue(&self) -> bool { } fn poll(&mut self) -> u32 { - self.looper_flags |= LOOPER_POLL; + self.looper_flags |= LooperFlag::Poll; if self.process_work_list || self.looper_need_return { bindings::POLLIN } else { @@ -422,7 +441,7 @@ pub(crate) struct Thread { #[pin] inner: SpinLock, #[pin] - work_condvar: PollCondVar, + work_condvar: CondVar, /// Used to insert this thread into the process' `ready_threads` list. /// /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`. @@ -445,7 +464,7 @@ impl ListItem<0> for Thread { impl Thread { pub(crate) fn new(id: i32, process: Arc) -> Result> { - let inner = InnerThread::new()?; + let inner = InnerThread::new(process.task.pid())?; Arc::pin_init( try_pin_init!(Thread { @@ -453,7 +472,7 @@ pub(crate) fn new(id: i32, process: Arc) -> Result> { process, task: ARef::from(&**kernel::current!()), inner <- kernel::new_spinlock!(inner, "Thread::inner"), - work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"), + work_condvar <- kernel::new_condvar!("Thread::work_condvar"), links <- ListLinks::new(), links_track <- AtomicTracker::new(), }), @@ -470,7 +489,7 @@ pub(crate) fn debug_print(self: &Arc, m: &SeqFile, print_all: bool) -> Res m, " thread {}: l {:02x} need_return {}\n", self.id, - inner.looper_flags, + u32::from(inner.looper_flags), inner.looper_need_return, ); } @@ -543,9 +562,9 @@ fn get_work_local(self: &Arc, wait: bool) -> Result, wait: bool) -> Result, wait: bool) -> Result) -> PushWorkRes { let sync = work.should_sync_wakeup(); + self.push_work_inner(work, sync) + } + pub(crate) fn push_work_inner( + &self, + work: DLArc, + sync: bool, + ) -> PushWorkRes { let res = self.inner.lock().push_work(work); if res.is_ok() { @@ -643,7 +669,8 @@ pub(crate) fn push_work(&self, work: DLArc) -> PushWorkRes { pub(crate) fn push_work_if_looper(&self, work: DLArc) -> BinderResult { let mut inner = self.inner.lock(); if inner.is_looper() && !inner.is_dead { - inner.push_work(work); + // Not notifying: Reply to current thread. + let _ = inner.push_work(work); Ok(()) } else { drop(inner); @@ -673,9 +700,9 @@ fn translate_object( let strong = obj.hdr.type_ == BINDER_TYPE_BINDER; // SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid // representation. - let ptr = unsafe { obj.__bindgen_anon_1.binder } as _; - let cookie = obj.cookie as _; - let flags = obj.flags as _; + let ptr = unsafe { obj.__bindgen_anon_1.binder }; + let cookie = obj.cookie; + let flags = obj.flags; let node = self .process .as_arc_borrow() @@ -686,7 +713,7 @@ fn translate_object( BinderObjectRef::Handle(obj) => { let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE; // SAFETY: `handle` is a `u32`; any bit pattern is a valid representation. - let handle = unsafe { obj.__bindgen_anon_1.handle } as _; + let handle = unsafe { obj.__bindgen_anon_1.handle }; let node = self.process.get_node_from_handle(handle, strong)?; security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?; view.transfer_binder_object(offset, obj, strong, node)?; @@ -728,11 +755,12 @@ fn translate_object( let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) { Ok(alloc_offset) => alloc_offset, Err(err) => { - pr_warn!( - "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})", + binder_debug!( + UserError, + "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})", sg_state.unused_buffer_space.offset, sg_state.unused_buffer_space.limit, - obj_length, + obj_length ); return Err(err.into()); } @@ -743,7 +771,7 @@ fn translate_object( ScatterGatherEntry { obj_index, offset: alloc_offset, - sender_uaddr: obj.buffer as _, + sender_uaddr: obj.buffer as usize, length: obj_length, pointer_fixups: KVec::new(), fixup_min_offset: 0, @@ -811,6 +839,7 @@ fn translate_object( let fds_len = num_fds.checked_mul(size_of::()).ok_or(EINVAL)?; if !is_aligned(parent_offset, size_of::()) { + binder_debug!(UserError, "FDA parent offset not aligned correctly"); return Err(EINVAL.into()); } @@ -829,6 +858,7 @@ fn translate_object( }; if !is_aligned(parent_entry.sender_uaddr, size_of::()) { + binder_debug!(UserError, "FDA parent buffer not aligned correctly"); return Err(EINVAL.into()); } @@ -850,7 +880,7 @@ fn translate_object( .ok_or(EINVAL)?; let mut fda_bytes = KVec::new(); - UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len) + UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len) .read_all(&mut fda_bytes, GFP_KERNEL)?; if fds_len != fda_bytes.len() { @@ -912,12 +942,9 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?; if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end { - pr_warn!( - "Fixups oob {} {} {} {}", - fixup_offset, - end_of_previous_fixup, - offset_end, - target_offset_end + binder_debug!( + UserError, + "fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}" ); return Err(EINVAL.into()); } @@ -925,18 +952,21 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let copy_off = end_of_previous_fixup; let copy_len = fixup_offset - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying into alloc: {:?}", err); + binder_debug!(UserError, "failed copying into alloc: {err:?}"); return Err(err.into()); } if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup { let res = alloc.write::(fixup_offset, pointer_value); if let Err(err) = res { - pr_warn!("Failed copying ptr into alloc: {:?}", err); + binder_debug!(UserError, "failed copying ptr into alloc: {err:?}"); return Err(err.into()); } } if let Err(err) = reader.skip(fixup_len) { - pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err); + binder_debug!( + UserError, + "failed skipping {fixup_len} from reader: {err:?}" + ); return Err(err.into()); } end_of_previous_fixup = target_offset_end; @@ -944,7 +974,7 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let copy_off = end_of_previous_fixup; let copy_len = offset_end - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying remainder into alloc: {:?}", err); + binder_debug!(UserError, "failed copying remainder into alloc: {err:?}"); return Err(err.into()); } } @@ -1048,7 +1078,7 @@ pub(crate) fn copy_transaction_data( let offset: usize = offset.try_into().map_err(|_| EINVAL)?; if offset < end_of_previous_object || !is_aligned(offset, size_of::()) { - pr_warn!("Got transaction with invalid offset."); + binder_debug!(UserError, "got transaction with invalid offset"); return Err(EINVAL.into()); } @@ -1073,7 +1103,7 @@ pub(crate) fn copy_transaction_data( ) { Ok(()) => end_of_previous_object = offset + object.size(), Err(err) => { - pr_warn!("Error while translating object."); + binder_debug!(UserError, "error while translating object: {err:?}"); return Err(err); } } @@ -1093,15 +1123,12 @@ pub(crate) fn copy_transaction_data( )?; if let Some(sg_state) = sg_state.as_mut() { - if let Err(err) = self.apply_sg(&mut alloc, sg_state) { - pr_warn!("Failure in apply_sg: {:?}", err); - return Err(err); - } + self.apply_sg(&mut alloc, sg_state)?; } if let Some((off_out, secctx)) = secctx.as_mut() { if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) { - pr_warn!("Failed to write security context: {:?}", err); + binder_debug!(UserError, "failed to write security context: {err:?}"); return Err(err.into()); } **off_out = secctx_off; @@ -1115,6 +1142,12 @@ fn unwind_transaction_stack(self: &Arc) { let mut inner = thread.inner.lock(); inner.pop_transaction_to_reply(thread.as_ref()) } { + binder_debug!( + DeadTransaction, + "release transaction {} in, still active", + transaction.debug_id + ); + let reply = Err(BR_DEAD_REPLY); if !transaction .from @@ -1154,7 +1187,7 @@ fn deliver_single_reply( transaction.set_outstanding(&mut self.process.inner.lock()); } - { + let ret = { let mut inner = self.inner.lock(); if !inner.pop_transaction_replied(transaction) { return false; @@ -1171,15 +1204,16 @@ fn deliver_single_reply( } match reply { - Ok(work) => { - inner.push_work(work); - } + Ok(work) => inner.push_work(work), Err(code) => inner.push_reply_work(code), } - } + }; // Notify the thread now that we've released the inner lock. self.work_condvar.notify_sync(); + if matches!(ret, PushWorkRes::OkNotifyPoll) { + self.process.notify_poll(true); + } false } @@ -1232,7 +1266,7 @@ fn read_transaction_info( info.from_pid = self.process.task.pid(); info.from_tid = self.id; info.code = td.transaction_data.code; - info.flags = td.transaction_data.flags; + info.flags = TransactionFlags::from_bits(td.transaction_data.flags); info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); info.data_size = td.transaction_data.data_size as usize; info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); @@ -1274,16 +1308,36 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); } - pr_warn!( - "{}:{} transaction to {} failed: {err:?}", - info.from_pid, - info.from_tid, - info.to_pid + binder_debug!( + FailedTransaction, + "transaction {} to {}:{} failed {:?}, code {} size {}-{}", + if info.is_reply { + "reply" + } else if info.is_oneway() { + "async" + } else { + "call" + }, + info.to_pid, + info.to_tid, + err, + info.code, + info.data_size, + info.offsets_size ); } } } + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we report it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply != 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } @@ -1294,7 +1348,10 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; - let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; + let list_completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let completion = list_completion.clone_arc(); let transaction = Transaction::new(node_ref, top, self, info)?; @@ -1303,7 +1360,7 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu { let mut inner = self.inner.lock(); if !transaction.is_stacked_on(&inner.current_transaction) { - pr_warn!("Transaction stack changed during transaction!"); + binder_debug!(UserError, "got new transaction with bad transaction stack"); return Err(EINVAL.into()); } inner.current_transaction = Some(transaction.clone_arc()); @@ -1326,8 +1383,18 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu } fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { - let orig = self.inner.lock().pop_transaction_to_reply(self)?; + let orig = match self.inner.lock().pop_transaction_to_reply(self) { + Ok(orig) => orig, + Err(err) => { + binder_debug!(UserError, "got reply transaction with no transaction stack"); + return Err(err.into()); + } + }; if !orig.from.is_current_transaction(&orig) { + binder_debug!( + UserError, + "got reply transaction with bad transaction stack" + ); return Err(EINVAL.into()); } @@ -1336,11 +1403,15 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { - let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; + let completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let process = orig.from.process.clone(); - let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; + let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds); let reply = Transaction::new_reply(self, process, info, allow_fds)?; - self.inner.lock().push_work(completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() @@ -1354,11 +1425,11 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { info.from_tid, info.to_pid ); - let param = err.source.as_ref().map_or(0, |e| e.to_errno()); let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); orig.from .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); + info.reply = BR_FAILED_REPLY; err.reply = BR_TRANSACTION_COMPLETE; err }); @@ -1376,9 +1447,11 @@ fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> Bin } else { BR_TRANSACTION_COMPLETE }; - let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; + let list_completion = + DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?; let completion = list_completion.clone_arc(); - self.inner.lock().push_work(list_completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(list_completion); match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { @@ -1392,7 +1465,7 @@ fn write(self: &Arc, req: &mut BinderWriteRead) -> Result { let write_start = req.write_buffer.wrapping_add(req.write_consumed); let write_len = req.write_size.saturating_sub(req.write_consumed); let mut reader = - UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader(); + UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader(); while reader.len() >= size_of::() && self.inner.lock().return_work.is_unused() { let before = reader.len(); @@ -1463,7 +1536,7 @@ fn read(self: &Arc, req: &mut BinderWriteRead, wait: bool) -> Result { let read_start = req.read_buffer.wrapping_add(req.read_consumed); let read_len = req.read_size.saturating_sub(req.read_consumed); let mut writer = BinderReturnWriter::new( - UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), + UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(), self, ); let (in_pool, has_transaction, thread_todo, use_proc_queue) = { @@ -1527,9 +1600,11 @@ fn read(self: &Arc, req: &mut BinderWriteRead, wait: bool) -> Result { // Write BR_SPAWN_LOOPER if the process needs more threads for its pool. if has_noop_placeholder && in_pool && self.process.needs_thread() { - let mut writer = - UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _) - .writer(); + let mut writer = UserSlice::new( + UserPtr::from_addr(req.read_buffer as usize), + req.read_size as usize, + ) + .writer(); writer.write(&BR_SPAWN_LOOPER)?; } Ok(()) @@ -1578,16 +1653,15 @@ pub(crate) fn write_read(self: &Arc, data: UserSlice, wait: bool) -> Resul ret } - pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) { - table.register_wait(file, &self.work_condvar); + pub(crate) fn poll(&self) -> Result<(bool, u32)> { let mut inner = self.inner.lock(); - (inner.should_use_process_work_queue(), inner.poll()) + Ok((inner.should_use_process_work_queue(), inner.poll())) } /// Make the call to `get_work` or `get_work_local` return immediately, if any. pub(crate) fn exit_looper(&self) { let mut inner = self.inner.lock(); - let should_notify = inner.looper_flags & LOOPER_WAITING != 0; + let should_notify = inner.looper_flags.contains(LooperFlag::Waiting); if should_notify { inner.looper_need_return = true; } @@ -1598,26 +1672,9 @@ pub(crate) fn exit_looper(&self) { } } - pub(crate) fn notify_if_poll_ready(&self, sync: bool) { - // Determine if we need to notify. This requires the lock. - let inner = self.inner.lock(); - let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue(); - drop(inner); - - // Now that the lock is no longer held, notify the waiters if we have to. - if notify { - if sync { - self.work_condvar.notify_sync(); - } else { - self.work_condvar.notify_one(); - } - } - } - pub(crate) fn release(self: &Arc) { self.inner.lock().is_dead = true; - //self.work_condvar.clear(); self.unwind_transaction_stack(); // Cancel all pending work items. @@ -1630,14 +1687,16 @@ pub(crate) fn release(self: &Arc) { #[pin_data] struct ThreadError { error_code: Atomic, + pid: i32, #[pin] links_track: AtomicTracker, } impl ThreadError { - fn try_new() -> Result> { + fn try_new(pid: i32) -> Result> { DTRWrap::arc_pin_init(pin_init!(Self { error_code: Atomic::new(BR_OK), + pid, links_track <- AtomicTracker::new(), })) .map(ListArc::into_arc) @@ -1664,7 +1723,16 @@ fn do_work( Ok(true) } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + let code = self.error_code.load(Relaxed); + if code != BR_OK { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_ERROR: {code}" + ); + } + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 5539672d7285..06aabb3cc2f1 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -4,6 +4,8 @@ use crate::transaction::Transaction; +use core::ptr; + use kernel::bindings::{rust_binder_transaction, task_struct}; use kernel::error::Result; use kernel::ffi::{c_int, c_uint, c_ulong}; @@ -26,7 +28,7 @@ #[inline] fn raw_transaction(t: &Transaction) -> rust_binder_transaction { - t as *const Transaction as rust_binder_transaction + ptr::from_ref(t).cast_mut().cast() } #[inline] diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 0e5d07b7e6f0..245f1556b5db 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. use kernel::{ + net::netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -11,12 +12,14 @@ task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, + uapi, }; use crate::{ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -24,6 +27,33 @@ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +kernel::impl_flags!( + /// Represents multiple transaction flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)] + pub struct TransactionFlags(u32); + + /// Represents a single transaction flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum TransactionFlag { + OneWay = TF_ONE_WAY, + AcceptFds = TF_ACCEPT_FDS, + ClearBuf = TF_CLEAR_BUF, + UpdateTxn = TF_UPDATE_TXN, + } +); + +impl TransactionFlags { + /// Creates a `TransactionFlags` from a raw `u32` value. + pub(crate) fn from_bits(bits: u32) -> Self { + Self(bits) + } + + /// Checks if the Oneway flag is set. + pub(crate) fn is_oneway(self) -> bool { + self.contains(TransactionFlag::OneWay) + } +} + #[derive(Zeroable)] pub(crate) struct TransactionInfo { pub(crate) from_pid: Pid, @@ -31,7 +61,7 @@ pub(crate) struct TransactionInfo { pub(crate) to_pid: Pid, pub(crate) to_tid: Pid, pub(crate) code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, pub(crate) data_ptr: UserPtr, pub(crate) data_size: usize, pub(crate) offsets_ptr: UserPtr, @@ -48,7 +78,45 @@ pub(crate) struct TransactionInfo { impl TransactionInfo { #[inline] pub(crate) fn is_oneway(&self) -> bool { - self.flags & TF_ONE_WAY != 0 + self.flags.is_oneway() + } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) = self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid != 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid != 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(u32::from(self.flags))?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) } } @@ -74,7 +142,7 @@ pub(crate) struct Transaction { allocation: SpinLock>, is_outstanding: Atomic, code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, data_size: usize, offsets_size: usize, data_address: usize, @@ -120,7 +188,7 @@ pub(crate) fn new( } alloc.set_info_oneway_node(node_ref.node.clone()); } - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -160,7 +228,7 @@ pub(crate) fn new_reply( return Err(err); } }; - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -193,7 +261,7 @@ pub(crate) fn debug_print_inner(&self, m: &SeqFile, prefix: &str) { self.from.id, self.to.task.pid(), self.code, - self.flags, + u32::from(self.flags), self.start_time.elapsed().as_millis(), ); if let Some(target_node) = &self.target_node { @@ -272,7 +340,7 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes let _t_outdated; let _oneway_node; - let oneway = self.flags & TF_ONE_WAY != 0; + let oneway = self.flags.is_oneway(); let process = self.to.clone(); let mut process_inner = process.inner.lock(); @@ -283,7 +351,7 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes crate::trace::trace_transaction(false, &self, None); if process_inner.is_frozen.is_frozen() { process_inner.async_recv = true; - if self.flags & TF_UPDATE_TXN != 0 { + if self.flags.contains(TransactionFlag::UpdateTxn) { if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { @@ -330,11 +398,15 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + process.notify_poll(true); + Ok(()) + } PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)), } } else { crate::trace::trace_transaction(false, &self, None); - process_inner.push_work(self) + process_inner.push_work(&process, self) }; drop(process_inner); @@ -354,7 +426,8 @@ pub(crate) fn can_replace(&self, old: &Transaction) -> bool { return false; } - if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) { + let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn; + if !(self.flags.contains_all(required) && old.flags.contains_all(required)) { return false; } @@ -391,7 +464,7 @@ fn do_work( writer: &mut BinderReturnWriter<'_>, ) -> Result { let send_failed_reply = ScopeGuard::new(|| { - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_FAILED_REPLY); self.from.deliver_reply(reply, &self, None); } @@ -403,6 +476,14 @@ fn do_work( } else { // On failure to process the list, we send a reply back to the sender and ignore the // transaction on the recipient. + binder_debug!( + FailedTransaction, + "transaction {} to {} failed, fd fixups failed, size {}-{}", + self.debug_id, + self.to.task.pid(), + self.data_size, + self.offsets_size + ); return Ok(true); }; @@ -410,20 +491,21 @@ fn do_work( let tr = tr_sec.tr_data(); if let Some(target_node) = &self.target_node { let (ptr, cookie) = target_node.get_id(); - tr.target.ptr = ptr as _; - tr.cookie = cookie as _; + tr.target.ptr = ptr as uapi::binder_uintptr_t; + tr.cookie = cookie as uapi::binder_uintptr_t; }; tr.code = self.code; - tr.flags = self.flags; - tr.data_size = self.data_size as _; - tr.data.ptr.buffer = self.data_address as _; - tr.offsets_size = self.offsets_size as _; + tr.flags = u32::from(self.flags); + tr.data_size = self.data_size as uapi::binder_size_t; + tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t; + tr.offsets_size = self.offsets_size as uapi::binder_size_t; if tr.offsets_size > 0 { - tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _; + tr.data.ptr.offsets = + (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t; } tr.sender_euid = self.sender_euid.into_uid_in_current_ns(); tr.sender_pid = 0; - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { // Not a reply and not one-way. tr.sender_pid = self.from.process.pid_in_current_ns(); } @@ -475,16 +557,23 @@ fn cancel(self: DArc) { drop(allocation); // If this is not a reply or oneway transaction, then send a dead reply. - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_DEAD_REPLY); self.from.deliver_reply(reply, &self, None); + } else { + binder_debug!( + pid = self.to.task.pid(), + DeadTransaction, + "undelivered transaction {}, process died", + self.debug_id + ); } self.drop_outstanding_txn(); } fn should_sync_wakeup(&self) -> bool { - self.flags & TF_ONE_WAY == 0 + !self.flags.is_oneway() } fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> { diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 70701d3bc81c..9f9c72727a05 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -401,9 +401,14 @@ static int cache_setup_properties(unsigned int cpu) else if (!acpi_disabled) ret = cache_setup_acpi(cpu); - // Assume there is no cache information available in DT/ACPI from now. - if (ret && use_arch_cache_info()) + /* + * No DT/ACPI cache nodes; fall back to arch-derived topology (e.g. + * arm64 CLIDR_EL1) and clear the error to avoid a spurious warning. + */ + if (ret && use_arch_cache_info()) { use_arch_info = true; + ret = 0; + } return ret; } diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index b1213786f72c..db7ee6547eef 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1026,26 +1026,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl) struct mhi_ep_chan *mhi_chan; int i; - /* Stop all the channels */ + /* Disable all the channels to prevent new transfers */ + for (i = 0; i < mhi_cntrl->max_chan; i++) { + mhi_chan = &mhi_cntrl->mhi_chan[i]; + if (!mhi_chan->ring.started) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Drain ring workers and in-flight transfers before notifying disconnect */ + flush_workqueue(mhi_cntrl->wq); + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + + /* Send channel disconnect status to client drivers */ for (i = 0; i < mhi_cntrl->max_chan; i++) { mhi_chan = &mhi_cntrl->mhi_chan[i]; if (!mhi_chan->ring.started) continue; mutex_lock(&mhi_chan->lock); - /* Send channel disconnect status to client drivers */ if (mhi_chan->xfer_cb) { result.transaction_status = -ENOTCONN; result.bytes_xferd = 0; mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - - mhi_chan->state = MHI_CH_STATE_DISABLED; mutex_unlock(&mhi_chan->lock); } - flush_workqueue(mhi_cntrl->wq); - /* Destroy devices associated with all channels */ device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device); @@ -1340,14 +1351,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id) ret = dev_set_name(&mhi_dev->dev, "%s_%s", dev_name(&mhi_cntrl->mhi_dev->dev), mhi_dev->name); - if (ret) { - put_device(&mhi_dev->dev); - return ret; - } + if (ret) + goto err_put_channels; ret = device_add(&mhi_dev->dev); if (ret) - put_device(&mhi_dev->dev); + goto err_put_channels; + + return 0; + +err_put_channels: + put_device(&mhi_dev->dev); /* DL channel reference */ + put_device(&mhi_dev->dev); /* UL channel reference */ + put_device(&mhi_dev->dev); /* device_initialize() reference */ return ret; } @@ -1614,6 +1630,7 @@ static void mhi_ep_remove(struct device *dev) { struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver); + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_result result = {}; struct mhi_ep_chan *mhi_chan; int dir; @@ -1622,6 +1639,22 @@ static void mhi_ep_remove(struct device *dev) if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) return; + /* Disable the channels to prevent new transfers */ + for (dir = 0; dir < 2; dir++) { + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + + if (!mhi_chan) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Flush in-flight transfers before notifying disconnect */ + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + /* Disconnect the channels associated with the driver */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; @@ -1637,7 +1670,6 @@ static void mhi_ep_remove(struct device *dev) mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - mhi_chan->state = MHI_CH_STATE_DISABLED; mhi_chan->xfer_cb = NULL; mutex_unlock(&mhi_chan->lock); } diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 12dcb1a2753c..fd3050889412 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1029,7 +1029,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, if (mhi_cntrl->edl_trigger) { ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr); if (ret) - goto err_release_dev; + goto err_del_dev; } mhi_cntrl->mhi_dev = mhi_dev; @@ -1038,6 +1038,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, return 0; +err_del_dev: + device_del(&mhi_dev->dev); err_release_dev: put_device(&mhi_dev->dev); error_setup_irq: diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 53c0ffe30070..4d458396233a 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state); void mhi_soc_reset(struct mhi_controller *mhi_cntrl) { + int __maybe_unused ret; + u32 tmp; + if (mhi_cntrl->reset) { mhi_cntrl->reset(mhi_cntrl); return; @@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl) /* Generic MHI SoC reset */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ); + /* Flush the posted write to the device (ignore return value) */ + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, + &tmp); } EXPORT_SYMBOL_GPL(mhi_soc_reset); diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 0d0d9c7ffa4b..b636e2c23b4d 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = { static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), @@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), @@ -1395,6 +1399,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width); mhi_cntrl->fw_image = info->fw; mhi_cntrl->edl_image = info->edl; + mhi_cntrl->no_m3 = info->no_m3; mhi_cntrl->read_reg = mhi_pci_read_reg; mhi_cntrl->write_reg = mhi_pci_write_reg; diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index f799503c8f36..ced83fbb1a51 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) /* Trigger MHI RESET so that the device will not access host memory */ if (reset_device) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - &in_reset) || - !in_reset, timeout); - if (!ret || in_reset) { + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms); + if (ret) { dev_err(dev, "Device failed to exit MHI Reset state\n"); write_lock_irq(&mhi_cntrl->pm_lock); cur_state = mhi_tryset_pm_state(mhi_cntrl, @@ -922,22 +914,39 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) return -EIO; } - /* Set MHI to M3 and wait for completion */ - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); - write_unlock_irq(&mhi_cntrl->pm_lock); - dev_dbg(dev, "Waiting for M3 completion\n"); + /* + * For devices without M3 support, just set the host state to M3. This + * host transition is needed to prevent the client drivers from + * accessing the device during suspend. + */ + if (mhi_cntrl->no_m3) { + new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + if (new_state != MHI_PM_M3) { + dev_err(dev, + "Error setting to PM state: %s from: %s\n", + to_mhi_pm_state_str(MHI_PM_M3), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } + } else { + /* Set MHI to M3 and wait for completion */ + mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + dev_dbg(dev, "Waiting for M3 completion\n"); - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M3 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); + ret = wait_event_timeout(mhi_cntrl->state_event, + mhi_cntrl->dev_state == MHI_STATE_M3 || + MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), + msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, - "Did not enter M3 state, MHI state: %s, PM state: %s\n", - mhi_state_str(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; + if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + dev_err(dev, + "Did not enter M3 state, MHI state: %s, PM state: %s\n", + mhi_state_str(mhi_cntrl->dev_state), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } } /* Notify clients about entering LPM */ @@ -969,7 +978,8 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) return -EIO; - if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { + if (!mhi_cntrl->no_m3 && + mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { dev_warn(dev, "Resuming from non M3 state (%s)\n", mhi_state_str(mhi_get_mhi_state(mhi_cntrl))); if (!force) @@ -995,6 +1005,15 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) return -EIO; } + /* + * For devices without M3 support, just move the host back to M0 + * directly. + */ + if (mhi_cntrl->no_m3) { + write_unlock_irq(&mhi_cntrl->pm_lock); + return mhi_pm_m0_transition(mhi_cntrl); + } + /* Set MHI to M0 and wait for completion */ mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0); write_unlock_irq(&mhi_cntrl->pm_lock); diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index d3d230247262..a118a89a7779 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) sysfs_bin_attr_init(res_attr); - cdx_dev->res_attr[num] = res_attr; sprintf(res_attr_name, "resource%d", num); res_attr->mmap = cdx_mmap_resource; @@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) res_attr->size = cdx_resource_len(cdx_dev, num); res_attr->private = (void *)(unsigned long)num; ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr); - if (ret) + if (ret) { kfree(res_attr); + return ret; + } + + cdx_dev->res_attr[num] = res_attr; return ret; } diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c index 63175b765c90..a98acb199805 100644 --- a/drivers/char/powernv-op-panel.c +++ b/drivers/char/powernv-op-panel.c @@ -89,7 +89,6 @@ static int __op_panel_update_display(void) static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, size_t len, loff_t *f_pos) { - loff_t f_pos_prev = *f_pos; ssize_t ret; int rc; @@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, if (rc != OPAL_SUCCESS) { pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n", rc); - *f_pos = f_pos_prev; return -EIO; } } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 6da817b9849f..8803268b4cdc 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode) return IEEE1284_PH_FWD_IDLE; } -static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) +static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec) { + struct timespec64 ts; long to_jiffies; - if ((tv_sec < 0) || (tv_usec < 0)) + if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC) return -EINVAL; - to_jiffies = usecs_to_jiffies(tv_usec); - to_jiffies += tv_sec * HZ; + ts.tv_sec = tv_sec; + ts.tv_nsec = tv_usec * NSEC_PER_USEC; + to_jiffies = timespec64_to_jiffies(&ts); if (to_jiffies <= 0) return -EINVAL; diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index dd45fe5eb6f2..255f69123af5 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t store_received_ref_clk3a(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_received_ref_clk3b(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_enable_clk3b_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_amcb1_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_redundant_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_ref_frequency(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_filter_select(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); static ssize_t store_hardware_switching_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_hardware_switching(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_refalign (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned long flags; sscanf(buf, "%lX", &tmp); @@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); static ssize_t store_mode_select (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); static ssize_t store_reset (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index e2b7451ea7cc..8d5179367eac 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -506,11 +506,8 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, ret = devm_request_irq(dev, irq, tpm_ioserirq_handler, IRQF_TRIGGER_HIGH, "TPM SERIRQ management", chip); - if (ret < 0) { - dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n", - irq); + if (ret < 0) goto _tpm_clean_answer; - } intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_STS_VALID_INT diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index d44903b29929..129aa222cc25 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -574,8 +574,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client) dev_name(&chip->dev), chip); if (rc) { - dev_err(dev, "%s() Unable to request irq: %d for use\n", - __func__, priv->irq); priv->irq = 0; } else { chip->flags |= TPM_CHIP_FLAG_IRQ; diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index b48cacacc066..7f828fae70d3 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -751,10 +751,8 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) rc = devm_request_irq(dev, client->irq, tpm_cr50_i2c_int_handler, IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, dev->driver->name, chip); - if (rc < 0) { - dev_err(dev, "Failed to probe IRQ %d\n", client->irq); + if (rc < 0) return rc; - } priv->irq = client->irq; } else { diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 198b97314168..62eecfa61646 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -157,6 +157,12 @@ struct ports_device { /* Major number for this device. Ports will be created as minors. */ int chr_major; + + /* + * Set to true during PM freeze to block TX paths that may race + * with virtqueue teardown (e.g. hvc put_chars with no_console_suspend). + */ + bool pm_freezing; }; struct port_stats { @@ -304,6 +310,12 @@ static struct port *find_port_by_id(struct ports_device *portdev, u32 id) return port; } +/* + * Finds a port by the virtqueue and returns a pointer to struct port + * with the reference count incremented. + * + * Callers MUST decrement it when finished. + */ static struct port *find_port_by_vq(struct ports_device *portdev, struct virtqueue *vq) { @@ -312,8 +324,10 @@ static struct port *find_port_by_vq(struct ports_device *portdev, spin_lock_irqsave(&portdev->ports_lock, flags); list_for_each_entry(port, &portdev->ports, list) - if (port->in_vq == vq || port->out_vq == vq) + if (port->in_vq == vq || port->out_vq == vq) { + kref_get(&port->kref); goto out; + } port = NULL; out: spin_unlock_irqrestore(&portdev->ports_lock, flags); @@ -402,7 +416,7 @@ static void reclaim_dma_bufs(void) } static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size, - int pages) + int pages, gfp_t gfp) { struct port_buffer *buf; @@ -436,11 +450,10 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size /* Increase device refcnt to avoid freeing it */ get_device(buf->dev); - buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, - GFP_KERNEL); + buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, gfp); } else { buf->dev = NULL; - buf->buf = kmalloc(buf_size, GFP_KERNEL); + buf->buf = kmalloc(buf_size, gfp); } if (!buf->buf) @@ -595,27 +608,46 @@ static void reclaim_consumed_buffers(struct port *port) static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, int nents, size_t in_count, - void *data, bool nonblock) + struct port_buffer *buf, bool nonblock) { struct virtqueue *out_vq; int err; unsigned long flags; unsigned int len; - - out_vq = port->out_vq; + struct ports_device *portdev; spin_lock_irqsave(&port->outvq_lock, flags); + portdev = READ_ONCE(port->portdev); + + if (!portdev) { + in_count = 0; + goto free_and_done; + } + + /* + * Check freeze flag under the lock so that the flag check and + * virtqueue_add_outbuf() are atomic with respect to + * remove_port_data() which also takes outvq_lock. This + * guarantees that once remove_port_data() returns, no new + * buffers can be added before remove_vqs() tears down the vq. + * Pairs with smp_store_release() in virtcons_freeze/restore. + */ + if (smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ + goto free_and_done; + + out_vq = port->out_vq; + reclaim_consumed_buffers(port); - err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC); + err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC); /* Tell Host to go! */ virtqueue_kick(out_vq); if (err) { in_count = 0; - goto done; + goto free_and_done; } if (out_vq->num_free == 0) @@ -632,10 +664,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, * buffer and relax the spinning requirement. The downside is * we need to kmalloc a GFP_ATOMIC buffer each time the * console driver writes something out. + * + * Spin until host returns the buffer. + * Capture the returned buf so we can free it. + * If broken, buf == NULL and buf stays in the vq; + * remove_vqs() will call virtqueue_detach_unused_buf() -> free_buf(). */ - while (!virtqueue_get_buf(out_vq, &len) + while (!(buf = virtqueue_get_buf(out_vq, &len)) && !virtqueue_is_broken(out_vq)) cpu_relax(); + +free_and_done: + if (buf) + free_buf(buf, false); done: spin_unlock_irqrestore(&port->outvq_lock, flags); @@ -816,14 +857,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, count = min((size_t)(32 * 1024), count); - buf = alloc_buf(port->portdev->vdev, count, 0); + buf = alloc_buf(port->portdev->vdev, count, 0, GFP_KERNEL); if (!buf) return -ENOMEM; ret = copy_from_user(buf->buf, ubuf, count); if (ret) { - ret = -EFAULT; - goto free_buf; + free_buf(buf, true); + return -EFAULT; } /* @@ -835,15 +876,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, */ nonblock = true; sg_init_one(sg, buf->buf, count); - ret = __send_to_port(port, sg, 1, count, buf, nonblock); - - if (nonblock && ret > 0) - goto out; - -free_buf: - free_buf(buf, true); -out: - return ret; + return __send_to_port(port, sg, 1, count, buf, nonblock); } struct sg_list { @@ -932,7 +965,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, goto error_out; occupancy = pipe_buf_usage(pipe); - buf = alloc_buf(port->portdev->vdev, 0, occupancy); + buf = alloc_buf(port->portdev->vdev, 0, occupancy, GFP_KERNEL); if (!buf) { ret = -ENOMEM; @@ -946,11 +979,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, sg_init_table(sgl.sg, sgl.size); ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); pipe_unlock(pipe); + if (likely(ret > 0)) ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true); - - if (unlikely(ret <= 0)) + else free_buf(buf, true); + return ret; error_out: @@ -1108,21 +1142,50 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) { struct port *port; struct scatterlist sg[1]; - void *data; - int ret; + struct port_buffer *pbuf; + struct ports_device *portdev; port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; - data = kmemdup(buf, count, GFP_ATOMIC); - if (!data) + /* + * Silently drop output in two cases, both by returning count so + * that the hvc layer does not spin-retry: + * + * 1. Device hot-unplug (!portdev): portdev was NULLed by + * unplug_port() after hvc_remove() was already called, so + * the hvc layer will stop invoking put_chars() very soon. + * Returning count avoids a pointless retry loop in the + * interim. + * + * 2. PM freeze (pm_freezing): the hvc console stays active + * under no_console_suspend but virtqueues are being torn + * down. Drop the output silently so the hvc layer does not + * stall suspend. + * + * This early check avoids a pointless GFP_ATOMIC allocation; + * __send_to_port() rechecks under outvq_lock for correctness. + * Pairs with smp_store_release() in virtcons_freeze/restore. + */ + portdev = READ_ONCE(port->portdev); + if (!portdev || + smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ + return count; + + pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC); + if (!pbuf) return -ENOMEM; - sg_init_one(sg, data, count); - ret = __send_to_port(port, sg, 1, count, data, false); - kfree(data); - return ret; + memcpy(pbuf->buf, buf, count); + pbuf->len = count; + sg_init_one(sg, pbuf->buf, count); + + /* + * Ownership of pbuf is transferred to __send_to_port(). + * Do not touch or free pbuf after this call. + */ + return __send_to_port(port, sg, 1, count, pbuf, false); } /* @@ -1295,7 +1358,7 @@ static int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(vq->vdev, PAGE_SIZE, 0); + buf = alloc_buf(vq->vdev, PAGE_SIZE, 0, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -1498,11 +1561,16 @@ static void unplug_port(struct port *port) remove_port_data(port); /* - * We should just assume the device itself has gone off -- - * else a close on an open port later will try to send out a - * control message. + * Null out portdev under outvq_lock so that __send_to_port() + * cannot race: it checks port->portdev inside the same lock + * and bails out if NULL, preventing any buffer from being + * enqueued to an already torn-down virtqueue. Also prevents + * a close on an open port later from sending a stale control + * message. */ + spin_lock_irq(&port->outvq_lock); port->portdev = NULL; + spin_unlock_irq(&port->outvq_lock); sysfs_remove_group(&port->dev->kobj, &port_attribute_group); device_destroy(&port_class, port->dev->devt); @@ -1536,7 +1604,8 @@ static void handle_control_message(struct virtio_device *vdev, cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, - "Invalid index %u in control packet\n", cpkt->id); + "Invalid index %u in control packet\n", + virtio32_to_cpu(vdev, cpkt->id)); return; } @@ -1553,7 +1622,8 @@ static void handle_control_message(struct virtio_device *vdev, dev_warn(&portdev->vdev->dev, "Request for adding port with " "out-of-bound id %u, max. supported id: %u\n", - cpkt->id, portdev->max_nr_ports - 1); + virtio32_to_cpu(vdev, cpkt->id), + portdev->max_nr_ports - 1); break; } add_port(portdev, virtio32_to_cpu(vdev, cpkt->id)); @@ -1706,6 +1776,7 @@ static void out_intr(struct virtqueue *vq) } wake_up_interruptible(&port->waitqueue); + kref_put(&port->kref, remove_port); } static void in_intr(struct virtqueue *vq) @@ -1751,6 +1822,8 @@ static void in_intr(struct virtqueue *vq) if (is_console_port(port) && hvc_poll(port->cons.hvc)) hvc_kick(); + + kref_put(&port->kref, remove_port); } static void control_intr(struct virtqueue *vq) @@ -1980,6 +2053,7 @@ static int virtcons_probe(struct virtio_device *vdev) /* Attach this portdev to this virtio_device, and vice-versa. */ portdev->vdev = vdev; vdev->priv = portdev; + portdev->pm_freezing = false; portdev->chr_major = register_chrdev(0, "virtio-portsdev", &portdev_fops); @@ -2099,9 +2173,30 @@ static int virtcons_freeze(struct virtio_device *vdev) { struct ports_device *portdev; struct port *port; + unsigned long flags; portdev = vdev->priv; + /* + * Block TX paths (put_chars, __send_to_port) before resetting the + * device and tearing down virtqueues. This prevents races with + * hvc console writes that remain active under no_console_suspend. + */ + smp_store_release(&portdev->pm_freezing, true); + + /* + * Synchronize with any concurrent __send_to_port() that may have + * passed the pm_freezing check. By acquiring and releasing the + * outvq_lock for each port, we ensure all active TX paths have + * completed before we reset the device. + */ + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) { + spin_lock(&port->outvq_lock); + spin_unlock(&port->outvq_lock); + } + spin_unlock_irqrestore(&portdev->ports_lock, flags); + virtio_reset_device(vdev); if (use_multiport(portdev)) @@ -2144,9 +2239,6 @@ static int virtcons_restore(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); - if (use_multiport(portdev)) - fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); - list_for_each_entry(port, &portdev->ports, list) { port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; @@ -2163,6 +2255,24 @@ static int virtcons_restore(struct virtio_device *vdev) if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); } + + /* + * Populate the control receive queue only after the list iteration + * is complete. If we fill this queue before iterating, the host could + * immediately deliver a VIRTIO_CONSOLE_PORT_REMOVE message. + * This would trigger the control workqueue, which modifies the + * portdev->ports list concurrently with the unprotected loop above, + * leading to a Use-After-Free and list corruption. + */ + if (use_multiport(portdev)) + fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); + + /* + * Allow TX paths only after all port->out_vq pointers have + * been reassigned to the newly allocated virtqueues. + */ + smp_store_release(&portdev->pm_freezing, false); + return 0; } #endif diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 34a345dc5e72..9bb5fa642fd8 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -760,7 +760,7 @@ static int __init hwicap_module_init(void) HWICAP_DEVICES, DRIVER_NAME); if (retval < 0) - return retval; + goto failed_class; retval = platform_driver_register(&hwicap_platform_driver); if (retval) @@ -771,6 +771,9 @@ static int __init hwicap_module_init(void) failed: unregister_chrdev_region(devt, HWICAP_DEVICES); + failed_class: + class_unregister(&icap_class); + return retval; } diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 1a1e64133315..46e1046abfca 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op) rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register IRQ handler. Aborting.\n"); + if (rc) return -ENODEV; - } return xillybus_endpoint_discovery(endpoint); } diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index 9858711e3e79..32064b6c7627 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev, } rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register MSI handler. Aborting.\n"); + if (rc) return -ENODEV; - } /* * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index d5d18fa2c638..52385b14a9a4 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d) unsigned int status; unsigned int val; + if (!dev->attached) + return IRQ_NONE; + status = inb(dev->iobase + AIO_IIRO_16_STATUS); if (!(status & AIO_IIRO_16_STATUS_IRQE)) return IRQ_NONE; diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index b6563a48ada6..fd36f820979c 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -224,10 +224,10 @@ static void c6xdigio_init(struct comedi_device *dev) static const struct pnp_device_id c6xdigio_pnp_tbl[] = { /* Standard LPT Printer Port */ - {.id = "PNP0400", .driver_data = 0}, + { .id = "PNP0400" }, /* ECP Printer Port */ - {.id = "PNP0401", .driver_data = 0}, - {} + { .id = "PNP0401" }, + { } }; static struct pnp_driver c6xdigio_pnp_driver = { diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 516a5d5a2840..384e9ba8d5df 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -173,10 +173,16 @@ static irqreturn_t das6402_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; + if (!dev->attached) + return IRQ_NONE; + + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DAS6402_STATUS_REG); if ((status & DAS6402_STATUS_INT) == 0) return IRQ_NONE; diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index bcc4b5ef48e8..0438b8c90e44 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -193,13 +193,16 @@ static irqreturn_t dt2811_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; if (!dev->attached) return IRQ_NONE; + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DT2811_ADCSR_REG); if (status & DT2811_ADCSR_ADERROR) { diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index 44221c928e32..86629b495fed 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -132,11 +132,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) struct comedi_device *dev = d; struct a2150_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[0]; + struct comedi_isadma_desc *desc; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned short *buf = desc->virt_addr; + struct comedi_async *async; + struct comedi_cmd *cmd; + unsigned short *buf; unsigned int max_points, num_points, residue, leftover; unsigned short dpnt; int status; @@ -145,6 +145,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) if (!dev->attached) return IRQ_HANDLED; + desc = &dma->desc[0]; + async = s->async; + cmd = &async->cmd; + buf = desc->virt_addr; + status = inw(dev->iobase + STATUS_REG); if ((status & INTR_BIT) == 0) return IRQ_NONE; diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index 7bc336333ace..537301eee9bf 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -215,13 +215,13 @@ static const int ni_irqpin[] = { #include "ni_mio_common.c" -static const struct pnp_device_id __maybe_unused device_ids[] = { - {.id = "NIC1900", .driver_data = 0}, - {.id = "NIC2400", .driver_data = 0}, - {.id = "NIC2500", .driver_data = 0}, - {.id = "NIC2600", .driver_data = 0}, - {.id = "NIC2700", .driver_data = 0}, - {.id = ""} +static const struct pnp_device_id device_ids[] = { + { .id = "NIC1900" }, + { .id = "NIC2400" }, + { .id = "NIC2500" }, + { .id = "NIC2600" }, + { .id = "NIC2700" }, + { } }; MODULE_DEVICE_TABLE(pnp, device_ids); diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index b2772d909896..6765cdc276ca 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -223,6 +223,9 @@ static irqreturn_t atmio16d_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; unsigned short val; + if (!dev->attached) + return IRQ_NONE; + val = inw(dev->iobase + AD_FIFO_REG); comedi_buf_write_samples(s, &val, 1); comedi_handle_events(dev, s); diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index 4be9ca4f4828..299e2a0e9474 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -685,17 +685,18 @@ static const struct ni_board_struct ni_boards[] = { .name = "pci-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PXI6220] = { .name = "pxi-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index 5d2c4b2aa3bb..8ad5789966f0 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -184,7 +184,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; unsigned short data; if (!dev->attached) { @@ -192,6 +192,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) return IRQ_HANDLED; } + cmd = &s->async->cmd; data = pcl711_ai_get_sample(dev, s); outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index 1fcb2f798c7a..b0d30cb1cfe9 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -242,7 +242,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; struct pcl816_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; + struct comedi_isadma_desc *desc; unsigned int nsamples; unsigned int bufptr; @@ -257,6 +257,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) return IRQ_HANDLED; } + desc = &dma->desc[dma->cur_dma]; nsamples = comedi_bytes_to_samples(s, desc->size) - devpriv->ai_poll_ptr; bufptr = devpriv->ai_poll_ptr; diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index aa775a024fc7..89f32c493caa 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -534,13 +534,14 @@ static irqreturn_t pcl818_interrupt(int irq, void *d) struct comedi_device *dev = d; struct pcl818_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; if (!dev->attached || !devpriv->ai_cmd_running) { pcl818_ai_clear_eoc(dev); return IRQ_HANDLED; } + cmd = &s->async->cmd; if (devpriv->ai_cmd_canceled) { /* * The cleanup from ai_cancel() has been delayed diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index d38202c8a12b..f42b7343b4e4 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -362,6 +362,9 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d) unsigned int triggered; unsigned char int_pend; + if (!dev->attached) + return IRQ_NONE; + /* are there any interrupts pending */ int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07; if (!int_pend) diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index 0995911a3ea3..d9995cbeecb6 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -362,12 +362,15 @@ static irqreturn_t pcmuio_interrupt(int irq, void *d) struct pcmuio_private *devpriv = dev->private; int handled = 0; + if (!dev->attached) + return IRQ_NONE; + if (irq == dev->irq) handled += pcmuio_handle_asic_interrupt(dev, 0); if (irq == devpriv->irq2) handled += pcmuio_handle_asic_interrupt(dev, 1); - return handled ? IRQ_HANDLED : IRQ_NONE; + return IRQ_RETVAL(handled); } /* chip->spinlock is already locked */ diff --git a/drivers/comedi/drivers/quatech_daqp_cs.c b/drivers/comedi/drivers/quatech_daqp_cs.c index 2a76c75c513b..d5e3e213e233 100644 --- a/drivers/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/comedi/drivers/quatech_daqp_cs.c @@ -211,13 +211,15 @@ static irqreturn_t daqp_interrupt(int irq, void *dev_id) { struct comedi_device *dev = dev_id; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; int loop_limit = 10000; int status; if (!dev->attached) return IRQ_NONE; + cmd = &s->async->cmd; + status = inb(dev->iobase + DAQP_STATUS_REG); if (!(status & DAQP_STATUS_EVENTS)) return IRQ_NONE; diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index a3d8f7a5874e..9eee281350d0 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -759,11 +759,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) /* All events reported through the global interrupt */ ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[0], ret); + if (ret) return ret; - } } else { for (i = 0; i < priv->nr_irqs; i++) { /* @@ -775,11 +772,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[i], ret); + if (ret) return ret; - } } } diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c index f69b6920463f..d77a256f0ed3 100644 --- a/drivers/counter/ti-ecap-capture.c +++ b/drivers/counter/ti-ecap-capture.c @@ -513,7 +513,7 @@ static int ecap_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; platform_set_drvdata(pdev, counter_dev); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index d9302ec21163..0dda3f2b14c7 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -526,7 +526,7 @@ static int ti_eqep_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler, IRQF_ONESHOT, dev_name(dev), counter); if (err < 0) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; counter->name = dev_name(dev); counter->parent = dev; diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 79ab44fe26aa..73c4ba3dc0bc 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -393,7 +394,7 @@ static int __init eisa_probe(struct eisa_root_device *root) } } - dev_info(root->dev, "EISA: Detected %d card%s\n", c, c == 1 ? "" : "s"); + dev_info(root->dev, "EISA: Detected %d card%s\n", c, str_plural(c)); return 0; } diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 7e3c9f38297b..e735f43dcdeb 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -210,3 +210,4 @@ module_platform_driver(adc_jack_driver); MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("ADC Jack extcon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index daddb5224794..b360f752d191 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -7,17 +7,28 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include -#include -#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32) +#define RSU_STATE_MASK GENMASK_ULL(31, 0) +#define RSU_VERSION_MASK GENMASK_ULL(63, 32) +#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) +#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) +/* + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as: + * [63:32] erase_size, [31:0] size (see stratix10-smc.h). + */ +#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0) +#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32) + #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) @@ -33,11 +44,32 @@ #define INVALID_DCMF_VERSION 0xFF #define INVALID_DCMF_STATUS 0xFFFFFFFF #define INVALID_SPT_ADDRESS 0x0 +#define INVALID_DEVICE_INFO (~0U) #define RSU_RETRY_SLEEP_MS (1U) #define RSU_ASYNC_MSG_RETRY (3U) +#define RSU_GET_SPT_CMD 0x5A #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) +struct flash_device_info { + unsigned int size; + unsigned int erase_size; +}; + +/** + * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word + * @di: slot to fill + * @packed: register value: [63:32] erase_size, [31:0] size + * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO) + */ +static void rsu_device_info_set_from_packed(struct flash_device_info *di, + unsigned long packed) +{ + di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed); + di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK, + packed); +} + typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); /** @@ -46,6 +78,7 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @client: active service client * @completion: state for callback completion * @lock: a mutex to protect callback completion state + * @async: supports async operations * @status.current_image: address of image currently running in flash * @status.fail_image: address of failed image in flash * @status.version: the interface version number of RSU firmware @@ -60,16 +93,20 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @dcmf_status.dcmf1: dcmf1 status * @dcmf_status.dcmf2: dcmf2 status * @dcmf_status.dcmf3: dcmf3 status + * @device_info: per-device flash information array; each entry contains + * size and erase size for one flash device * @retry_counter: the current image's retry counter * @max_retry: the preset max retry value * @spt0_address: address of spt0 * @spt1_address: address of spt1 + * @get_spt_response_buf: response from sdm for get_spt command */ struct stratix10_rsu_priv { struct stratix10_svc_chan *chan; struct stratix10_svc_client client; struct completion completion; struct mutex lock; + bool async; struct { unsigned long current_image; unsigned long fail_image; @@ -93,16 +130,73 @@ struct stratix10_rsu_priv { unsigned int dcmf3; } dcmf_status; + struct flash_device_info device_info[4]; + unsigned int retry_counter; unsigned int max_retry; unsigned long spt0_address; unsigned long spt1_address; + + unsigned int *get_spt_response_buf; }; +/** + * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid + * @priv: RSU private data + */ +static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) { + priv->device_info[i].size = INVALID_DEVICE_INFO; + priv->device_info[i].erase_size = INVALID_DEVICE_INFO; + } +} + typedef void (*rsu_async_callback)(struct device *dev, struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data); +/** + * rsu_status_callback() - Status callback from Intel Service Layer + * @client: pointer to service client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for RSU status request. Status is + * only updated after a system reboot, so a get updated status call is + * made during driver probe. + */ +static void rsu_status_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + priv->status.version = FIELD_GET(RSU_VERSION_MASK, + res->a2); + priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2); + priv->status.fail_image = res->a1; + priv->status.current_image = res->a0; + priv->status.error_location = + FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3); + priv->status.error_details = + FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3); + } else { + dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n", + res->a0); + priv->status.version = 0; + priv->status.state = 0; + priv->status.fail_image = 0; + priv->status.current_image = 0; + priv->status.error_location = 0; + priv->status.error_details = 0; + } + + complete(&priv->completion); +} + /** * rsu_async_status_callback() - Status callback from rsu_async_send() * @dev: pointer to device object @@ -147,6 +241,32 @@ static void rsu_command_callback(struct stratix10_svc_client *client, complete(&priv->completion); } +/** + * rsu_retry_callback() - Callback from Intel service layer for getting + * the current image's retry counter from the firmware + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for retry counter, which is used by + * user to know how many times the images is still allowed to reload + * itself before giving up and starting RSU fail-over flow. + */ +static void rsu_retry_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned int *counter = (unsigned int *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) + priv->retry_counter = *counter; + else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support retry\n"); + else + dev_err(client->dev, "Failed to get retry counter %lu\n", + BIT(data->status)); + + complete(&priv->completion); +} /** * rsu_max_retry_callback() - Callback from Intel service layer for getting @@ -229,8 +349,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, } /** - * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send() - * to retrieve the SPT table information. + * rsu_get_device_info_callback() - Callback from Intel service layer for + * getting the QSPI device info + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for QSPI device info. + * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or + * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command). + */ +static void rsu_get_device_info_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_1_2_regs *res = data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + if (!res) { + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n"); + rsu_device_info_invalidate(priv); + complete(&priv->completion); + return; + } + + rsu_device_info_set_from_packed(&priv->device_info[0], res->a1); + rsu_device_info_set_from_packed(&priv->device_info[1], res->a2); + rsu_device_info_set_from_packed(&priv->device_info[2], res->a3); + rsu_device_info_set_from_packed(&priv->device_info[3], res->a4); + + } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) { + dev_warn(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n"); + rsu_device_info_invalidate(priv); + } else { + if (res) + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n", + res->a0); + else + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n", + data->status); + rsu_device_info_invalidate(priv); + } + + complete(&priv->completion); +} + +/** + * rsu_async_get_spt_table_callback() - Callback to be used by the + * rsu_async_send() to retrieve the SPT table information. * @dev: pointer to device object * @priv: pointer to priv object * @data: pointer to callback data structure @@ -243,6 +412,38 @@ static void rsu_async_get_spt_table_callback(struct device *dev, priv->spt1_address = *((unsigned long *)data->kaddr2); } +static void rsu_get_spt_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned long *mbox_err = (unsigned long *)data->kaddr1; + unsigned long *resp_len = (unsigned long *)data->kaddr2; + + if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) || + (*resp_len != RSU_GET_SPT_RESP_LEN)) + goto error; + + priv->spt0_address = priv->get_spt_response_buf[0]; + priv->spt0_address <<= 32; + priv->spt0_address |= priv->get_spt_response_buf[1]; + priv->spt1_address = priv->get_spt_response_buf[2]; + priv->spt1_address <<= 32; + priv->spt1_address |= priv->get_spt_response_buf[3]; + + goto complete; + +error: + dev_err(priv->client.dev, + "failed to get SPTs (status=%#x, mbox_err=%lu, resp_len=%lu)\n", + data->status, mbox_err ? *mbox_err : 0, + resp_len ? *resp_len : 0); + +complete: + stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + complete(&priv->completion); +} + /** * __rsu_send_msg_locked() - send a message to Intel service layer * @priv: pointer to rsu private data @@ -259,7 +460,7 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, unsigned long arg, rsu_callback callback) { - struct stratix10_svc_client_msg msg; + struct stratix10_svc_client_msg msg = {0}; int ret; lockdep_assert_held(&priv->lock); @@ -271,6 +472,14 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, if (arg) msg.arg[0] = arg; + if (command == COMMAND_MBOX_SEND_CMD) { + msg.arg[1] = 0; + msg.payload = NULL; + msg.payload_length = 0; + msg.payload_output = priv->get_spt_response_buf; + msg.payload_length_output = RSU_GET_SPT_RESP_LEN; + } + ret = stratix10_svc_send(priv->chan, &msg); if (ret < 0) goto status_done; @@ -395,6 +604,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri if (status && !handle) { dev_err(dev, "Failed to send async message\n"); + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); return -ETIMEDOUT; } @@ -434,6 +645,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri } status_done: + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); stratix10_svc_async_done(priv->chan, handle); return ret; } @@ -681,15 +894,30 @@ static ssize_t notify_store(struct device *dev, if (ret) return ret; - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + else + ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, status, rsu_command_callback); if (ret) { dev_err(dev, "Error, RSU notify returned %i\n", ret); return ret; } /* to get the updated state */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + } else { + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!ret) + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, + rsu_retry_callback); + } if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); return ret; @@ -698,6 +926,75 @@ static ssize_t notify_store(struct device *dev, return count; } +static ssize_t rsu_device_info_show(struct device *dev, char *buf, + unsigned int index, bool erase_size) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + unsigned int value; + + if (!priv) + return -ENODEV; + + if (index >= ARRAY_SIZE(priv->device_info)) + return -EINVAL; + + value = erase_size ? priv->device_info[index].erase_size : + priv->device_info[index].size; + + if (value == INVALID_DEVICE_INFO) + return -EIO; + + return sysfs_emit(buf, "0x%08x\n", value); +} + +static ssize_t size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, false); +} + +static ssize_t size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, false); +} + +static ssize_t size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, false); +} + +static ssize_t size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, false); +} + +static ssize_t erase_size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, true); +} + +static ssize_t erase_size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, true); +} + +static ssize_t erase_size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, true); +} + +static ssize_t erase_size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, true); +} + static ssize_t spt0_address_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -742,6 +1039,14 @@ static DEVICE_ATTR_RO(dcmf0_status); static DEVICE_ATTR_RO(dcmf1_status); static DEVICE_ATTR_RO(dcmf2_status); static DEVICE_ATTR_RO(dcmf3_status); +static DEVICE_ATTR_RO(size0); +static DEVICE_ATTR_RO(size1); +static DEVICE_ATTR_RO(size2); +static DEVICE_ATTR_RO(size3); +static DEVICE_ATTR_RO(erase_size0); +static DEVICE_ATTR_RO(erase_size1); +static DEVICE_ATTR_RO(erase_size2); +static DEVICE_ATTR_RO(erase_size3); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); static DEVICE_ATTR_RO(spt0_address); @@ -764,6 +1069,14 @@ static struct attribute *rsu_attrs[] = { &dev_attr_dcmf1_status.attr, &dev_attr_dcmf2_status.attr, &dev_attr_dcmf3_status.attr, + &dev_attr_size0.attr, + &dev_attr_size1.attr, + &dev_attr_size2.attr, + &dev_attr_size3.attr, + &dev_attr_erase_size0.attr, + &dev_attr_erase_size1.attr, + &dev_attr_erase_size2.attr, + &dev_attr_erase_size3.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, &dev_attr_spt0_address.attr, @@ -795,7 +1108,12 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; - /* spt0/1_address and status fields default to 0 from kzalloc */ + priv->max_retry = INVALID_RETRY_COUNTER; + priv->spt0_address = INVALID_SPT_ADDRESS; + priv->spt1_address = INVALID_SPT_ADDRESS; + priv->get_spt_response_buf = NULL; + priv->async = false; + rsu_device_info_invalidate(priv); mutex_init(&priv->lock); init_completion(&priv->completion); @@ -809,19 +1127,39 @@ static int stratix10_rsu_probe(struct platform_device *pdev) } ret = stratix10_svc_add_async_client(priv->chan, false); - if (ret) { - dev_err(dev, "failed to add async client\n"); - goto free_channel; + if (ret < 0) { + dev_dbg(dev, "Async operations not supported, fallback to non-async mode\n"); + priv->async = false; + } else { + priv->async = true; } platform_set_drvdata(pdev, priv); /* get the initial state from firmware */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + else + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; + } + + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!priv->async) { + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + goto free_channel; + } } /* get DCMF version from firmware */ @@ -829,28 +1167,64 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_dcmf_version_callback); if (ret) { dev_err(dev, "Error, getting DCMF version %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0, rsu_dcmf_status_callback); if (ret) { dev_err(dev, "Error, getting DCMF status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, rsu_max_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU max retry %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, - rsu_async_get_spt_table_callback); + /* get QSPI device info from firmware */ + ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0, + rsu_get_device_info_callback); + if (ret) { + dev_err(dev, "Error, getting QSPI Device Info %i\n", ret); + if (priv->async) + goto remove_async_client; + goto free_channel; + } + + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, + 0, rsu_async_get_spt_table_callback); + } else { + priv->get_spt_response_buf = + stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN); + if (IS_ERR(priv->get_spt_response_buf)) { + ret = PTR_ERR(priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + dev_err(dev, "failed to allocate get spt buffer\n"); + } else { + ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD, + RSU_GET_SPT_CMD, rsu_get_spt_callback); + } + } if (ret) { dev_err(dev, "Error, getting SPT table %i\n", ret); - goto remove_async_client; + if (priv->async) { + goto remove_async_client; + } else if (!IS_ERR_OR_NULL(priv->get_spt_response_buf)) { + stratix10_svc_free_memory(priv->chan, + priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + } + goto free_channel; } return 0; @@ -866,6 +1240,9 @@ static void stratix10_rsu_remove(struct platform_device *pdev) { struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); } diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 5e20057ee344..f8c2da207cb4 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ /* stratix10 service layer clients */ #define STRATIX10_RSU "stratix10-rsu" +#define SOCFPGA_HWMON "socfpga-hwmon" /* Maximum number of SDM client IDs. */ #define MAX_SDM_CLIENT_IDS 16 @@ -104,9 +106,11 @@ struct stratix10_svc_chan; /** * struct stratix10_svc - svc private data * @stratix10_svc_rsu: pointer to stratix10 RSU device + * @stratix10_svc_hwmon: pointer to stratix10 HWMON device */ struct stratix10_svc { struct platform_device *stratix10_svc_rsu; + struct platform_device *stratix10_svc_hwmon; }; /** @@ -445,13 +449,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl, * svc_thread_recv_status_ok() - handle the successful status * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client - * @res: result from SMC or HVC call + * @res: result from SMC or HVC call (a0-a3; used for routing and most commands) + * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL * * Send back the correspond status to the service clients. */ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data, - struct arm_smccc_res res) + struct arm_smccc_res res, + struct arm_smccc_1_2_regs *res12) { cb_data->kaddr1 = NULL; cb_data->kaddr2 = NULL; @@ -513,6 +519,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, res.a2 = res.a2 * BYTE_TO_WORD_SIZE; cb_data->kaddr2 = &res.a2; break; + case COMMAND_RSU_GET_DEVICE_INFO: + if (WARN_ON(!res12)) { + cb_data->status = BIT(SVC_STATUS_ERROR); + break; + } + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = res12; + cb_data->kaddr2 = NULL; + cb_data->kaddr3 = NULL; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -522,6 +538,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data); } +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res); + /** * svc_normal_to_secure_thread() - the function to run in the kthread * @data: data pointer for kthread function @@ -539,6 +559,7 @@ static int svc_normal_to_secure_thread(void *data) struct stratix10_svc_data *pdata = NULL; struct stratix10_svc_cb_data *cbdata = NULL; struct arm_smccc_res res; + struct arm_smccc_1_2_regs res12 = { 0 }; unsigned long a0, a1, a2, a3, a4, a5, a6, a7; int ret_fifo = 0; @@ -727,6 +748,16 @@ static int svc_normal_to_secure_thread(void *data) a5 = (unsigned long)pdata->paddr_output; a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE; break; + case COMMAND_RSU_GET_DEVICE_INFO: + a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + a1 = 0; + a2 = 0; + a3 = 0; + a4 = 0; + a5 = 0; + a6 = 0; + a7 = 0; + break; default: pr_warn("it shouldn't happen\n"); mutex_unlock(&ctrl->sdm_lock); @@ -740,7 +771,18 @@ static int svc_normal_to_secure_thread(void *data) pr_debug(" a3=0x%016x\n", (unsigned int)a3); pr_debug(" a4=0x%016x\n", (unsigned int)a4); pr_debug(" a5=0x%016x\n", (unsigned int)a5); - ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + struct arm_smccc_1_2_regs args12 = { 0 }; + + args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + svc_smccc_1_2_full(ctrl, &args12, &res12); + res.a0 = res12.a0; + res.a1 = res12.a1; + res.a2 = res12.a2; + res.a3 = res12.a3; + } else { + ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + } pr_debug("%s: %s: after SMC call -- res.a0=0x%016x", __func__, chan->name, (unsigned int)res.a0); @@ -763,9 +805,15 @@ static int svc_normal_to_secure_thread(void *data) } switch (res.a0) { - case INTEL_SIP_SMC_STATUS_OK: - svc_thread_recv_status_ok(pdata, cbdata, res); + case INTEL_SIP_SMC_STATUS_OK: { + struct arm_smccc_1_2_regs *devinfo_res = + (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ? + &res12 : NULL; + + svc_thread_recv_status_ok(pdata, cbdata, res, + devinfo_res); break; + } case INTEL_SIP_SMC_STATUS_BUSY: switch (pdata->command) { case COMMAND_RECONFIG_DATA_SUBMIT: @@ -806,10 +854,16 @@ static int svc_normal_to_secure_thread(void *data) case INTEL_SIP_SMC_RSU_ERROR: pr_err("%s: STATUS_ERROR\n", __func__); cbdata->status = BIT(SVC_STATUS_ERROR); - cbdata->kaddr1 = &res.a1; - cbdata->kaddr2 = (res.a2) ? - svc_pa_to_va(res.a2) : NULL; - cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + cbdata->kaddr1 = &res12; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + } else { + cbdata->kaddr1 = &res.a1; + cbdata->kaddr2 = (res.a2) ? + svc_pa_to_va(res.a2) : NULL; + cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + } pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: @@ -1025,6 +1079,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1, arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); } +/** + * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method + * @ctrl: service controller (selects SMC vs HVC) + * @args: arguments + * @res: full register-file result (a0-a17) + */ +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res) +{ + if (ctrl->invoke_fn == svc_smccc_smc) { + arm_smccc_1_2_smc(args, res); + } else if (ctrl->invoke_fn == svc_smccc_hvc) { + arm_smccc_1_2_hvc(args, res); + } else { + WARN_ON_ONCE(1); + /* + * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted + * as success. Force an error path and clear fabricated payload. + */ + memset(res, 0, sizeof(*res)); + res->a0 = INTEL_SIP_SMC_STATUS_ERROR; + } +} + /** * get_invoke_func() - invoke SMC or HVC call * @dev: pointer to device @@ -1329,6 +1408,14 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg, args.a0 = INTEL_SIP_SMC_ASYNC_RSU_NOTIFY; args.a2 = p_msg->arg[0]; break; + case COMMAND_HWMON_READTEMP: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READTEMP; + args.a2 = p_msg->arg[0]; + break; + case COMMAND_HWMON_READVOLT: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READVOLT; + args.a2 = p_msg->arg[0]; + break; default: dev_err(ctrl->dev, "Invalid command ,%d\n", p_msg->command); ret = -EINVAL; @@ -1422,6 +1509,10 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan, */ data->kaddr1 = (void *)&handle->res; break; + case COMMAND_HWMON_READTEMP: + case COMMAND_HWMON_READVOLT: + data->kaddr1 = (void *)&handle->res.a2; + break; default: dev_alert(ctrl->dev, "Invalid command\n ,%d", p_msg->command); @@ -2016,16 +2107,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) if (ret) goto err_put_device; + if (IS_ENABLED(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON)) { + svc->stratix10_svc_hwmon = + platform_device_alloc(SOCFPGA_HWMON, 0); + if (!svc->stratix10_svc_hwmon) { + dev_err(dev, "failed to allocate %s device\n", + SOCFPGA_HWMON); + } else { + svc->stratix10_svc_hwmon->dev.parent = dev; + + ret = platform_device_add(svc->stratix10_svc_hwmon); + if (ret) { + dev_err(dev, "failed to add %s device: %d\n", + SOCFPGA_HWMON, ret); + platform_device_put(svc->stratix10_svc_hwmon); + svc->stratix10_svc_hwmon = NULL; + } + } + } + ret = of_platform_default_populate(dev_of_node(dev), NULL, dev); if (ret) - goto err_unregister_rsu_dev; + goto err_unregister_clients; pr_info("Intel Service Layer Driver Initialized\n"); return 0; -err_unregister_rsu_dev: - platform_device_unregister(svc->stratix10_svc_rsu); +err_unregister_clients: + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); + if (svc->stratix10_svc_rsu) + platform_device_unregister(svc->stratix10_svc_rsu); goto err_free_fifos; err_put_device: platform_device_put(svc->stratix10_svc_rsu); @@ -2050,6 +2163,8 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev) struct stratix10_svc *svc = ctrl->svc; platform_device_unregister(svc->stratix10_svc_rsu); + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); stratix10_svc_async_exit(ctrl); diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 44badfd11e1b..070c025ee215 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CVP_BAR 0 /* BAR used for data transfer in memory mode */ #define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ @@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr, static int altera_cvp_send_block(struct altera_cvp_conf *conf, const u32 *data, size_t len) { - u32 mask, words = len / sizeof(u32); + u32 words = len / sizeof(u32); int i, remainder; for (i = 0; i < words; i++) @@ -270,9 +271,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf, /* write up to 3 trailing bytes, if any */ remainder = len % sizeof(u32); if (remainder) { - mask = BIT(remainder * 8) - 1; - if (mask) - conf->write_data(conf, *data & mask); + u32 word = 0; + + memcpy(&word, data, remainder); + conf->write_data(conf, word); } return 0; diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 7aa4983ab67d..495edc448b68 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -925,6 +925,8 @@ static int fme_perf_pmu_register(struct platform_device *pdev, PERF_PMU_CAP_NO_EXCLUDE; name = devm_kasprintf(priv->dev, GFP_KERNEL, "dfl_fme%d", pdev->id); + if (!name) + return -ENOMEM; ret = perf_pmu_register(pmu, name, -1); if (ret) diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 0a295ccf1644..b8ec2e6f615f 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr, ret = s10_svc_send_msg(priv, COMMAND_RECONFIG, &ctype, sizeof(ctype)); if (ret < 0) - goto init_done; + goto init_error; - ret = wait_for_completion_timeout( - &priv->status_return_completion, S10_RECONFIG_TIMEOUT); - if (!ret) { + if (!wait_for_completion_timeout(&priv->status_return_completion, + S10_RECONFIG_TIMEOUT)) { dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n"); ret = -ETIMEDOUT; - goto init_done; + goto init_error; } - ret = 0; if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) { ret = -ETIMEDOUT; - goto init_done; + goto init_error; } /* Allocate buffers from the service layer's pool. */ @@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr, if (IS_ERR(kbuf)) { s10_free_buffers(mgr); ret = PTR_ERR(kbuf); - goto init_done; + goto init_error; } priv->svc_bufs[i].buf = kbuf; priv->svc_bufs[i].lock = 0; } -init_done: + return 0; + +init_error: stratix10_svc_done(priv->chan); return ret; } @@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf, if (!s10_free_buffers(mgr)) dev_err(dev, "%s not all buffers were freed\n", __func__); + if (ret < 0) + stratix10_svc_done(priv->chan); + return ret; } diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 6994d68e9036..45b65a3264af 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) if (IS_ERR(priv->io_base)) return PTR_ERR(priv->io_base); - priv->clk = devm_clk_get(&pdev->dev, "aclk"); + priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk"); if (IS_ERR(priv->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), "input clock not found\n"); - err = clk_prepare_enable(priv->clk); - if (err) { - dev_err(&pdev->dev, "unable to enable clock\n"); - return err; - } - - clk_disable(priv->clk); - br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name, &xlnx_pr_decoupler_br_ops, priv); if (IS_ERR(br)) { err = PTR_ERR(br); dev_err(&pdev->dev, "unable to register %s", priv->ipconfig->name); - goto err_clk; + return err; } platform_set_drvdata(pdev, br); return 0; - -err_clk: - clk_unprepare(priv->clk); - - return err; } static void xlnx_pr_decoupler_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); - struct xlnx_pr_decoupler_data *p = bridge->priv; fpga_bridge_unregister(bridge); - - clk_unprepare(p->clk); } static struct platform_driver xlnx_pr_decoupler_driver = { diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 9d1d599ef718..2f8ba32e4002 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -598,7 +598,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev), priv); if (err) { - dev_err(dev, "unable to request IRQ\n"); clk_disable_unprepare(priv->clk); return err; } diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c index 9787c09faad8..490d44ad1b50 100644 --- a/drivers/gpib/agilent_82350b/agilent_82350b.c +++ b/drivers/gpib/agilent_82350b/agilent_82350b.c @@ -839,11 +839,10 @@ static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device } static const struct pci_device_id agilent_82350b_pci_table[] = { - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, - PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, PCI_SUBDEVICE_ID_82350A) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82350B) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82351A) }, + { } }; MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964..c62c30fc3472 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1093,10 +1093,10 @@ static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cb7210_pci_table[] = { - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB) }, + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB) }, + { PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB) }, + { } }; MODULE_DEVICE_TABLE(pci, cb7210_pci_table); diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c index c13bc302d9e9..530a777b1384 100644 --- a/drivers/gpib/cec/cec_gpib.c +++ b/drivers/gpib/cec/cec_gpib.c @@ -353,8 +353,8 @@ static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cec_pci_table[] = { - {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, - {0} + { PCI_DEVICE_SUB(CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID) }, + { } }; MODULE_DEVICE_TABLE(pci, cec_pci_table); diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 69f6aa73ab9a..384800e6bf97 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -289,18 +289,19 @@ int autopoll_all_devices(struct gpib_board *board) dev_dbg(board->gpib_dev, "autopoll has board lock\n"); retval = serial_poll_all(board, serial_timeout); - if (retval < 0) { - mutex_unlock(&board->big_gpib_mutex); - mutex_unlock(&board->user_mutex); - return retval; + if (retval >= 0) { + dev_dbg(board->gpib_dev, "complete\n"); + /* + * need to wake wait queue in case someone is + * waiting on RQS + */ + wake_up_interruptible(&board->wait); } - dev_dbg(board->gpib_dev, "complete\n"); - /* - * need to wake wait queue in case someone is - * waiting on RQS - */ - wake_up_interruptible(&board->wait); + if (retval <= 0) { + atomic_set(&board->stuck_srq, 1); + set_bit(SRQI_NUM, &board->status); + } mutex_unlock(&board->big_gpib_mutex); mutex_unlock(&board->user_mutex); diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c index b672dd6aad25..511e1d61c1fb 100644 --- a/drivers/gpib/common/iblib.c +++ b/drivers/gpib/common/iblib.c @@ -193,9 +193,6 @@ static int autospoll_thread(void *board_void) } if (retval <= 0) { dev_err(board->gpib_dev, "stuck SRQ\n"); - - atomic_set(&board->stuck_srq, 1); // XXX could be better - set_bit(SRQI_NUM, &board->status); } } return retval; diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c index fcafdc02ea2e..5e10e9353fed 100644 --- a/drivers/gpib/fmh_gpib/fmh_gpib.c +++ b/drivers/gpib/fmh_gpib/fmh_gpib.c @@ -1338,7 +1338,7 @@ static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int write_byte(nec_priv, AUX_LO_SPEED, AUXMR); nec7210_set_handshake_mode(board, nec_priv, handshake_mode); - /* Hueristically check if hardware supports fifo half full/empty interrupts */ + /* Heuristically check if hardware supports fifo half full/empty interrupts */ fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && (fifo_status_bits & TX_FIFO_HALF_EMPTY); diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c index 46175ba2ac36..e98cf5aea99f 100644 --- a/drivers/gpib/hp_82341/hp_82341.c +++ b/drivers/gpib/hp_82341/hp_82341.c @@ -803,8 +803,8 @@ static void hp_82341_detach(struct gpib_board *board) #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id hp_82341_pnp_table[] = { - {.id = "HWP1411"}, - {.id = ""} + { .id = "HWP1411" }, + { } }; MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); #endif diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h index d7f63c795096..f8a80bdc70dc 100644 --- a/drivers/gpib/include/amccs5933.h +++ b/drivers/gpib/include/amccs5933.h @@ -13,7 +13,7 @@ enum { }; // incoming mailbox 0-3 register offsets -extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) +static inline int INCOMING_MAILBOX_REG(unsigned int mailbox) { return (0x10 + 4 * mailbox); }; @@ -29,25 +29,25 @@ enum { }; // select byte 0 to 3 of incoming mailbox -extern inline int INBOX_BYTE_BITS(unsigned int byte) +static inline int INBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3) << 8; }; // select incoming mailbox 0 to 3 -extern inline int INBOX_SELECT_BITS(unsigned int mailbox) +static inline int INBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 10; }; // select byte 0 to 3 of outgoing mailbox -extern inline int OUTBOX_BYTE_BITS(unsigned int byte) +static inline int OUTBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3); }; // select outgoing mailbox 0 to 3 -extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) +static inline int OUTBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 2; }; diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index 3562f3184c28..6cd6ff596fda 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -371,14 +371,19 @@ enum ines_pci_subdevice_ids { }; static struct pci_device_id ines_pci_table[] = { - {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, - PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, - {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, - {0} + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, + PCI_VENDOR_ID_AMCC, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, + PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030), + }, { + PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB), + }, + { } }; MODULE_DEVICE_TABLE(pci, ines_pci_table); diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index e6ea9422d6f2..dfb8f3ebc27f 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(usb, lpvo_table); * (about twice the log volume of [1]) * To switch debug level: * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} - * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug + * On the fly: echo {0,1,2} > /sys/module/lpvo_usb_gpib/parameters/debug */ static int debug; @@ -1811,14 +1811,9 @@ static ssize_t lpvo_write(struct file *file, const char __user *user_buffer, dev = file->private_data; - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, user_buffer, count)) { - kfree(buf); - return -EFAULT; - } + buf = memdup_user(user_buffer, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); rv = lpvo_do_write(dev, buf, count); kfree(buf); diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h index a1fdba9672a0..dd251afa90e3 100644 --- a/drivers/gpib/tnt4882/mite.h +++ b/drivers/gpib/tnt4882/mite.h @@ -45,12 +45,12 @@ struct mite_struct { extern struct mite_struct *mite_devices; -extern inline unsigned int mite_irq(struct mite_struct *mite) +static inline unsigned int mite_irq(struct mite_struct *mite) { return mite->pcidev->irq; }; -extern inline unsigned int mite_device_id(struct mite_struct *mite) +static inline unsigned int mite_device_id(struct mite_struct *mite) { return mite->pcidev->device; }; diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c index 51a920e1d9a4..3cd13f637ed4 100644 --- a/drivers/gpib/tnt4882/tnt4882_gpib.c +++ b/drivers/gpib/tnt4882/tnt4882_gpib.c @@ -1373,8 +1373,8 @@ static struct pci_driver tnt4882_pci_driver = { #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id tnt4882_pnp_table[] = { - {.id = "NICC601"}, - {.id = ""} + { .id = "NICC601" }, + { } }; MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); #endif diff --git a/drivers/greybus/manifest.c b/drivers/greybus/manifest.c index 9be5d95da587..3b685ace2036 100644 --- a/drivers/greybus/manifest.c +++ b/drivers/greybus/manifest.c @@ -122,6 +122,8 @@ static int identify_descriptor(struct gb_interface *intf, switch (desc_header->type) { case GREYBUS_TYPE_STRING: expected_size += sizeof(struct greybus_descriptor_string); + if (desc_size < expected_size) + break; expected_size += desc->string.length; /* String descriptors are padded to 4 byte boundaries */ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 942282614c48..fecff8610ea8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1993,7 +1993,7 @@ config SENSORS_SL28CPLD config SENSORS_SBTSI tristate "Emulated SB-TSI temperature sensor" - depends on I2C + depends on AMD_SBTSI help If you say yes here you get support for emulated temperature sensors on AMD SoCs with SB-TSI interface connected to a BMC device. @@ -2189,6 +2189,16 @@ config SENSORS_SMSC47M192 This driver can also be built as a module. If so, the module will be called smsc47m192. +config SENSORS_ALTERA_SOCFPGA_HWMON + tristate "Altera SoC FPGA hardware monitoring features" + depends on INTEL_STRATIX10_SERVICE + help + If you say yes here you get support for the temperature and + voltage sensors of Altera SoC FPGA devices. + + This driver can also be built as a module. If so, the module + will be called socfpga-hwmon. + config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" depends on HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 0cad7e21634c..1229b6b3996d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -224,6 +224,7 @@ obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o +obj-$(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON) += socfpga-hwmon.o obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index fc17ad93a376..6502e20517a8 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -221,3 +221,4 @@ module_platform_driver(iio_hwmon_driver); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("IIO to hwmon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 1ac0288fbbd8..4fa7f522f245 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -748,3 +748,4 @@ MODULE_DESCRIPTION("NTC Thermistor Driver"); MODULE_AUTHOR("MyungJoo Ham "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ntc-thermistor"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c28f8625cd3a..11c8108d69b2 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -7,13 +7,12 @@ * Copyright (c) 2020, Kun Yi */ +#include #include -#include -#include #include +#include #include -#include -#include +#include /* * SB-TSI registers only support SMBus byte data access. "_INT" registers are @@ -22,39 +21,17 @@ */ #define SBTSI_REG_TEMP_INT 0x01 /* RO */ #define SBTSI_REG_STATUS 0x02 /* RO */ -#define SBTSI_REG_CONFIG 0x03 /* RO */ #define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ #define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ #define SBTSI_REG_TEMP_DEC 0x10 /* RW */ #define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ #define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ -/* - * Bit for reporting value with temperature measurement range. - * bit == 0: Use default temperature range (0C to 255.875C). - * bit == 1: Use extended temperature range (-49C to +206.875C). - */ -#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 -/* - * ReadOrder bit specifies the reading order of integer and decimal part of - * CPU temperature for atomic reads. If bit == 0, reading integer part triggers - * latching of the decimal part, so integer part should be read first. - * If bit == 1, read order should be reversed. - */ -#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 - #define SBTSI_TEMP_EXT_RANGE_ADJ 49000 #define SBTSI_TEMP_MIN 0 #define SBTSI_TEMP_MAX 255875 -/* Each client has this additional data */ -struct sbtsi_data { - struct i2c_client *client; - bool ext_range_mode; - bool read_order; -}; - /* * From SB-TSI spec: CPU temperature readings and limit registers encode the * temperature in increments of 0.125 from 0 to 255.875. The "high byte" @@ -84,40 +61,79 @@ static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) *decimal = (temp & 0x7) << 5; } +/* + * Read integer and decimal parts of an SB-TSI temperature register pair + * The read order is determined by the ReadOrder bit to ensure atomic latching. + */ +static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2, + u8 *val1, u8 *val2) +{ + int ret; + + guard(sbtsi)(data); + ret = sbtsi_xfer(data, reg1, val1, true); + if (!ret) + ret = sbtsi_xfer(data, reg2, val2, true); + return ret; +} + +/* + * Write integer and decimal parts of an SB-TSI temperature register pair. + */ +static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec, + u8 val_int, u8 val_dec) +{ + int ret; + + guard(sbtsi)(data); + ret = sbtsi_xfer(data, reg_int, &val_int, false); + if (!ret) + ret = sbtsi_xfer(data, reg_dec, &val_dec, false); + return ret; +} + static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct sbtsi_data *data = dev_get_drvdata(dev); s32 temp_int, temp_dec; + int err; + u8 val_int, val_dec; switch (attr) { case hwmon_temp_input: - if (data->read_order) { - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - } else { - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - } + if (data->read_order) + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_DEC, SBTSI_REG_TEMP_INT, + &val_dec, &val_int); + else + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_INT, SBTSI_REG_TEMP_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_max: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_HIGH_INT, SBTSI_REG_TEMP_HIGH_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_min: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_LOW_INT, SBTSI_REG_TEMP_LOW_DEC, + &val_int, &val_dec); + + if (err < 0) + return err; break; default: return -EINVAL; } - - if (temp_int < 0) - return temp_int; - if (temp_dec < 0) - return temp_dec; - + temp_int = val_int; + temp_dec = val_dec; *val = sbtsi_reg_to_mc(temp_int, temp_dec); if (data->ext_range_mode) *val -= SBTSI_TEMP_EXT_RANGE_ADJ; @@ -129,7 +145,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct sbtsi_data *data = dev_get_drvdata(dev); - int reg_int, reg_dec, err; + int reg_int, reg_dec; u8 temp_int, temp_dec; switch (attr) { @@ -150,11 +166,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); sbtsi_mc_to_reg(val, &temp_int, &temp_dec); - err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); - if (err) - return err; - - return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); + return sbtsi_temp_write(data, reg_int, reg_dec, temp_int, temp_dec); } static umode_t sbtsi_is_visible(const void *data, @@ -195,55 +207,33 @@ static const struct hwmon_chip_info sbtsi_chip_info = { .info = sbtsi_info, }; -static int sbtsi_probe(struct i2c_client *client) +static int sbtsi_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) { - struct device *dev = &client->dev; + struct sbtsi_data *data = dev_get_drvdata(adev->dev.parent); + struct device *dev = &adev->dev; struct device *hwmon_dev; - struct sbtsi_data *data; - int err; - data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - - err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); - if (err < 0) - return err; - data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err); - data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err); - - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbtsi", data, &sbtsi_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } -static const struct i2c_device_id sbtsi_id[] = { - { .name = "sbtsi" }, +static const struct auxiliary_device_id sbtsi_id[] = { + { .name = AMD_SBTSI_ADEV "." AMD_SBTSI_AUX_HWMON }, { } }; -MODULE_DEVICE_TABLE(i2c, sbtsi_id); +MODULE_DEVICE_TABLE(auxiliary, sbtsi_id); -static const struct of_device_id __maybe_unused sbtsi_of_match[] = { - { - .compatible = "amd,sbtsi", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, sbtsi_of_match); - -static struct i2c_driver sbtsi_driver = { +static struct auxiliary_driver sbtsi_driver = { .driver = { .name = "sbtsi", - .of_match_table = of_match_ptr(sbtsi_of_match), }, .probe = sbtsi_probe, .id_table = sbtsi_id, }; - -module_i2c_driver(sbtsi_driver); +module_auxiliary_driver(sbtsi_driver); MODULE_AUTHOR("Kun Yi "); MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); diff --git a/drivers/hwmon/socfpga-hwmon.c b/drivers/hwmon/socfpga-hwmon.c new file mode 100644 index 000000000000..5b43274d0aa2 --- /dev/null +++ b/drivers/hwmon/socfpga-hwmon.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Altera SoC FPGA hardware monitoring driver + * + * Copyright (c) 2026 Altera Corporation + * + * Authors: + * Nazim Amirul + * Tze Yee Ng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HWMON_TIMEOUT msecs_to_jiffies(SVC_HWMON_REQUEST_TIMEOUT_MS) +#define HWMON_RETRY_SLEEP_US 1000U +#define HWMON_ASYNC_MSG_RETRY 3U +#define SOCFPGA_HWMON_MAXSENSORS 16 +#define SOCFPGA_HWMON_CHANNEL_MASK GENMASK(15, 0) +#define SOCFPGA_HWMON_PAGE_SHIFT 16 +#define SOCFPGA_HWMON_CHAN(page, channel) \ + (((page) << SOCFPGA_HWMON_PAGE_SHIFT) | \ + ((channel) & SOCFPGA_HWMON_CHANNEL_MASK)) +#define SOCFPGA_HWMON_ATTR_VISIBLE 0444 +/* Temperature from SDM is signed Q8.8 degrees Celsius (8 fractional bits). */ +#define SOCFPGA_HWMON_TEMP_FRAC_BITS 8 +#define SOCFPGA_HWMON_TEMP_FRAC_DIV BIT(SOCFPGA_HWMON_TEMP_FRAC_BITS) +#define SOCFPGA_HWMON_TEMP_MDEG_SCALE 1000 +/* Voltage from SDM is unsigned Q16 volts (16 fractional bits). */ +#define SOCFPGA_HWMON_VOLT_FRAC_BITS 16 +#define SOCFPGA_HWMON_VOLT_FRAC_DIV BIT(SOCFPGA_HWMON_VOLT_FRAC_BITS) +#define SOCFPGA_HWMON_VOLT_MV_SCALE 1000 + +#define ETEMP_INACTIVE 0x80000000U +#define ETEMP_TOO_OLD 0x80000001U +#define ETEMP_NOT_PRESENT 0x80000002U +#define ETEMP_TIMEOUT 0x80000003U +#define ETEMP_CORRUPT 0x80000004U +#define ETEMP_BUSY 0x80000005U +#define ETEMP_NOT_INITIALIZED 0x800000FFU + +struct socfpga_hwmon_channel { + u32 reg; + const char *label; +}; + +struct socfpga_hwmon_board_data { + const struct socfpga_hwmon_channel *temp; + unsigned int num_temp; + const struct socfpga_hwmon_channel *volt; + unsigned int num_volt; +}; + +struct socfpga_hwmon_priv { + struct stratix10_svc_chan *chan; + struct stratix10_svc_client client; + struct completion completion; + struct mutex lock; /* protect SVC calls */ + bool async; + int last_err; /* sync-mode SVC result; 0 on success */ + u32 temperature; + u32 voltage; + int temperature_channels; + int voltage_channels; + const char *temp_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + const char *volt_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + u32 temp_chan[SOCFPGA_HWMON_MAXSENSORS]; + u32 volt_chan[SOCFPGA_HWMON_MAXSENSORS]; +}; + +static umode_t socfpga_hwmon_is_visible(const void *dev, + enum hwmon_sensor_types type, + u32 attr, int chan) +{ + const struct socfpga_hwmon_priv *priv = dev; + + switch (type) { + case hwmon_temp: + if (chan < priv->temperature_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + case hwmon_in: + if (chan < priv->voltage_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + default: + return 0; + } +} + +static void socfpga_hwmon_readtemp_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->temperature = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static void socfpga_hwmon_readvolt_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->voltage = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static int socfpga_hwmon_parse_temp(long *val, u32 temperature) +{ + switch (temperature) { + case ETEMP_INACTIVE: + case ETEMP_NOT_PRESENT: + case ETEMP_CORRUPT: + case ETEMP_NOT_INITIALIZED: + return -EOPNOTSUPP; + case ETEMP_TIMEOUT: + case ETEMP_BUSY: + case ETEMP_TOO_OLD: + return -EAGAIN; + default: + /* SDM returns a 16-bit signed Q8.8 value in the low 16 bits. */ + *val = (long)(s16)(temperature & SOCFPGA_HWMON_CHANNEL_MASK) * + SOCFPGA_HWMON_TEMP_MDEG_SCALE / SOCFPGA_HWMON_TEMP_FRAC_DIV; + return 0; + } +} + +static int socfpga_hwmon_encode_temp_arg(u32 reg, u64 *arg) +{ + u32 page = (reg >> SOCFPGA_HWMON_PAGE_SHIFT) & SOCFPGA_HWMON_CHANNEL_MASK; + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = (1ULL << channel) | ((u64)page << SOCFPGA_HWMON_PAGE_SHIFT); + return 0; +} + +static int socfpga_hwmon_encode_volt_arg(u32 reg, u64 *arg) +{ + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = 1ULL << channel; + return 0; +} + +static int socfpga_hwmon_async_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_cb_data data = {}; + unsigned long deadline = jiffies + HWMON_TIMEOUT; + void *handle = NULL; + int status, index, ret; + + for (index = 0; index < HWMON_ASYNC_MSG_RETRY; index++) { + status = stratix10_svc_async_send(priv->chan, msg, &handle, + NULL, NULL); + if (status == 0) + break; + dev_warn(dev, "Failed to send async message: %d\n", status); + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (status && !handle) { + dev_err(dev, "Failed to send async message after %u retries: %d\n", + HWMON_ASYNC_MSG_RETRY, status); + return status; + } + + ret = -ETIMEDOUT; + while (!time_after(jiffies, deadline)) { + status = stratix10_svc_async_poll(priv->chan, handle, &data); + if (status == -EAGAIN) { + /* still in progress */ + } else if (status < 0) { + ret = status; + break; + } else if (status == 0) { + ret = 0; + break; + } + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (ret) { + dev_err(dev, "Failed to get async response\n"); + goto done; + } + + if (data.status) { + dev_err(dev, "%s returned 0x%x from SDM\n", __func__, + data.status); + ret = -EFAULT; + goto done; + } + + if (type == hwmon_temp) + priv->temperature = (u32)*(unsigned long *)data.kaddr1; + else + priv->voltage = (u32)*(unsigned long *)data.kaddr1; + + ret = 0; + +done: + stratix10_svc_async_done(priv->chan, handle); + return ret; +} + +static int socfpga_hwmon_sync_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + int ret; + + reinit_completion(&priv->completion); + + if (type == hwmon_temp) + priv->client.receive_cb = socfpga_hwmon_readtemp_cb; + else + priv->client.receive_cb = socfpga_hwmon_readvolt_cb; + + ret = stratix10_svc_send(priv->chan, msg); + if (ret < 0) + goto status_done; + + ret = wait_for_completion_timeout(&priv->completion, HWMON_TIMEOUT); + if (!ret) { + dev_err(priv->client.dev, "timeout waiting for SMC call\n"); + ret = -ETIMEDOUT; + goto status_done; + } + + ret = priv->last_err; + +status_done: + stratix10_svc_done(priv->chan); + return ret; +} + +static int socfpga_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int chan, long *val) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_client_msg msg = {0}; + int ret; + + if (chan >= SOCFPGA_HWMON_MAXSENSORS) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + ret = socfpga_hwmon_encode_temp_arg(priv->temp_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READTEMP; + break; + case hwmon_in: + ret = socfpga_hwmon_encode_volt_arg(priv->volt_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READVOLT; + break; + default: + return -EOPNOTSUPP; + } + + guard(mutex)(&priv->lock); + if (priv->async) + ret = socfpga_hwmon_async_read(dev, type, &msg); + else + ret = socfpga_hwmon_sync_read(dev, type, &msg); + if (ret) + return ret; + + if (type == hwmon_temp) + ret = socfpga_hwmon_parse_temp(val, priv->temperature); + else + /* SDM returns Q16 volts; convert to hwmon millivolts. */ + *val = (long)priv->voltage * SOCFPGA_HWMON_VOLT_MV_SCALE / + SOCFPGA_HWMON_VOLT_FRAC_DIV; + return ret; +} + +static int socfpga_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int chan, const char **str) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + *str = priv->volt_chan_names[chan]; + return 0; + case hwmon_temp: + *str = priv->temp_chan_names[chan]; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops socfpga_hwmon_ops = { + .is_visible = socfpga_hwmon_is_visible, + .read = socfpga_hwmon_read, + .read_string = socfpga_hwmon_read_string, +}; + +static const struct hwmon_channel_info *socfpga_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + NULL +}; + +static const struct hwmon_chip_info socfpga_hwmon_chip_info = { + .ops = &socfpga_hwmon_ops, + .info = socfpga_hwmon_info, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCERAM" }, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, +}; + +static const struct socfpga_hwmon_board_data s10_hwmon_board = { + .temp = s10_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(s10_hwmon_temp_channels), + .volt = s10_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(s10_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 4), "1.8V VCCPT" }, + { SOCFPGA_HWMON_CHAN(0, 5), "1.2V VCCCRCORE" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCH" }, + { SOCFPGA_HWMON_CHAN(0, 7), "0.8V VCCL" }, +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, + { SOCFPGA_HWMON_CHAN(1, 0), "Main Die corner bottom left max" }, + { SOCFPGA_HWMON_CHAN(2, 0), "Main Die corner top left max" }, + { SOCFPGA_HWMON_CHAN(3, 0), "Main Die corner bottom right max" }, + { SOCFPGA_HWMON_CHAN(4, 0), "Main Die corner top right max" }, +}; + +static const struct socfpga_hwmon_board_data agilex_hwmon_board = { + .temp = agilex_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(agilex_hwmon_temp_channels), + .volt = agilex_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_board_data * +socfpga_hwmon_get_board(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!np) + return NULL; + + if (of_device_is_compatible(np, "intel,stratix10-svc")) + return &s10_hwmon_board; + if (of_device_is_compatible(np, "intel,agilex-svc")) + return &agilex_hwmon_board; + + return NULL; +} + +static int socfpga_hwmon_init_channels(struct device *dev, + const struct socfpga_hwmon_board_data *board, + struct socfpga_hwmon_priv *priv) +{ + unsigned int i; + + if (board->num_temp > SOCFPGA_HWMON_MAXSENSORS || + board->num_volt > SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + for (i = 0; i < board->num_temp; i++) { + priv->temp_chan_names[i] = board->temp[i].label; + priv->temp_chan[i] = board->temp[i].reg; + } + priv->temperature_channels = board->num_temp; + + for (i = 0; i < board->num_volt; i++) { + priv->volt_chan_names[i] = board->volt[i].label; + priv->volt_chan[i] = board->volt[i].reg; + } + priv->voltage_channels = board->num_volt; + + return 0; +} + +static void socfpga_hwmon_release_svc(void *data) +{ + struct socfpga_hwmon_priv *priv = data; + + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); +} + +static int socfpga_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + const struct socfpga_hwmon_board_data *board; + struct socfpga_hwmon_priv *priv; + struct device *hwmon_dev; + int ret; + + if (!parent || !parent->of_node) { + dev_err(dev, "missing parent device node\n"); + return -ENODEV; + } + + board = socfpga_hwmon_get_board(parent); + if (!board) { + dev_err(dev, "unsupported service layer compatible\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client.dev = dev; + priv->client.priv = priv; + init_completion(&priv->completion); + mutex_init(&priv->lock); + + ret = socfpga_hwmon_init_channels(dev, board, priv); + if (ret) + return ret; + + priv->chan = stratix10_svc_request_channel_byname(&priv->client, + SVC_CLIENT_HWMON); + if (IS_ERR(priv->chan)) { + ret = PTR_ERR(priv->chan); + if (ret == -EPROBE_DEFER) + dev_dbg(dev, "service channel %s not ready, deferring probe\n", + SVC_CLIENT_HWMON); + else + dev_err(dev, "couldn't get service channel %s: %d\n", + SVC_CLIENT_HWMON, ret); + return ret; + } + + ret = stratix10_svc_add_async_client(priv->chan, false); + switch (ret) { + case 0: + priv->async = true; + break; + case -EINVAL: + case -EOPNOTSUPP: + /* + * stratix10_svc_add_async_client() returns -EINVAL when the + * async controller is not initialized; fall back to sync mode. + */ + dev_dbg(dev, "async operations not supported, using sync mode\n"); + priv->async = false; + break; + default: + dev_err(dev, "failed to add async client: %d\n", ret); + stratix10_svc_free_channel(priv->chan); + return ret; + } + + ret = devm_add_action_or_reset(dev, socfpga_hwmon_release_svc, priv); + if (ret) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "socfpga_hwmon", + priv, + &socfpga_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + platform_set_drvdata(pdev, priv); + return 0; +} + +static struct platform_driver socfpga_hwmon_driver = { + .probe = socfpga_hwmon_probe, + .driver = { + .name = "socfpga-hwmon", + }, +}; +module_platform_driver(socfpga_hwmon_driver); + +MODULE_AUTHOR("Nazim Amirul "); +MODULE_AUTHOR("Tze Yee Ng "); +MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:socfpga-hwmon"); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 762109307b86..b3c67e96a82a 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event); static ssize_t cntr_val_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; if (!coresight_get_mode(drvdata->csdev)) { spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, config->cntr_val[i]); + val = config->cntr_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); - return ret; + } else { + val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx)); } - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); - } - - return ret; + return sysfs_emit(buf, "%#x\n", val); } static ssize_t cntr_val_store(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index c302072b293a..e1a59b434505 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -76,7 +76,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) { /* sequencer state control registers */ idx = (offset & GENMASK(3, 0)) / 4; - if (idx < ETM_MAX_SEQ_STATES) { + if (idx < ETM_MAX_SEQ_TRANSITIONS) { reg_csdev->driver_regval = &drvcfg->seq_ctrl[idx]; err = 0; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 14bb31bd6a0b..2247ad55d444 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -93,7 +93,7 @@ static int etm4_probe_cpu(unsigned int cpu); static bool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n) { return (n < drvdata->nr_ss_cmp) && - drvdata->nr_pe && + drvdata->nr_pe_cmp && (drvdata->config.ss_status[n] & TRCSSCSRn_PC); } @@ -542,7 +542,8 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR); if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR); @@ -756,8 +757,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev, .ATTR_CFG_FLD_timestamp_CFG = U64_MAX, }; struct perf_event_attr *attr = &event->attr; - unsigned long cfg_hash; - int preset, cc_threshold; + int cc_threshold; u8 ts_level; /* Clear configuration from previous run */ @@ -843,16 +843,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev, /* bit[12], Return stack enable bit */ config->cfg |= TRCCONFIGR_RS; - /* - * Set any selected configuration and preset. A zero configid means no - * configuration active, preset = 0 means no preset selected. - */ - cfg_hash = ATTR_CFG_GET_FLD(attr, configid); - if (cfg_hash) { - preset = ATTR_CFG_GET_FLD(attr, preset); - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - } - /* branch broadcast - enable if selected and supported */ if (ATTR_CFG_GET_FLD(attr, branch_broadcast)) { if (!drvdata->trcbb) { @@ -876,7 +866,9 @@ static int etm4_enable_perf(struct coresight_device *csdev, struct coresight_path *path) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret; + struct perf_event_attr *attr = &event->attr; + unsigned long cfg_hash; + int ret, preset; if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL; @@ -887,7 +879,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* Configure the tracer based on the session's specifics */ ret = etm4_parse_event_config(csdev, event); if (ret) - goto out; + goto err; + + /* + * Set any selected configuration and preset. A zero configid means no + * configuration active, preset = 0 means no preset selected. + */ + cfg_hash = ATTR_CFG_GET_FLD(attr, configid); + if (cfg_hash) { + preset = ATTR_CFG_GET_FLD(attr, preset); + ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); + if (ret) + goto err; + } drvdata->trcid = path->trace_id; @@ -896,16 +900,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* And enable it */ ret = etm4_enable_hw(drvdata); - -out: - /* Failed to start tracer; roll back to DISABLED mode */ if (ret) { - coresight_set_mode(csdev, CS_MODE_DISABLED); - return ret; + if (cfg_hash) + cscfg_csdev_disable_active_config(csdev); + goto err; } csdev->path = path; return 0; + +err: + /* Failed to start tracer; roll back to DISABLED mode */ + coresight_set_mode(csdev, CS_MODE_DISABLED); + return ret; } static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -919,8 +926,10 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset); if (cfg_hash) { ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - if (ret) + if (ret) { + etm4_release_trace_id(drvdata); return ret; + } } raw_spin_lock(&drvdata->spinlock); @@ -1508,6 +1517,8 @@ static void etm4_init_arch_data(void *info) drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up); /* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */ drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5); + if (drvdata->nrseqstate) + drvdata->nr_seq_ctrls = ETM_MAX_SEQ_TRANSITIONS; /* NUMCNTR, bits[30:28] number of counters available for tracing */ drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5); @@ -1896,7 +1907,7 @@ static int etm4_cpu_save(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i)); if (drvdata->nrseqstate) { @@ -2009,7 +2020,7 @@ static void etm4_cpu_restore(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index e9eeea6240d5..cc6cdd3ae29d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -223,7 +223,7 @@ static ssize_t reset_store(struct device *dev, config->vipcssctlr = 0x0; /* Disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) config->seq_ctrl[i] = 0x0; config->seq_rst = 0x0; config->seq_state = 0x0; @@ -1395,9 +1395,11 @@ static ssize_t seq_idx_store(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + if (!drvdata->nr_seq_ctrls) + return -ENOTSUPP; if (kstrtoul(buf, 16, &val)) return -EINVAL; - if (val >= drvdata->nrseqstate - 1) + if (val >= drvdata->nr_seq_ctrls) return -EINVAL; /* diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 89d81ce4e04e..df9e0748d71e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -614,6 +614,7 @@ static inline u32 etm4_res_sel_pair(u8 res_sel_idx) #define ETM_MAX_NR_PE 8 #define ETMv4_MAX_CNTR 4 #define ETM_MAX_SEQ_STATES 4 +#define ETM_MAX_SEQ_TRANSITIONS 3 #define ETM_MAX_EXT_INP_SEL 4 #define ETM_MAX_EXT_INP 256 #define ETM_MAX_EXT_OUT 4 @@ -877,7 +878,7 @@ struct etmv4_config { u32 vipcssctlr; u8 seq_idx; u8 syncfreq; - u32 seq_ctrl[ETM_MAX_SEQ_STATES]; + u32 seq_ctrl[ETM_MAX_SEQ_TRANSITIONS]; u32 seq_rst; u32 seq_state; u8 cntr_idx; @@ -928,7 +929,7 @@ struct etmv4_save_state { u32 trcvissctlr; u32 trcvipcssctlr; - u32 trcseqevr[ETM_MAX_SEQ_STATES]; + u32 trcseqevr[ETM_MAX_SEQ_TRANSITIONS]; u32 trcseqrstevr; u32 trcseqstr; u32 trcextinselr; @@ -953,11 +954,6 @@ struct etmv4_save_state { u32 trcclaimset; - u32 cntr_val[ETMv4_MAX_CNTR]; - u32 seq_state; - u32 vinst_ctrl; - u32 ss_status[ETM_MAX_SS_CMP]; - u32 trcpdcr; }; @@ -981,6 +977,7 @@ struct etmv4_save_state { * @numcidc: Number of contextID comparators. * @numvmidc: Number of VMID comparators. * @nrseqstate: The number of sequencer states that are implemented. + * @nr_seq_ctrls: The number of sequence state transition control registers. * @nr_event: Indicates how many events the trace unit support. * @nr_resource:The number of resource selection pairs available for tracing. * @nr_ss_cmp: Number of single-shot comparator controls that are available. @@ -1046,6 +1043,7 @@ struct etmv4_drvdata { u8 numextinsel; u8 numvmidc; u8 nrseqstate; + u8 nr_seq_ctrls; u8 nr_event; u8 nr_resource; u8 nr_ss_cmp; diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 233c4c32513c..52f168f8b459 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) HISI_PTT_WAIT_TRACE_TIMEOUT_US); } -static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) +static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) { u32 val; - readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, - val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, - HISI_PTT_RESET_TIMEOUT_US); + return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, + val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, + HISI_PTT_RESET_TIMEOUT_US); } static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) @@ -194,7 +194,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) { struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; u32 val; - int i; /* Check device idle before start trace */ if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) { @@ -202,14 +201,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) return -EBUSY; } - ctrl->started = true; - /* Reset the DMA before start tracing */ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val |= HISI_PTT_TRACE_CTRL_RST; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); - hisi_ptt_wait_dma_reset_done(hisi_ptt); + if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) { + pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n"); + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val &= ~HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + return -ETIMEDOUT; + } val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val &= ~HISI_PTT_TRACE_CTRL_RST; @@ -218,10 +221,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) /* Reset the index of current buffer */ hisi_ptt->trace_ctrl.buf_index = 0; - /* Zero the trace buffers */ - for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) - memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE); - /* Clear the interrupt status */ writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); @@ -234,6 +233,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) if (!hisi_ptt->trace_ctrl.is_port) val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; + ctrl->started = true; + /* Start the Trace */ val |= HISI_PTT_TRACE_CTRL_EN; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 661127aed2f9..652557a5b851 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -92,6 +92,7 @@ source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/dummy/Kconfig" source "drivers/iio/filter/Kconfig" +source "drivers/iio/flow/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/health/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index cb80ef837e84..f03a4100c800 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -29,6 +29,7 @@ obj-y += dac/ obj-y += dummy/ obj-y += gyro/ obj-y += filter/ +obj-y += flow/ obj-y += frequency/ obj-y += health/ obj-y += humidity/ diff --git a/drivers/iio/TODO b/drivers/iio/TODO index 2ace27d1ac62..7b05308248d7 100644 --- a/drivers/iio/TODO +++ b/drivers/iio/TODO @@ -5,12 +5,17 @@ Documentation tree - Yaml conversions for abandoned drivers - ABI Documentation - - Audit driviers/iio/staging/Documentation + - Audit drivers/staging/iio/Documentation -- Converting drivers from device tree centric to more generic -property handlers. +- Converting drivers from device tree centric to generic property handlers. - Refactor old platform_data constructs from drivers and convert it -to state struct and using property handlers and readers. + to state struct and using property handlers and readers. + +- Convert selected IIO drivers to use fully device managed resource APIs. + +- Evaluate and possibly adopt cleanup.h helpers (guard()(), __free(), et cetera) + in drivers where manual resource management is still used and there is a + significant improvement to the code flow and complexity to be had. Mailing list: linux-iio@vger.kernel.org diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 0315f4bfd69a..4fa1b04f75b6 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -60,8 +60,8 @@ static const struct adxl345_chip_info adxl375_spi_info = { }; static const struct spi_device_id adxl345_spi_id[] = { - { "adxl345", (kernel_ulong_t)&adxl345_spi_info }, - { "adxl375", (kernel_ulong_t)&adxl375_spi_info }, + { .name = "adxl345", .driver_data = (kernel_ulong_t)&adxl345_spi_info }, + { .name = "adxl375", .driver_data = (kernel_ulong_t)&adxl375_spi_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl345_spi_id); diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 68cb2557f390..080c9f6f9a11 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -768,8 +768,7 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxl355_irq", data->dready_trig); if (ret) - return dev_err_probe(data->dev, ret, "request irq %d failed\n", - irq); + return ret; ret = devm_iio_trigger_register(data->dev, data->dready_trig); if (ret) diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c index 437be2f1d53c..d2209ca8ca6e 100644 --- a/drivers/iio/accel/adxl355_spi.c +++ b/drivers/iio/accel/adxl355_spi.c @@ -40,8 +40,8 @@ static int adxl355_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl355_spi_id[] = { - { "adxl355", (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, - { "adxl359", (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, + { .name = "adxl355", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, + { .name = "adxl359", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, { } }; MODULE_DEVICE_TABLE(spi, adxl355_spi_id); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 8c3de11a10a3..4ff1c7a0988b 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1486,7 +1486,7 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, adxl367_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c index 785652c7fc92..1d1418452bdc 100644 --- a/drivers/iio/accel/adxl367_spi.c +++ b/drivers/iio/accel/adxl367_spi.c @@ -137,7 +137,7 @@ static int adxl367_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl367_spi_id[] = { - { "adxl367", 0 }, + { .name = "adxl367" }, { } }; MODULE_DEVICE_TABLE(spi, adxl367_spi_id); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 25fdb4254372..128e148c4e96 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -34,8 +34,8 @@ static int adxl372_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl372_spi_id[] = { - { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, - { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, + { .name = "adxl371", .driver_data = (kernel_ulong_t)&adxl371_chip_info }, + { .name = "adxl372", .driver_data = (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c index ee3e6338b53d..ae8467f4e6c0 100644 --- a/drivers/iio/accel/adxl380_spi.c +++ b/drivers/iio/accel/adxl380_spi.c @@ -34,10 +34,10 @@ static int adxl380_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl380_spi_id[] = { - { "adxl318", (kernel_ulong_t)&adxl318_chip_info }, - { "adxl319", (kernel_ulong_t)&adxl319_chip_info }, - { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, - { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { .name = "adxl318", .driver_data = (kernel_ulong_t)&adxl318_chip_info }, + { .name = "adxl319", .driver_data = (kernel_ulong_t)&adxl319_chip_info }, + { .name = "adxl380", .driver_data = (kernel_ulong_t)&adxl380_chip_info }, + { .name = "adxl382", .driver_data = (kernel_ulong_t)&adxl382_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl380_spi_id); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 62bda8d76691..e7dbbd4766dd 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -988,10 +988,8 @@ static int bma180_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "bma180_event", data->trig); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto err_trigger_free; - } data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/accel/bma220_core.c b/drivers/iio/accel/bma220_core.c index 269e2b720ddb..b83abfdd84fd 100644 --- a/drivers/iio/accel/bma220_core.c +++ b/drivers/iio/accel/bma220_core.c @@ -544,8 +544,7 @@ int bma220_common_probe(struct device *dev, struct regmap *regmap, int irq) &bma220_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_add_action_or_reset(dev, bma220_deinit, data); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index d897aec1c9f2..d6c1087cc3c0 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -25,7 +25,7 @@ static int bma220_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma220_spi_id[] = { - { "bma220", 0 }, + { .name = "bma220" }, { } }; diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 05f72707f830..7e3cff4cca91 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1789,8 +1789,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index 4b89e313704f..7ac0a7881b59 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -87,7 +87,7 @@ static int bma400_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma400_spi_ids[] = { - { "bma400", 0 }, + { .name = "bma400" }, { } }; MODULE_DEVICE_TABLE(spi, bma400_spi_ids); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index dc8a6285cf3d..46bb36d9366e 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -988,8 +988,10 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, do_div(sample_period, count); tstamp = data->timestamp - (count - 1) * sample_period; - if (samples && count > samples) - count = samples; + if (samples) + count = min3(count, samples, BMC150_ACCEL_FIFO_LENGTH); + else + count = min(count, BMC150_ACCEL_FIFO_LENGTH); count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 7d2be6f63538..405bd7b2d199 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -52,15 +52,15 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct spi_device_id bmc150_accel_id[] = { - {"bma222"}, - {"bma222e"}, - {"bma250e"}, - {"bma253"}, - {"bma255"}, - {"bma280"}, - {"bmc150_accel"}, - {"bmc156_accel", BOSCH_BMC156}, - {"bmi055_accel"}, + { .name = "bma222", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma222e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma250e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma253", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma255", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma280", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc150_accel", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc156_accel", .driver_data = BOSCH_BMC156 }, + { .name = "bmi055_accel", .driver_data = BOSCH_UNKNOWN }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 44cb50c76cb1..ae89e162e097 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -72,9 +72,9 @@ static const struct of_device_id bmi088_of_match[] = { MODULE_DEVICE_TABLE(of, bmi088_of_match); static const struct spi_device_id bmi088_accel_id[] = { - {"bmi085-accel", BOSCH_BMI085}, - {"bmi088-accel", BOSCH_BMI088}, - {"bmi090l-accel", BOSCH_BMI090L}, + { .name = "bmi085-accel", .driver_data = BOSCH_BMI085 }, + { .name = "bmi088-accel", .driver_data = BOSCH_BMI088 }, + { .name = "bmi090l-accel", .driver_data = BOSCH_BMI090L }, { } }; MODULE_DEVICE_TABLE(spi, bmi088_accel_id); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index fe35a1270786..6f0497ab6133 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define DMARD09_DRV_NAME "dmard09" @@ -79,6 +80,12 @@ static int dmard09_read_raw(struct iio_dev *indio_dev, *val = accel; return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + /* 1 g / 32 LSB, in m/s^2 */ + *val2 = IIO_G_TO_M_S_2(NANO / 32); + + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index d0c2a8daef0d..18d7b09bddd2 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -969,6 +969,8 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) if (!count) return 0; + count = min(count, FXLS8962AF_FIFO_LENGTH); + data->old_timestamp = data->timestamp; data->timestamp = iio_get_time_ns(indio_dev); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c index 8936032526a0..baf3c4352f14 100644 --- a/drivers/iio/accel/fxls8962af-spi.c +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -34,8 +34,8 @@ static const struct of_device_id fxls8962af_spi_of_match[] = { MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); static const struct spi_device_id fxls8962af_spi_id_table[] = { - { "fxls8962af", fxls8962af }, - { "fxls8964af", fxls8964af }, + { .name = "fxls8962af", .driver_data = fxls8962af }, + { .name = "fxls8964af", .driver_data = fxls8964af }, { } }; MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 737572bb44d9..eb521125f4eb 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -121,9 +121,8 @@ static const struct iio_chan_spec gravity_channels[] = { /* Channel read_raw handler */ static int accel_3d_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) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int report_id = -1; @@ -150,7 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, else { *val = 0; hid_sensor_power_state(&accel_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&accel_state->common_attributes, false); @@ -183,10 +182,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int accel_3d_write_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) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int ret = 0; @@ -222,8 +219,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, /* Callback handler to send event after all samples are received and captured */ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -246,9 +242,9 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -280,10 +276,10 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int accel_3d_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned usage_id, - struct accel_3d_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct accel_3d_state *st) { int ret; @@ -302,10 +298,10 @@ static int accel_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", - st->accel[0].index, - st->accel[0].report_id, - st->accel[1].index, st->accel[1].report_id, - st->accel[2].index, st->accel[2].report_id); + st->accel[0].index, + st->accel[0].report_id, + st->accel[1].index, st->accel[1].report_id, + st->accel[2].index, st->accel[2].report_id); st->scale_precision = hid_sensor_format_scale( hsdev->usage, @@ -328,7 +324,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct accel_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -365,8 +361,8 @@ static int hid_accel_3d_probe(struct platform_device *pdev) return -ENOMEM; } ret = accel_3d_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, accel_state); + (struct iio_chan_spec *)indio_dev->channels, + hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; @@ -379,32 +375,32 @@ static int hid_accel_3d_probe(struct platform_device *pdev) atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &accel_state->common_attributes); + &accel_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - accel_state->callbacks.send_event = accel_3d_proc_event; accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &accel_state->callbacks); + &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return ret; @@ -417,8 +413,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); } diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 39485572a76b..02dd1db7a646 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -1435,7 +1435,7 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi &kx022a_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(dev, indio_trig); if (ret) diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 31a26baba9f7..71da958d4051 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -36,7 +36,7 @@ static void kxsd9_spi_remove(struct spi_device *spi) } static const struct spi_device_id kxsd9_spi_id[] = { - {"kxsd9", 0}, + { .name = "kxsd9" }, { } }; MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index aca02e83f789..872c47acf882 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -28,8 +28,8 @@ static void mma7455_spi_remove(struct spi_device *spi) } static const struct spi_device_id mma7455_spi_ids[] = { - { "mma7455", 0 }, - { "mma7456", 0 }, + { .name = "mma7455" }, + { .name = "mma7456" }, { } }; MODULE_DEVICE_TABLE(spi, mma7455_spi_ids); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 7d9cbfa01360..7e01427fd9c4 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -420,10 +420,8 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) NULL, mma9551_event_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "mma9551_event", indio_dev); - if (ret < 0) { - dev_err(dev, "request irq %d failed\n", data->irqs[i]); + if (ret) return ret; - } dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), data->irqs[i]); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index ab43b1e0ff04..8e7aaac89d47 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1099,11 +1099,8 @@ static int mma9553_probe(struct i2c_client *client) mma9553_event_handler, IRQF_TRIGGER_RISING, "mma9553_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto out_poweroff; - } } ret = pm_runtime_set_active(&client->dev); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index e0e73b87cba8..caf9b1b6397f 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -1059,7 +1059,7 @@ static int msa311_setup_interrupts(struct msa311_priv *msa311) msa311_irq_thread, IRQF_ONESHOT, msa311->chip_name, indio_dev); if (err) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name); if (!trig) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 2034fe92bae3..a66fee828ec1 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -489,11 +489,8 @@ static int mxc4005_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "mxc4005_event", data->dready_trig); - if (ret) { - dev_err(&client->dev, - "failed to init threaded irq\n"); + if (ret) return ret; - } data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 573831199bba..a92a563f6a4b 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1504,10 +1504,10 @@ static int sca3000_probe(struct spi_device *spi) } static const struct spi_device_id sca3000_id[] = { - { "sca3000_d01", (kernel_ulong_t)&sca3000_chip_info_d01 }, - { "sca3000_e02", (kernel_ulong_t)&sca3000_chip_info_e02 }, - { "sca3000_e04", (kernel_ulong_t)&sca3000_chip_info_e04 }, - { "sca3000_e05", (kernel_ulong_t)&sca3000_chip_info_e05 }, + { .name = "sca3000_d01", .driver_data = (kernel_ulong_t)&sca3000_chip_info_d01 }, + { .name = "sca3000_e02", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e02 }, + { .name = "sca3000_e04", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e04 }, + { .name = "sca3000_e05", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e05 }, { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index 8380b237831c..67582cc34149 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -664,8 +664,8 @@ static const struct of_device_id sca3300_dt_ids[] = { MODULE_DEVICE_TABLE(of, sca3300_dt_ids); static const struct spi_device_id sca3300_ids[] = { - { "sca3300" }, - { "scl3300" }, + { .name = "sca3300" }, + { .name = "scl3300" }, { } }; MODULE_DEVICE_TABLE(spi, sca3300_ids); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index e6bf8d0d4ff5..171f241c0a30 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -146,26 +146,26 @@ static int st_accel_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_accel_id_table[] = { - { LIS3DH_ACCEL_DEV_NAME }, - { LSM330D_ACCEL_DEV_NAME }, - { LSM330DL_ACCEL_DEV_NAME }, - { LSM330DLC_ACCEL_DEV_NAME }, - { LIS331DLH_ACCEL_DEV_NAME }, - { LSM330_ACCEL_DEV_NAME }, - { LSM303AGR_ACCEL_DEV_NAME }, - { LIS2DH12_ACCEL_DEV_NAME }, - { LIS2DS12_ACCEL_DEV_NAME }, - { LIS3L02DQ_ACCEL_DEV_NAME }, - { LNG2DM_ACCEL_DEV_NAME }, - { H3LIS331DL_ACCEL_DEV_NAME }, - { LIS331DL_ACCEL_DEV_NAME }, - { LIS3LV02DL_ACCEL_DEV_NAME }, - { LIS2DW12_ACCEL_DEV_NAME }, - { LIS3DHH_ACCEL_DEV_NAME }, - { LIS3DE_ACCEL_DEV_NAME }, - { LIS302DL_ACCEL_DEV_NAME }, - { LSM303C_ACCEL_DEV_NAME }, - { IIS328DQ_ACCEL_DEV_NAME }, + { .name = LIS3DH_ACCEL_DEV_NAME }, + { .name = LSM330D_ACCEL_DEV_NAME }, + { .name = LSM330DL_ACCEL_DEV_NAME }, + { .name = LSM330DLC_ACCEL_DEV_NAME }, + { .name = LIS331DLH_ACCEL_DEV_NAME }, + { .name = LSM330_ACCEL_DEV_NAME }, + { .name = LSM303AGR_ACCEL_DEV_NAME }, + { .name = LIS2DH12_ACCEL_DEV_NAME }, + { .name = LIS2DS12_ACCEL_DEV_NAME }, + { .name = LIS3L02DQ_ACCEL_DEV_NAME }, + { .name = LNG2DM_ACCEL_DEV_NAME }, + { .name = H3LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS3LV02DL_ACCEL_DEV_NAME }, + { .name = LIS2DW12_ACCEL_DEV_NAME }, + { .name = LIS3DHH_ACCEL_DEV_NAME }, + { .name = LIS3DE_ACCEL_DEV_NAME }, + { .name = LIS302DL_ACCEL_DEV_NAME }, + { .name = LSM303C_ACCEL_DEV_NAME }, + { .name = IIS328DQ_ACCEL_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 8b5a3e0489e9..50840c3440c4 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -7,12 +7,20 @@ * IIO driver for STK8312; 7-bit I2C address: 0x3D. */ +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include #include + #include #include #include @@ -542,11 +550,8 @@ static int stk8312_probe(struct i2c_client *client) IRQF_ONESHOT, "stk8312_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index d0c53b8ac850..6c1e286c0a1d 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -7,11 +7,18 @@ * STK8BA50 7-bit I2C address: 0x18. */ +#include +#include +#include +#include #include #include -#include #include +#include +#include +#include #include + #include #include #include @@ -431,11 +438,8 @@ static int stk8ba50_probe(struct i2c_client *client) stk8ba50_data_rdy_trig_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "stk8ba50_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c index ff9bc5f06c18..938ba43c2c35 100644 --- a/drivers/iio/adc/88pm886-gpadc.c +++ b/drivers/iio/adc/88pm886-gpadc.c @@ -279,7 +279,7 @@ static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const static int pm886_gpadc_hw_enable(struct regmap *map) { - const u8 config[] = { + static const u8 config[] = { PM886_GPADC_CONFIG1_EN_ALL, PM886_GPADC_CONFIG2_EN_ALL, PM886_GPADC_GND_DET2_EN, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3755a81c1efd..415e519ad4eb 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -33,7 +33,8 @@ config AB8500_GPADC and USB voltages integral to the U8500 platform. config AD_SIGMA_DELTA - tristate + tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST + depends on SPI select IIO_BUFFER select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER @@ -945,6 +946,25 @@ config LTC2309 This driver can also be built as a module. If so, the module will be called ltc2309. +config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + depends on GPIOLIB + depends on PWM + select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM + help + Say yes here to build support for Analog Devices LTC2378-20 and + similar analog to digital converters. + + This driver can also be built as a module. If so, the module will + be called ltc2378. + config LTC2471 tristate "Linear Technology LTC2471 and LTC2473 ADC driver" depends on I2C @@ -1076,6 +1096,7 @@ config MAX1363 config MAX14001 tristate "Analog Devices MAX14001/MAX14002 ADC driver" depends on SPI + select REGMAP help Say yes here to build support for Analog Devices MAX14001/MAX14002 Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs. @@ -1086,6 +1107,7 @@ config MAX14001 config MAX34408 tristate "Maxim max34408/max344089 ADC driver" depends on I2C + select REGMAP_I2C help Say yes here to build support for Maxim max34408/max34409 current sense monitor with 8-bits ADC interface with overcurrent delay/threshold and @@ -1165,6 +1187,17 @@ config MCP3911 This driver can also be built as a module. If so, the module will be called mcp3911. +config MEDIATEK_MT6323_AUXADC + tristate "MediaTek MT6323 PMIC AUXADC driver" + depends on MFD_MT6397 + help + Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC. + This driver provides multiple channels for system monitoring, + such as battery voltage, PMIC temperature, and others. + + This driver can also be built as a module. If so, the module will be + called mt6323-auxadc. + config MEDIATEK_MT6359_AUXADC tristate "MediaTek MT6359 PMIC AUXADC driver" depends on MFD_MT6397 @@ -1789,6 +1822,18 @@ config TI_ADS1119 This driver can also be built as a module. If so, the module will be called ti-ads1119. +config TI_ADS112C14 + tristate "Texas Instruments ADS112C14/ADS122C14" + depends on I2C + select CRC8 + select REGMAP + help + If you say yes here you get support for Texas Instruments ADS112C14, + ADS122C14 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads112c14. + config TI_ADS124S08 tristate "Texas Instruments ADS124S08" depends on SPI @@ -1964,6 +2009,39 @@ config TWL6030_GPADC This driver can also be built as a module. If so, the module will be called twl6030-gpadc. +config VERSAL_SYSMON_CORE + tristate + select REGMAP + +config VERSAL_SYSMON + tristate "AMD Versal SysMon driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon). This driver provides voltage and temperature + monitoring through the IIO subsystem. + + The SysMon measures up to 160 supply voltages and reads up to + 64 temperature satellites distributed across the SoC. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon. + +config VERSAL_SYSMON_I2C + tristate "AMD Versal SysMon I2C driver" + depends on I2C + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon) via I2C interface. This driver enables voltage + and temperature monitoring when the Versal chip has SysMon + configured with I2C access. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon-i2c. + config VF610_ADC tristate "Freescale vf610 ADC driver" depends on HAS_IOMEM diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 707dd708912f..dcec0abb03b7 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2309) += ltc2309.o +obj-$(CONFIG_LTC2378) += ltc2378.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o @@ -100,6 +101,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3564) += mcp3564.o obj-$(CONFIG_MCP3911) += mcp3911.o +obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o @@ -153,6 +155,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o +obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o @@ -168,6 +171,9 @@ obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o +obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o +obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index c2f6f5f9812c..a33896b2cf97 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -1176,37 +1176,37 @@ static int ad4000_probe(struct spi_device *spi) } 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 }, - { "ad7685", (kernel_ulong_t)&ad7685_chip_info }, - { "ad7686", (kernel_ulong_t)&ad7686_chip_info }, - { "ad7687", (kernel_ulong_t)&ad7687_chip_info }, - { "ad7688", (kernel_ulong_t)&ad7688_chip_info }, - { "ad7690", (kernel_ulong_t)&ad7690_chip_info }, - { "ad7691", (kernel_ulong_t)&ad7691_chip_info }, - { "ad7693", (kernel_ulong_t)&ad7693_chip_info }, - { "ad7942", (kernel_ulong_t)&ad7942_chip_info }, - { "ad7946", (kernel_ulong_t)&ad7946_chip_info }, - { "ad7980", (kernel_ulong_t)&ad7980_chip_info }, - { "ad7982", (kernel_ulong_t)&ad7982_chip_info }, - { "ad7983", (kernel_ulong_t)&ad7983_chip_info }, - { "ad7984", (kernel_ulong_t)&ad7984_chip_info }, - { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info }, - { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info }, + { .name = "ad4000", .driver_data = (kernel_ulong_t)&ad4000_chip_info }, + { .name = "ad4001", .driver_data = (kernel_ulong_t)&ad4001_chip_info }, + { .name = "ad4002", .driver_data = (kernel_ulong_t)&ad4002_chip_info }, + { .name = "ad4003", .driver_data = (kernel_ulong_t)&ad4003_chip_info }, + { .name = "ad4004", .driver_data = (kernel_ulong_t)&ad4004_chip_info }, + { .name = "ad4005", .driver_data = (kernel_ulong_t)&ad4005_chip_info }, + { .name = "ad4006", .driver_data = (kernel_ulong_t)&ad4006_chip_info }, + { .name = "ad4007", .driver_data = (kernel_ulong_t)&ad4007_chip_info }, + { .name = "ad4008", .driver_data = (kernel_ulong_t)&ad4008_chip_info }, + { .name = "ad4010", .driver_data = (kernel_ulong_t)&ad4010_chip_info }, + { .name = "ad4011", .driver_data = (kernel_ulong_t)&ad4011_chip_info }, + { .name = "ad4020", .driver_data = (kernel_ulong_t)&ad4020_chip_info }, + { .name = "ad4021", .driver_data = (kernel_ulong_t)&ad4021_chip_info }, + { .name = "ad4022", .driver_data = (kernel_ulong_t)&ad4022_chip_info }, + { .name = "adaq4001", .driver_data = (kernel_ulong_t)&adaq4001_chip_info }, + { .name = "adaq4003", .driver_data = (kernel_ulong_t)&adaq4003_chip_info }, + { .name = "ad7685", .driver_data = (kernel_ulong_t)&ad7685_chip_info }, + { .name = "ad7686", .driver_data = (kernel_ulong_t)&ad7686_chip_info }, + { .name = "ad7687", .driver_data = (kernel_ulong_t)&ad7687_chip_info }, + { .name = "ad7688", .driver_data = (kernel_ulong_t)&ad7688_chip_info }, + { .name = "ad7690", .driver_data = (kernel_ulong_t)&ad7690_chip_info }, + { .name = "ad7691", .driver_data = (kernel_ulong_t)&ad7691_chip_info }, + { .name = "ad7693", .driver_data = (kernel_ulong_t)&ad7693_chip_info }, + { .name = "ad7942", .driver_data = (kernel_ulong_t)&ad7942_chip_info }, + { .name = "ad7946", .driver_data = (kernel_ulong_t)&ad7946_chip_info }, + { .name = "ad7980", .driver_data = (kernel_ulong_t)&ad7980_chip_info }, + { .name = "ad7982", .driver_data = (kernel_ulong_t)&ad7982_chip_info }, + { .name = "ad7983", .driver_data = (kernel_ulong_t)&ad7983_chip_info }, + { .name = "ad7984", .driver_data = (kernel_ulong_t)&ad7984_chip_info }, + { .name = "ad7988-1", .driver_data = (kernel_ulong_t)&ad7988_1_chip_info }, + { .name = "ad7988-5", .driver_data = (kernel_ulong_t)&ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4000_id); diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 9c5f19321e3b..e97400a1aa48 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -1763,13 +1763,13 @@ static const struct ad4030_chip_info adaq4224_chip_info = { }; static const struct spi_device_id ad4030_id_table[] = { - { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info }, - { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info }, - { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info }, - { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info }, - { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info }, - { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info }, - { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info }, + { .name = "ad4030-24", .driver_data = (kernel_ulong_t)&ad4030_24_chip_info }, + { .name = "ad4630-16", .driver_data = (kernel_ulong_t)&ad4630_16_chip_info }, + { .name = "ad4630-24", .driver_data = (kernel_ulong_t)&ad4630_24_chip_info }, + { .name = "ad4632-16", .driver_data = (kernel_ulong_t)&ad4632_16_chip_info }, + { .name = "ad4632-24", .driver_data = (kernel_ulong_t)&ad4632_24_chip_info }, + { .name = "adaq4216", .driver_data = (kernel_ulong_t)&adaq4216_chip_info }, + { .name = "adaq4224", .driver_data = (kernel_ulong_t)&adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4030_id_table); diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 0797d64aeaec..04cd6628ebff 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -135,6 +135,7 @@ #define AD4087_CHIP_ID 0x0057 #define AD4088_CHIP_ID 0x0058 #define AD4880_CHIP_ID 0x0059 +#define AD4883_CHIP_ID 0x005B #define AD4884_CHIP_ID 0x005C #define AD4080_MAX_CHANNELS 2 @@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = { AD4880_CHANNEL_DEFINE(20, 32, 1), }; +static const struct iio_chan_spec ad4883_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + static const struct iio_chan_spec ad4884_channels[] = { AD4880_CHANNEL_DEFINE(16, 16, 0), AD4880_CHANNEL_DEFINE(16, 16, 1), @@ -646,6 +652,16 @@ static const struct ad4080_chip_info ad4880_chip_info = { .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, }; +static const struct ad4080_chip_info ad4883_chip_info = { + .name = "ad4883", + .product_id = AD4883_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4883_channels, + .lvds_cnv_clk_cnt_max = 5, +}; + static const struct ad4080_chip_info ad4884_chip_info = { .name = "ad4884", .product_id = AD4884_CHIP_ID, @@ -697,6 +713,11 @@ static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) if (ret) return ret; + ret = iio_backend_data_size_set(st->back[ch], + st->info->channels[0].scan_type.realbits); + if (ret) + return ret; + if (!st->lvds_cnv_en) return 0; @@ -848,17 +869,18 @@ static int ad4080_probe(struct spi_device *spi) } static const struct spi_device_id ad4080_id[] = { - { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, - { "ad4081", (kernel_ulong_t)&ad4081_chip_info }, - { "ad4082", (kernel_ulong_t)&ad4082_chip_info }, - { "ad4083", (kernel_ulong_t)&ad4083_chip_info }, - { "ad4084", (kernel_ulong_t)&ad4084_chip_info }, - { "ad4085", (kernel_ulong_t)&ad4085_chip_info }, - { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, - { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, - { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, - { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, - { "ad4884", (kernel_ulong_t)&ad4884_chip_info }, + { .name = "ad4080", .driver_data = (kernel_ulong_t)&ad4080_chip_info }, + { .name = "ad4081", .driver_data = (kernel_ulong_t)&ad4081_chip_info }, + { .name = "ad4082", .driver_data = (kernel_ulong_t)&ad4082_chip_info }, + { .name = "ad4083", .driver_data = (kernel_ulong_t)&ad4083_chip_info }, + { .name = "ad4084", .driver_data = (kernel_ulong_t)&ad4084_chip_info }, + { .name = "ad4085", .driver_data = (kernel_ulong_t)&ad4085_chip_info }, + { .name = "ad4086", .driver_data = (kernel_ulong_t)&ad4086_chip_info }, + { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info }, + { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info }, + { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info }, + { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info }, + { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -874,6 +896,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4087", &ad4087_chip_info }, { .compatible = "adi,ad4088", &ad4088_chip_info }, { .compatible = "adi,ad4880", &ad4880_chip_info }, + { .compatible = "adi,ad4883", &ad4883_chip_info }, { .compatible = "adi,ad4884", &ad4884_chip_info }, { } }; diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 7f39f3484062..3ffc4b765e0e 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -2425,12 +2425,12 @@ static const struct of_device_id ad4130_of_match[] = { MODULE_DEVICE_TABLE(of, ad4130_of_match); static const struct spi_device_id ad4130_id_table[] = { - { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info }, - { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info }, - { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info }, - { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, - { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info }, - { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info }, + { .name = "ad4129-4", .driver_data = (kernel_ulong_t)&ad4129_4_chip_info }, + { .name = "ad4129-8", .driver_data = (kernel_ulong_t)&ad4129_8_chip_info }, + { .name = "ad4130-4", .driver_data = (kernel_ulong_t)&ad4130_4_chip_info }, + { .name = "ad4130", .driver_data = (kernel_ulong_t)&ad4130_8_chip_info }, + { .name = "ad4131-4", .driver_data = (kernel_ulong_t)&ad4131_4_chip_info }, + { .name = "ad4131-8", .driver_data = (kernel_ulong_t)&ad4131_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4130_id_table); diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index cb9d74316f6a..0490218bb0e9 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -472,7 +472,7 @@ static int ad4134_probe(struct spi_device *spi) } static const struct spi_device_id ad4134_id[] = { - { "ad4134" }, + { .name = "ad4134" }, { } }; MODULE_DEVICE_TABLE(spi, ad4134_id); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 627cbf5a37b0..f7757b3a931c 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -2979,9 +2979,9 @@ static int ad4170_probe(struct spi_device *spi) } static const struct spi_device_id ad4170_id_table[] = { - { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info }, - { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info }, - { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info }, + { .name = "ad4170-4", .driver_data = (kernel_ulong_t)&ad4170_chip_info }, + { .name = "ad4190-4", .driver_data = (kernel_ulong_t)&ad4190_chip_info }, + { .name = "ad4195-4", .driver_data = (kernel_ulong_t)&ad4195_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4170_id_table); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c index 940b042d743a..480d1798f17b 100644 --- a/drivers/iio/adc/ad4851.c +++ b/drivers/iio/adc/ad4851.c @@ -1285,15 +1285,15 @@ static const struct of_device_id ad4851_of_match[] = { }; static const struct spi_device_id ad4851_spi_id[] = { - { "ad4851", (kernel_ulong_t)&ad4851_info }, - { "ad4852", (kernel_ulong_t)&ad4852_info }, - { "ad4853", (kernel_ulong_t)&ad4853_info }, - { "ad4854", (kernel_ulong_t)&ad4854_info }, - { "ad4855", (kernel_ulong_t)&ad4855_info }, - { "ad4856", (kernel_ulong_t)&ad4856_info }, - { "ad4857", (kernel_ulong_t)&ad4857_info }, - { "ad4858", (kernel_ulong_t)&ad4858_info }, - { "ad4858i", (kernel_ulong_t)&ad4858i_info }, + { .name = "ad4851", .driver_data = (kernel_ulong_t)&ad4851_info }, + { .name = "ad4852", .driver_data = (kernel_ulong_t)&ad4852_info }, + { .name = "ad4853", .driver_data = (kernel_ulong_t)&ad4853_info }, + { .name = "ad4854", .driver_data = (kernel_ulong_t)&ad4854_info }, + { .name = "ad4855", .driver_data = (kernel_ulong_t)&ad4855_info }, + { .name = "ad4856", .driver_data = (kernel_ulong_t)&ad4856_info }, + { .name = "ad4857", .driver_data = (kernel_ulong_t)&ad4857_info }, + { .name = "ad4858", .driver_data = (kernel_ulong_t)&ad4858_info }, + { .name = "ad4858i", .driver_data = (kernel_ulong_t)&ad4858i_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4851_spi_id); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c index e93b8bb60e8e..465a8ba01a22 100644 --- a/drivers/iio/adc/ad7091r8.c +++ b/drivers/iio/adc/ad7091r8.c @@ -249,9 +249,9 @@ static const struct of_device_id ad7091r8_of_match[] = { MODULE_DEVICE_TABLE(of, ad7091r8_of_match); static const struct spi_device_id ad7091r8_spi_id[] = { - { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info }, - { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info }, - { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info }, + { .name = "ad7091r2", .driver_data = (kernel_ulong_t)&ad7091r2_init_info }, + { .name = "ad7091r4", .driver_data = (kernel_ulong_t)&ad7091r4_init_info }, + { .name = "ad7091r8", .driver_data = (kernel_ulong_t)&ad7091r8_init_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 19058d081418..fc5dbc624856 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -1683,8 +1683,8 @@ static const struct of_device_id ad7124_of_match[] = { MODULE_DEVICE_TABLE(of, ad7124_of_match); static const struct spi_device_id ad71124_ids[] = { - { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info }, - { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info }, + { .name = "ad7124-4", .driver_data = (kernel_ulong_t)&ad7124_4_chip_info }, + { .name = "ad7124-8", .driver_data = (kernel_ulong_t)&ad7124_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad71124_ids); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index f78e758706ed..eb47175a0528 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -2065,19 +2065,19 @@ static const struct of_device_id ad7173_of_match[] = { MODULE_DEVICE_TABLE(of, ad7173_of_match); static const struct spi_device_id ad7173_id_table[] = { - { "ad4111", (kernel_ulong_t)&ad4111_device_info }, - { "ad4112", (kernel_ulong_t)&ad4112_device_info }, - { "ad4113", (kernel_ulong_t)&ad4113_device_info }, - { "ad4114", (kernel_ulong_t)&ad4114_device_info }, - { "ad4115", (kernel_ulong_t)&ad4115_device_info }, - { "ad4116", (kernel_ulong_t)&ad4116_device_info }, - { "ad7172-2", (kernel_ulong_t)&ad7172_2_device_info }, - { "ad7172-4", (kernel_ulong_t)&ad7172_4_device_info }, - { "ad7173-8", (kernel_ulong_t)&ad7173_8_device_info }, - { "ad7175-2", (kernel_ulong_t)&ad7175_2_device_info }, - { "ad7175-8", (kernel_ulong_t)&ad7175_8_device_info }, - { "ad7176-2", (kernel_ulong_t)&ad7176_2_device_info }, - { "ad7177-2", (kernel_ulong_t)&ad7177_2_device_info }, + { .name = "ad4111", .driver_data = (kernel_ulong_t)&ad4111_device_info }, + { .name = "ad4112", .driver_data = (kernel_ulong_t)&ad4112_device_info }, + { .name = "ad4113", .driver_data = (kernel_ulong_t)&ad4113_device_info }, + { .name = "ad4114", .driver_data = (kernel_ulong_t)&ad4114_device_info }, + { .name = "ad4115", .driver_data = (kernel_ulong_t)&ad4115_device_info }, + { .name = "ad4116", .driver_data = (kernel_ulong_t)&ad4116_device_info }, + { .name = "ad7172-2", .driver_data = (kernel_ulong_t)&ad7172_2_device_info }, + { .name = "ad7172-4", .driver_data = (kernel_ulong_t)&ad7172_4_device_info }, + { .name = "ad7173-8", .driver_data = (kernel_ulong_t)&ad7173_8_device_info }, + { .name = "ad7175-2", .driver_data = (kernel_ulong_t)&ad7175_2_device_info }, + { .name = "ad7175-8", .driver_data = (kernel_ulong_t)&ad7175_8_device_info }, + { .name = "ad7176-2", .driver_data = (kernel_ulong_t)&ad7176_2_device_info }, + { .name = "ad7177-2", .driver_data = (kernel_ulong_t)&ad7177_2_device_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7173_id_table); diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c index 94b172dce866..071d26191b3c 100644 --- a/drivers/iio/adc/ad7191.c +++ b/drivers/iio/adc/ad7191.c @@ -535,7 +535,7 @@ static const struct of_device_id ad7191_of_match[] = { MODULE_DEVICE_TABLE(of, ad7191_of_match); static const struct spi_device_id ad7191_id_table[] = { - { "ad7191" }, + { .name = "ad7191" }, { } }; MODULE_DEVICE_TABLE(spi, ad7191_id_table); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 2cfad0bda752..d8f5886fcf69 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1448,11 +1448,11 @@ static const struct of_device_id ad7192_of_match[] = { MODULE_DEVICE_TABLE(of, ad7192_of_match); static const struct spi_device_id ad7192_ids[] = { - { "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, - { "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, - { "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] }, + { .name = "ad7190", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, + { .name = "ad7192", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, + { .name = "ad7193", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, + { .name = "ad7194", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, + { .name = "ad7195", .driver_data = (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 0ef36c249ab8..594a070c3745 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -453,8 +453,8 @@ static int ad7266_probe(struct spi_device *spi) } static const struct spi_device_id ad7266_id[] = { - { "ad7265", 0 }, - { "ad7266", 0 }, + { .name = "ad7265" }, + { .name = "ad7266" }, { } }; MODULE_DEVICE_TABLE(spi, ad7266_id); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 2972e706de92..505fa68ee539 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -1082,7 +1082,7 @@ static int ad7280_probe(struct spi_device *spi) } static const struct spi_device_id ad7280_id[] = { - { "ad7280a", 0 }, + { .name = "ad7280a" }, { } }; MODULE_DEVICE_TABLE(spi, ad7280_id); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index 0ba0fbad4f70..1d7bf46b80e2 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -299,7 +299,7 @@ static int ad7292_probe(struct spi_device *spi) } static const struct spi_device_id ad7292_id_table[] = { - { "ad7292", 0 }, + { .name = "ad7292" }, { } }; MODULE_DEVICE_TABLE(spi, ad7292_id_table); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 5fa76aec360e..c669022b5dec 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -353,7 +353,7 @@ 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 }, + { .name = "ad7298" }, { } }; MODULE_DEVICE_TABLE(spi, ad7298_id); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index 9f77990a03f9..2e90f125a797 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -2116,24 +2116,24 @@ static const struct of_device_id ad7380_of_match_table[] = { }; static const struct spi_device_id ad7380_id_table[] = { - { "ad7380", (kernel_ulong_t)&ad7380_chip_info }, - { "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 }, - { "ad7389-4", (kernel_ulong_t)&ad7389_4_chip_info }, - { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info }, - { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info }, - { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info }, + { .name = "ad7380", .driver_data = (kernel_ulong_t)&ad7380_chip_info }, + { .name = "ad7381", .driver_data = (kernel_ulong_t)&ad7381_chip_info }, + { .name = "ad7383", .driver_data = (kernel_ulong_t)&ad7383_chip_info }, + { .name = "ad7384", .driver_data = (kernel_ulong_t)&ad7384_chip_info }, + { .name = "ad7386", .driver_data = (kernel_ulong_t)&ad7386_chip_info }, + { .name = "ad7387", .driver_data = (kernel_ulong_t)&ad7387_chip_info }, + { .name = "ad7388", .driver_data = (kernel_ulong_t)&ad7388_chip_info }, + { .name = "ad7380-4", .driver_data = (kernel_ulong_t)&ad7380_4_chip_info }, + { .name = "ad7381-4", .driver_data = (kernel_ulong_t)&ad7381_4_chip_info }, + { .name = "ad7383-4", .driver_data = (kernel_ulong_t)&ad7383_4_chip_info }, + { .name = "ad7384-4", .driver_data = (kernel_ulong_t)&ad7384_4_chip_info }, + { .name = "ad7386-4", .driver_data = (kernel_ulong_t)&ad7386_4_chip_info }, + { .name = "ad7387-4", .driver_data = (kernel_ulong_t)&ad7387_4_chip_info }, + { .name = "ad7388-4", .driver_data = (kernel_ulong_t)&ad7388_4_chip_info }, + { .name = "ad7389-4", .driver_data = (kernel_ulong_t)&ad7389_4_chip_info }, + { .name = "adaq4370-4", .driver_data = (kernel_ulong_t)&adaq4370_4_chip_info }, + { .name = "adaq4380-4", .driver_data = (kernel_ulong_t)&adaq4380_4_chip_info }, + { .name = "adaq4381-4", .driver_data = (kernel_ulong_t)&adaq4381_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 21d3f6aae972..c007f453ae18 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -411,42 +411,42 @@ static int ad7476_probe(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { - { "ad7091", (kernel_ulong_t)&ad7091_chip_info }, - { "ad7091r", (kernel_ulong_t)&ad7091r_chip_info }, - { "ad7273", (kernel_ulong_t)&ad7273_chip_info }, - { "ad7274", (kernel_ulong_t)&ad7274_chip_info }, - { "ad7276", (kernel_ulong_t)&ad7276_chip_info }, - { "ad7277", (kernel_ulong_t)&ad7277_chip_info }, - { "ad7278", (kernel_ulong_t)&ad7278_chip_info }, - { "ad7466", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7467", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7468", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7475", (kernel_ulong_t)&ad7475_chip_info }, - { "ad7476", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7476a", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7477", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7477a", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7478", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7478a", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7495", (kernel_ulong_t)&ad7495_chip_info }, - { "ad7910", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7920", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7940", (kernel_ulong_t)&ad7940_chip_info }, - { "adc081s", (kernel_ulong_t)&adc081s_chip_info }, - { "adc101s", (kernel_ulong_t)&adc101s_chip_info }, - { "adc121s", (kernel_ulong_t)&adc121s_chip_info }, - { "ads7866", (kernel_ulong_t)&ads7866_chip_info }, - { "ads7867", (kernel_ulong_t)&ads7867_chip_info }, - { "ads7868", (kernel_ulong_t)&ads7868_chip_info }, - { "bd79105", (kernel_ulong_t)&bd79105_chip_info }, + { .name = "ad7091", .driver_data = (kernel_ulong_t)&ad7091_chip_info }, + { .name = "ad7091r", .driver_data = (kernel_ulong_t)&ad7091r_chip_info }, + { .name = "ad7273", .driver_data = (kernel_ulong_t)&ad7273_chip_info }, + { .name = "ad7274", .driver_data = (kernel_ulong_t)&ad7274_chip_info }, + { .name = "ad7276", .driver_data = (kernel_ulong_t)&ad7276_chip_info }, + { .name = "ad7277", .driver_data = (kernel_ulong_t)&ad7277_chip_info }, + { .name = "ad7278", .driver_data = (kernel_ulong_t)&ad7278_chip_info }, + { .name = "ad7466", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7467", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7468", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7475", .driver_data = (kernel_ulong_t)&ad7475_chip_info }, + { .name = "ad7476", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7476a", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7477", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7477a", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7478", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7478a", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7495", .driver_data = (kernel_ulong_t)&ad7495_chip_info }, + { .name = "ad7910", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7920", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7940", .driver_data = (kernel_ulong_t)&ad7940_chip_info }, + { .name = "adc081s", .driver_data = (kernel_ulong_t)&adc081s_chip_info }, + { .name = "adc101s", .driver_data = (kernel_ulong_t)&adc101s_chip_info }, + { .name = "adc121s", .driver_data = (kernel_ulong_t)&adc121s_chip_info }, + { .name = "ads7866", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ads7867", .driver_data = (kernel_ulong_t)&ads7867_chip_info }, + { .name = "ads7868", .driver_data = (kernel_ulong_t)&ads7868_chip_info }, + { .name = "bd79105", .driver_data = (kernel_ulong_t)&bd79105_chip_info }, /* * The ROHM BU79100G is identical to the TI's ADS7866 from the software * point of view. The binding document mandates the ADS7866 to be * marked as a fallback for the BU79100G, but we still need the SPI ID * here to make the module loading work. */ - { "bu79100g", (kernel_ulong_t)&ads7866_chip_info }, - { "ltc2314-14", (kernel_ulong_t)<c2314_14_chip_info }, + { .name = "bu79100g", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ltc2314-14", .driver_data = (kernel_ulong_t)<c2314_14_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7476_id); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 7e17ccbcedd0..1ebdfb8580ab 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -463,17 +463,17 @@ static int ad7606_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad7606_id_table[] = { - { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info }, - { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info }, - { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info }, - { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info }, - { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info }, - { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info }, - { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info }, - { "ad7607", (kernel_ulong_t)&ad7607_bus_info }, - { "ad7608", (kernel_ulong_t)&ad7608_bus_info }, - { "ad7609", (kernel_ulong_t)&ad7609_bus_info }, - { "ad7616", (kernel_ulong_t)&ad7616_bus_info }, + { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_bus_info }, + { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_bus_info }, + { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_bus_info }, + { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_bus_info }, + { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_bus_info }, + { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_bus_info }, + { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_bus_info }, + { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_bus_info }, + { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_bus_info }, + { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_bus_info }, + { .name = "ad7616", .driver_data = (kernel_ulong_t)&ad7616_bus_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7606_id_table); diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 9e4a66477d2d..92957196de59 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -285,12 +285,12 @@ 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 }, + { .name = "ad7766", .driver_data = ID_AD7766 }, + { .name = "ad7766-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7766-2", .driver_data = ID_AD7766_2 }, + { .name = "ad7767", .driver_data = ID_AD7766 }, + { .name = "ad7767-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7767-2", .driver_data = 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 598936e47fd2..e9060c1bbe6f 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -1918,10 +1918,10 @@ static int ad7768_probe(struct spi_device *spi) } static const struct spi_device_id ad7768_id_table[] = { - { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info }, - { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info }, - { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info }, - { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info }, + { .name = "ad7768-1", .driver_data = (kernel_ulong_t)&ad7768_chip_info }, + { .name = "adaq7767-1", .driver_data = (kernel_ulong_t)&adaq7767_chip_info }, + { .name = "adaq7768-1", .driver_data = (kernel_ulong_t)&adaq7768_chip_info }, + { .name = "adaq7769-1", .driver_data = (kernel_ulong_t)&adaq7769_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7768_id_table); diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 003e23d6e242..79fb38d0f400 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -142,7 +142,6 @@ struct ad7779_state { const struct ad7779_chip_info *chip_info; struct clk *mclk; struct iio_trigger *trig; - struct completion completion; unsigned int sampling_freq; enum ad7779_filter filter_enabled; struct iio_backend *back; @@ -842,8 +841,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, st->trig); if (ret) - return dev_err_probe(dev, ret, "request IRQ %d failed\n", - st->spi->irq); + return ret; ret = devm_iio_trigger_register(dev, st->trig); if (ret) @@ -851,8 +849,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev indio_dev->trig = iio_trigger_get(st->trig); - init_completion(&st->completion); - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &ad7779_trigger_handler, diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 26382b1f8c9e..1cfad70320fe 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -347,10 +347,10 @@ 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 }, + { .name = "ad7170", .driver_data = ID_AD7170 }, + { .name = "ad7171", .driver_data = ID_AD7171 }, + { .name = "ad7780", .driver_data = ID_AD7780 }, + { .name = "ad7781", .driver_data = ID_AD7781 }, { } }; MODULE_DEVICE_TABLE(spi, ad7780_id); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index bcdc19e799aa..c2688307bbba 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -458,11 +458,11 @@ static int ad7791_probe(struct spi_device *spi) } static const struct spi_device_id ad7791_spi_ids[] = { - { "ad7787", AD7787 }, - { "ad7788", AD7788 }, - { "ad7789", AD7789 }, - { "ad7790", AD7790 }, - { "ad7791", AD7791 }, + { .name = "ad7787", .driver_data = AD7787 }, + { .name = "ad7788", .driver_data = AD7788 }, + { .name = "ad7789", .driver_data = AD7789 }, + { .name = "ad7790", .driver_data = AD7790 }, + { .name = "ad7791", .driver_data = AD7791 }, { } }; MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index ea1c192e2142..b0f44c144f20 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -829,15 +829,15 @@ 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 }, + { .name = "ad7785", .driver_data = ID_AD7785 }, + { .name = "ad7792", .driver_data = ID_AD7792 }, + { .name = "ad7793", .driver_data = ID_AD7793 }, + { .name = "ad7794", .driver_data = ID_AD7794 }, + { .name = "ad7795", .driver_data = ID_AD7795 }, + { .name = "ad7796", .driver_data = ID_AD7796 }, + { .name = "ad7797", .driver_data = ID_AD7797 }, + { .name = "ad7798", .driver_data = ID_AD7798 }, + { .name = "ad7799", .driver_data = ID_AD7799 }, { } }; MODULE_DEVICE_TABLE(spi, ad7793_id); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 068171d54596..62aaedbce748 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -328,7 +328,7 @@ static int ad7887_probe(struct spi_device *spi) } static const struct spi_device_id ad7887_id[] = { - { "ad7887", ID_AD7887 }, + { .name = "ad7887", .driver_data = ID_AD7887 }, { } }; MODULE_DEVICE_TABLE(spi, ad7887_id); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index acc87d486aa4..44e014da9722 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -360,13 +360,13 @@ 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 }, + { .name = "ad7904", .driver_data = AD7904 }, + { .name = "ad7914", .driver_data = AD7914 }, + { .name = "ad7923", .driver_data = AD7924 }, + { .name = "ad7924", .driver_data = AD7924 }, + { .name = "ad7908", .driver_data = AD7908 }, + { .name = "ad7918", .driver_data = AD7918 }, + { .name = "ad7928", .driver_data = AD7928 }, { } }; MODULE_DEVICE_TABLE(spi, ad7923_id); diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index 7722cf9e8214..12656d3cdcec 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -865,9 +865,9 @@ static const struct of_device_id ad7944_of_match[] = { MODULE_DEVICE_TABLE(of, ad7944_of_match); static const struct spi_device_id ad7944_spi_id[] = { - { "ad7944", (kernel_ulong_t)&ad7944_chip_info }, - { "ad7985", (kernel_ulong_t)&ad7985_chip_info }, - { "ad7986", (kernel_ulong_t)&ad7986_chip_info }, + { .name = "ad7944", .driver_data = (kernel_ulong_t)&ad7944_chip_info }, + { .name = "ad7985", .driver_data = (kernel_ulong_t)&ad7985_chip_info }, + { .name = "ad7986", .driver_data = (kernel_ulong_t)&ad7986_chip_info }, { } }; diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ebc629bcfd4d..93a57a3d59b8 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -410,9 +410,9 @@ static const struct of_device_id ad7949_spi_of_id[] = { MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); static const struct spi_device_id ad7949_spi_id[] = { - { "ad7949", ID_AD7949 }, - { "ad7682", ID_AD7682 }, - { "ad7689", ID_AD7689 }, + { .name = "ad7949", .driver_data = ID_AD7949 }, + { .name = "ad7682", .driver_data = ID_AD7682 }, + { .name = "ad7689", .driver_data = ID_AD7689 }, { } }; MODULE_DEVICE_TABLE(spi, ad7949_spi_id); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 0c377f9a7f25..34b7b1256f0d 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -1406,13 +1406,13 @@ static const struct of_device_id ad9467_of_match[] = { MODULE_DEVICE_TABLE(of, ad9467_of_match); static const struct spi_device_id ad9467_ids[] = { - { "ad9211", (kernel_ulong_t)&ad9211_chip_tbl }, - { "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, }, + { .name = "ad9211", .driver_data = (kernel_ulong_t)&ad9211_chip_tbl }, + { .name = "ad9265", .driver_data = (kernel_ulong_t)&ad9265_chip_tbl }, + { .name = "ad9434", .driver_data = (kernel_ulong_t)&ad9434_chip_tbl }, + { .name = "ad9467", .driver_data = (kernel_ulong_t)&ad9467_chip_tbl }, + { .name = "ad9643", .driver_data = (kernel_ulong_t)&ad9643_chip_tbl }, + { .name = "ad9649", .driver_data = (kernel_ulong_t)&ad9649_chip_tbl }, + { .name = "ad9652", .driver_data = (kernel_ulong_t)&ad9652_chip_tbl }, { } }; MODULE_DEVICE_TABLE(spi, ad9467_ids); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c0a9babfa519..c6c3ea953fea 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * ADE9000 driver * * Copyright 2025 Analog Devices Inc. @@ -1768,7 +1768,7 @@ static int ade9000_probe(struct spi_device *spi) }; static const struct spi_device_id ade9000_id[] = { - { "ade9000", 0 }, + { .name = "ade9000" }, { } }; MODULE_DEVICE_TABLE(spi, ade9000_id); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 26b9c75bd4d8..589f03618ecf 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -53,6 +53,10 @@ #define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 #define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 #define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0) +#define AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(3, 2) +#define AXI_AD408X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD408X_PACKET_FORMAT_16BIT 0x1 +#define AXI_AD408X_PACKET_FORMAT_14BIT 0x2 #define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068 #define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0) @@ -436,6 +440,31 @@ static int axi_adc_ad408x_filter_type_set(struct iio_backend *back, AXI_AD408X_CNTRL_3_FILTER_EN_MSK); } +static int axi_adc_ad408x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + case 20: + val = AXI_AD408X_PACKET_FORMAT_20BIT; + break; + case 16: + val = AXI_AD408X_PACKET_FORMAT_16BIT; + break; + case 14: + val = AXI_AD408X_PACKET_FORMAT_14BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + static int axi_adc_ad408x_interface_data_align(struct iio_backend *back, u32 timeout_us) { @@ -659,6 +688,7 @@ static const struct iio_backend_ops adi_ad408x_ops = { .free_buffer = axi_adc_free_buffer, .data_sample_trigger = axi_adc_data_sample_trigger, .filter_type_set = axi_adc_ad408x_filter_type_set, + .data_size_set = axi_adc_ad408x_data_size_set, .interface_data_align = axi_adc_ad408x_interface_data_align, .num_lanes_set = axi_adc_num_lanes_set, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index f610ad729bf3..0ac74ad92fca 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -988,7 +988,7 @@ static int at91_adc_probe(struct platform_device *pdev) struct iio_dev *idev; struct at91_adc_state *st; u32 reg, prop; - char *s; + const char *s; idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state)); if (!idev) @@ -1023,7 +1023,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->res = st->caps->high_res_bits; if (st->caps->low_res_bits && - !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s) + !of_property_read_string(node, "atmel,adc-use-res", &s) && !strcmp(s, "lowres")) st->res = st->caps->low_res_bits; diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index 30672e584c10..aace7e103770 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -405,3 +405,4 @@ module_platform_driver(envelope_detector_driver); MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index b01ba86c2945..9fa63de4b9ca 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -526,7 +526,7 @@ static const struct of_device_id hi8435_dt_ids[] = { MODULE_DEVICE_TABLE(of, hi8435_dt_ids); static const struct spi_device_id hi8435_id[] = { - { "hi8435", 0 }, + { .name = "hi8435" }, { } }; MODULE_DEVICE_TABLE(spi, hi8435_id); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 17e9badf1d1c..00adafa7cbd7 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -4,6 +4,8 @@ * * Copyright (c) 2016 Andreas Klinger */ +#include +#include #include #include #include @@ -20,6 +22,8 @@ #include #include +#define HX711_DATA_BITS 24 + /* gain to pulse and scale conversion */ #define HX711_GAIN_MAX 3 #define HX711_RESET_GAIN 128 @@ -73,6 +77,20 @@ static int hx711_get_scale_to_gain(const int *gain_scale, int scale) return -EINVAL; } +/** + * struct hx711_chip_info - per-variant static configuration + * @name: IIO device name + * @channels: channel specification array + * @num_channels: number of entries in @channels + * @iio_info: IIO info ops for this variant + */ +struct hx711_chip_info { + const char *name; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + unsigned int num_channels; + const struct iio_info *iio_info; +}; + struct hx711_data { struct device *dev; struct gpio_desc *gpiod_pd_sck; @@ -80,6 +98,7 @@ struct hx711_data { int gain_set; /* gain set on device */ int gain_chan_a; /* gain for channel A */ int gain_scale[HX711_GAIN_MAX]; + const struct hx711_chip_info *chip_info; struct mutex lock; /* * triggered buffer @@ -137,17 +156,18 @@ static int hx711_cycle(struct hx711_data *hx711_data) return gpiod_get_value(hx711_data->gpiod_dout); } -static int hx711_read(struct hx711_data *hx711_data) +static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) { - int i, ret; int value = 0; - int val = gpiod_get_value(hx711_data->gpiod_dout); + int val; + int ret; /* we double check if it's really down */ + val = gpiod_get_value(hx711_data->gpiod_dout); if (val) return -EIO; - for (i = 0; i < 24; i++) { + for (int i = 0; i < HX711_DATA_BITS; i++) { value <<= 1; ret = hx711_cycle(hx711_data); if (ret) @@ -156,7 +176,7 @@ static int hx711_read(struct hx711_data *hx711_data) value ^= 0x800000; - for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++) + for (int i = 0; i < trailing_pulses; i++) hx711_cycle(hx711_data); return value; @@ -186,8 +206,9 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data) static int hx711_reset(struct hx711_data *hx711_data) { - int val = hx711_wait_for_ready(hx711_data); + int val; + val = hx711_wait_for_ready(hx711_data); if (val) { /* * an examination with the oszilloscope indicated @@ -219,7 +240,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set == 32) { hx711_data->gain_set = hx711_data->gain_chan_a; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -231,7 +253,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set != 32) { hx711_data->gain_set = 32; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -244,10 +267,26 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) return 0; } -static int hx711_reset_read(struct hx711_data *hx711_data, int chan) +static int hx711_set_hx711_channel(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan, + int *trailing_pulses) { int ret; - int val; + + ret = hx711_set_gain_for_channel(hx711_data, chan->channel); + if (ret < 0) + return ret; + + *trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set); + + return 0; +} + +static int hx711_reset_read(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan) +{ + int trailing_pulses; + int ret; /* * hx711_reset() must be called from here @@ -258,13 +297,11 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan) return -EIO; } - ret = hx711_set_gain_for_channel(hx711_data, chan); + ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses); if (ret < 0) return ret; - val = hx711_read(hx711_data); - - return val; + return hx711_read(hx711_data, trailing_pulses); } static int hx711_read_raw(struct iio_dev *indio_dev, @@ -277,7 +314,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: mutex_lock(&hx711_data->lock); - *val = hx711_reset_read(hx711_data, chan->channel); + *val = hx711_reset_read(hx711_data, chan); mutex_unlock(&hx711_data->lock); @@ -331,7 +368,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev, if (gain != 32) hx711_data->gain_chan_a = gain; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) { mutex_unlock(&hx711_data->lock); return ret; @@ -366,8 +404,8 @@ static irqreturn_t hx711_trigger(int irq, void *p) memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer)); iio_for_each_active_channel(indio_dev, i) { - hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data, - indio_dev->channels[i].channel); + hx711_data->buffer.channel[j] = + hx711_reset_read(hx711_data, &indio_dev->channels[i]); j++; } @@ -455,8 +493,16 @@ static const struct iio_chan_spec hx711_chan_spec[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; +static const struct hx711_chip_info hx711_chip = { + .name = "hx711", + .channels = hx711_chan_spec, + .iio_info = &hx711_iio_info, + .num_channels = ARRAY_SIZE(hx711_chan_spec), +}; + static int hx711_probe(struct platform_device *pdev) { + const struct hx711_chip_info *chip_info; struct device *dev = &pdev->dev; struct hx711_data *hx711_data; struct iio_dev *indio_dev; @@ -472,6 +518,12 @@ static int hx711_probe(struct platform_device *pdev) mutex_init(&hx711_data->lock); + chip_info = device_get_match_data(dev); + if (!chip_info) + return dev_err_probe(dev, -ENODEV, "missing driver data\n"); + + hx711_data->chip_info = chip_info; + /* * PD_SCK stands for power down and serial clock input of HX711 * in the driver it is an output @@ -533,11 +585,11 @@ static int hx711_probe(struct platform_device *pdev) hx711_data->data_ready_delay_ns = 1000000000 / hx711_data->clock_frequency; - indio_dev->name = "hx711"; - indio_dev->info = &hx711_iio_info; + indio_dev->name = chip_info->name; + indio_dev->info = chip_info->iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = hx711_chan_spec; - indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, @@ -554,7 +606,7 @@ static int hx711_probe(struct platform_device *pdev) } static const struct of_device_id of_hx711_match[] = { - { .compatible = "avia,hx711", }, + { .compatible = "avia,hx711", .data = &hx711_chip }, { } }; diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c new file mode 100644 index 000000000000..b0ff2be1aee0 --- /dev/null +++ b/drivers/iio/adc/ltc2378.c @@ -0,0 +1,857 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices LTC2378 ADC series driver + * + * Copyright (C) 2026 Analog Devices Inc. + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LTC2378_TDSDOBUSYL_NS 5 +#define LTC2378_TBUSYLH_NS 13 +#define LTC2378_TCNV_HIGH_NS 20 +#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */ + +#define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = _storage_bits - _real_bits, \ + .endianness = IIO_BE, \ + }, \ +} + +#define LTC2378_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +#define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +#define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = 0, \ + .endianness = IIO_CPU, \ + }, \ +} + +/* + * Currently, the available offload hardware + DMA configuration only supports + * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit + * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes + * with data and 2 bytes always zeroed out. Nevertheless, for the offload use + * case, the IIO buffer is configured for 32 storage bits in CPU endianness so + * data is correctly aligned in user space despite 2 out of the 4 bytes being + * zeros. + */ +#define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32) + +#define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32) + +struct ltc2378_chip_info { + const char *name; + unsigned int internal_ref_uV; + struct u32_fract internal_div; + struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */ + struct iio_chan_spec offload_chan; + unsigned int max_sample_rate_Hz; + unsigned int tconv_ns; +}; + +struct ltc2378_state { + const struct ltc2378_chip_info *info; + struct gpio_desc *cnv_gpio; + struct spi_device *spi; + struct mutex lock; /* Protect data acquisition cycle */ + int ref_uV; + struct spi_transfer xfer; + struct spi_transfer offload_xfer; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct pwm_waveform cnv_wf; + struct spi_message offload_msg; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + unsigned int cnv_Hz; + unsigned int sample_freq_range[3]; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + struct { + union { + __be16 sample_buf16_be; + __be32 sample_buf32_be; + u16 sample_buf16; + u32 sample_buf32; + } data; + aligned_s64 timestamp; + } scan __aligned(IIO_DMA_MINALIGN); +}; + +static const struct ltc2378_chip_info ltc2338_18_chip_info = { + .name = "ltc2338-18", + .internal_ref_uV = 2048000, + .internal_div = { .numerator = 5, .denominator = 2 }, + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2364_16_chip_info = { + .name = "ltc2364-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2364_18_chip_info = { + .name = "ltc2364-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2367_16_chip_info = { + .name = "ltc2367-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2367_18_chip_info = { + .name = "ltc2367-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2368_16_chip_info = { + .name = "ltc2368-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2368_18_chip_info = { + .name = "ltc2368-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2369_18_chip_info = { + .name = "ltc2369-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, +}; + +static const struct ltc2378_chip_info ltc2370_16_chip_info = { + .name = "ltc2370-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, +}; + +static const struct ltc2378_chip_info ltc2376_16_chip_info = { + .name = "ltc2376-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2376_18_chip_info = { + .name = "ltc2376-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2376_20_chip_info = { + .name = "ltc2376-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2377_16_chip_info = { + .name = "ltc2377-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2377_18_chip_info = { + .name = "ltc2377-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2377_20_chip_info = { + .name = "ltc2377-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2378_16_chip_info = { + .name = "ltc2378-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2378_18_chip_info = { + .name = "ltc2378-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2378_20_chip_info = { + .name = "ltc2378-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 675, +}; + +static const struct ltc2378_chip_info ltc2379_18_chip_info = { + .name = "ltc2379-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, +}; + +static const struct ltc2378_chip_info ltc2380_16_chip_info = { + .name = "ltc2380-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, +}; + +static int ltc2378_convert_and_acquire(struct ltc2378_state *st) +{ + int ret; + + /* Cause a rising edge of CNV to initiate a new ADC conversion */ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + fsleep(LTC2378_MAX_DATA_WAIT_US); + ret = spi_sync_transfer(st->spi, &st->xfer, 1); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + + return ret; +} + +static irqreturn_t ltc2378_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = ltc2378_convert_and_acquire(st); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, + struct ltc2378_state *st, int *val) +{ + const struct iio_scan_type *scan_type = &chan->scan_type; + u32 sample; + int ret; + + guard(mutex)(&st->lock); + ret = ltc2378_convert_and_acquire(st); + if (ret) + return ret; + + if (chan->scan_type.endianness == IIO_BE) { + if (chan->scan_type.realbits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32_be); + else + sample = be16_to_cpu(st->scan.data.sample_buf16_be); + } else { /* IIO_CPU */ + if (chan->scan_type.realbits > 16) + sample = st->scan.data.sample_buf32; + else + sample = st->scan.data.sample_buf16; + } + + sample >>= chan->scan_type.shift; + + if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT) + *val = sign_extend32(sample, scan_type->realbits - 1); + else + *val = sample; + + return 0; +} + +static int ltc2378_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ltc2378_channel_single_read(chan, st, val); + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: { + struct u32_fract fract = st->info->internal_div; + *val = st->ref_uV / MILLI; + if (fract.numerator && fract.denominator) + *val = mult_frac(*val, fract.numerator, fract.denominator); + /* + * For all LTC2378-like devices, the amount of bits that express + * voltage magnitude depend on the polarity / output code format: + * - straight binary: All precision/resolution bits are used. + * - 2's complement: One of the precision bits is used for sign. + */ + if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT) + *val2 = chan->scan_type.realbits - 1; + else + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->cnv_Hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ltc2378_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +/* + * SPI offload wiring schema + * + * +-------------+ +-------------+ + * | CNV |<-----+--| GPIO | + * | | +--| PWM0 | + * | | | | + * | | +--| PWM1 | + * | | | +-------------+ + * | | +->| TRIGGER | + * | | | | + * | ADC | | SPI | + * | | | controller | + * | | | | + * | SDI |<--------| SDO | + * | SDO |-------->| SDI | + * | SCLK |<--------| SCLK | + * +-------------+ +-------------+ + * + */ +static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz) +{ + struct spi_offload_trigger_config config = st->offload_trigger_config; + unsigned int min_read_offset, offload_period_ns; + struct pwm_waveform cnv_wf = { }; + u64 target = LTC2378_TCNV_HIGH_NS; + unsigned int count; + u64 offload_offset_ns; + int ret; + + if (freq_Hz == 0) + return -EINVAL; + + if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz)) + return -ERANGE; + + /* Configure CNV PWM waveform */ + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz); + + /* + * Ensure CNV high time meets minimum requirement (20ns). The PWM + * hardware may round the duty cycle, so iterate until we get at least + * the minimum required high time (or reach a try count limit). + */ + count = 100; + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += 10; /* Increment by PWM duty cycle period */ + } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS); + + /* Check the minimum CNV high time is met */ + if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS) + return -EDOM; + + /* + * Configure SPI offload PWM trigger. + * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. + * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) + * + * Use the same period as CNV PWM to avoid timing issues. + * Convert back from period to frequency for the SPI offload API. + */ + offload_period_ns = cnv_wf.period_length_ns; + config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns); + min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS; + offload_offset_ns = min_read_offset; + count = 100; + do { + config.periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + offload_offset_ns += 10; + } while (count-- && config.periodic.offset_ns < min_read_offset); + + /* Check the minimum CNV to SCLK delay is met */ + if (config.periodic.offset_ns < min_read_offset) + return -EDOM; + + /* Check the PWM periods remain the same */ + offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz); + if (cnv_wf.period_length_ns != offload_period_ns) + return -EDOM; + + st->offload_trigger_config = config; + st->cnv_wf = cnv_wf; + st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns); + + return 0; +} + +static int ltc2378_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ltc2378_update_conversion_rate(st, val); + default: + return -EINVAL; + } +} + +static const struct iio_info ltc2378_iio_info = { + .read_raw = <c2378_read_raw, +}; + +static const struct iio_info ltc2378_offload_iio_info = { + .read_raw = <c2378_read_raw, + .read_avail = <c2378_read_avail, + .write_raw = <c2378_write_raw, +}; + +static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true); + if (ret) + return ret; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_pwm_disable; + + return 0; + +out_pwm_disable: + pwm_disable(st->cnv_trigger); + return ret; +} + +static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = { + .postenable = <c2378_offload_buffer_postenable, + .predisable = <c2378_offload_buffer_predisable, +}; + +static int ltc2378_prepare_offload_message(struct device *dev, + struct ltc2378_state *st) +{ + unsigned int resolution = st->info->offload_chan.scan_type.realbits; + + st->offload_xfer.bits_per_word = resolution; + st->offload_xfer.len = spi_bpw_to_bytes(resolution); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + /* Initialize message with offload */ + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(dev, st->spi, &st->offload_msg); +} + +static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev, + struct ltc2378_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = <c2378_offload_buffer_ops; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +static int ltc2378_pwm_get(struct ltc2378_state *st) +{ + struct device *dev = &st->spi->dev; + + st->cnv_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), + "failed to get cnv pwm\n"); + + /* + * Disable the PWM connected to CNV in case it was left running by + * something else. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct spi_offload_config ltc2378_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + /* + * The internal reference buffer amplifies both the internal reference + * and REFIN by a factor of 2. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret == -ENODEV) { /* refin is optional */ + st->ref_uV = st->info->internal_ref_uV * 2; + return 0; + } + + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read refin regulator\n"); + + st->ref_uV = ret * 2; + + return 0; +} + +static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read ref regulator\n"); + + st->ref_uV = ret; + + return 0; +} + +static int ltc2378_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ltc2378_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + + if (st->info->internal_ref_uV) + ret = ltc2378_refin_setup(dev, st); + else + ret = ltc2378_ref_setup(dev, st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "failed to get CNV GPIO"); + + st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + indio_dev->info = <c2378_iio_info; + indio_dev->channels = st->info->chan; + indio_dev->num_channels = ARRAY_SIZE(st->info->chan); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ltc2378_trigger_handler, + NULL); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + indio_dev->info = <c2378_offload_iio_info; + indio_dev->channels = &st->info->offload_chan; + indio_dev->num_channels = 1; + ret = ltc2378_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup SPI offload\n"); + + ret = ltc2378_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "failed to get PWM\n"); + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */ + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sample averaging and the sampling frequency + * without hitting the maximum conversion rate. + */ + ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4); + if (ret) + return dev_err_probe(dev, ret, + "failed to set offload samp freq\n"); + + ret = ltc2378_prepare_offload_message(&spi->dev, st); + if (ret) + return dev_err_probe(dev, ret, "failed to optimize SPI message\n"); + + /* + * Set single-read transfer bits_per_word so the SPI subsystem + * rearranges data to CPU endianness, enabling us to reuse + * offload_chan specifications for single-shot reads. + */ + st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits; + } + + st->xfer.rx_buf = &st->scan.data; + st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ltc2378_of_match[] = { + { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info }, + { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info }, + { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info }, + { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info }, + { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info }, + { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info }, + { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info }, + { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info }, + { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info }, + { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info }, + { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info }, + { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info }, + { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info }, + { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info }, + { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info }, + { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info }, + { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info }, + { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info }, + { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info }, + { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2378_of_match); + +static const struct spi_device_id ltc2378_spi_id[] = { + { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info }, + { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info }, + { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info }, + { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info }, + { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info }, + { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info }, + { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info }, + { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info }, + { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info }, + { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info }, + { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info }, + { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info }, + { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info }, + { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info }, + { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info }, + { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info }, + { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info }, + { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info }, + { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info }, + { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ltc2378_spi_id); + +static struct spi_driver ltc2378_driver = { + .driver = { + .name = "ltc2378", + .of_match_table = ltc2378_of_match + }, + .probe = ltc2378_probe, + .id_table = ltc2378_spi_id, +}; +module_spi_driver(ltc2378_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("SPI_OFFLOAD"); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 8b2c7b2a9c8f..fb6f12316724 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -72,12 +72,12 @@ enum max1027_id { }; static const struct spi_device_id max1027_id[] = { - { "max1027", max1027 }, - { "max1029", max1029 }, - { "max1031", max1031 }, - { "max1227", max1227 }, - { "max1229", max1229 }, - { "max1231", max1231 }, + { .name = "max1027", .driver_data = max1027 }, + { .name = "max1029", .driver_data = max1029 }, + { .name = "max1031", .driver_data = max1031 }, + { .name = "max1227", .driver_data = max1227 }, + { .name = "max1229", .driver_data = max1229 }, + { .name = "max1231", .driver_data = max1231 }, { } }; MODULE_DEVICE_TABLE(spi, max1027_id); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index d394e03cc4e8..75e51ce3077e 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -256,9 +256,9 @@ static int max1118_probe(struct spi_device *spi) } static const struct spi_device_id max1118_id[] = { - { "max1117", max1117 }, - { "max1118", max1118 }, - { "max1119", max1119 }, + { .name = "max1117", .driver_data = max1117 }, + { .name = "max1118", .driver_data = max1118 }, + { .name = "max1119", .driver_data = max1119 }, { } }; MODULE_DEVICE_TABLE(spi, max1118_id); diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c index 6c803df220b6..63b6561dcf47 100644 --- a/drivers/iio/adc/max11205.c +++ b/drivers/iio/adc/max11205.c @@ -145,8 +145,8 @@ static int max11205_probe(struct spi_device *spi) } static const struct spi_device_id max11205_spi_ids[] = { - { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, - { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, + { .name = "max11205a", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, + { .name = "max11205b", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, { } }; MODULE_DEVICE_TABLE(spi, max11205_spi_ids); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index dc1b96356592..23ae684cf08b 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -1025,7 +1025,7 @@ static const struct of_device_id max11410_spi_of_id[] = { MODULE_DEVICE_TABLE(of, max11410_spi_of_id); static const struct spi_device_id max11410_id[] = { - { "max11410" }, + { .name = "max11410" }, { } }; MODULE_DEVICE_TABLE(spi, max11410_id); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index d62c1a011659..b3cecbf9a89e 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -15,10 +15,6 @@ #define MAX1241_VAL_MASK GENMASK(11, 0) #define MAX1241_SHUTDOWN_DELAY_USEC 4 -enum max1241_id { - max1241, -}; - struct max1241 { struct spi_device *spi; struct mutex lock; @@ -166,7 +162,7 @@ static int max1241_probe(struct spi_device *spi) else dev_dbg(dev, "no shutdown pin passed, low-power mode disabled"); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = "max1241"; indio_dev->info = &max1241_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max1241_channels; @@ -176,9 +172,10 @@ static int max1241_probe(struct spi_device *spi) } static const struct spi_device_id max1241_id[] = { - { "max1241", max1241 }, + { .name = "max1241" }, { } }; +MODULE_DEVICE_TABLE(spi, max1241_id); static const struct of_device_id max1241_dt_ids[] = { { .compatible = "maxim,max1241" }, diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c index 09017163b191..58adeac62dde 100644 --- a/drivers/iio/adc/max14001.c +++ b/drivers/iio/adc/max14001.c @@ -362,8 +362,8 @@ static struct max14001_chip_info max14002_chip_info = { }; static const struct spi_device_id max14001_id_table[] = { - { "max14001", (kernel_ulong_t)&max14001_chip_info }, - { "max14002", (kernel_ulong_t)&max14002_chip_info }, + { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info }, + { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info }, { } }; diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 686c519aa3d2..18cb957e7095 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -476,19 +476,19 @@ static const struct of_device_id mcp320x_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); static const struct spi_device_id mcp320x_id[] = { - { "mcp3001", mcp3001 }, - { "mcp3002", mcp3002 }, - { "mcp3004", mcp3004 }, - { "mcp3008", mcp3008 }, - { "mcp3201", mcp3201 }, - { "mcp3202", mcp3202 }, - { "mcp3204", mcp3204 }, - { "mcp3208", mcp3208 }, - { "mcp3301", mcp3301 }, - { "mcp3550-50", mcp3550_50 }, - { "mcp3550-60", mcp3550_60 }, - { "mcp3551", mcp3551 }, - { "mcp3553", mcp3553 }, + { .name = "mcp3001", .driver_data = mcp3001 }, + { .name = "mcp3002", .driver_data = mcp3002 }, + { .name = "mcp3004", .driver_data = mcp3004 }, + { .name = "mcp3008", .driver_data = mcp3008 }, + { .name = "mcp3201", .driver_data = mcp3201 }, + { .name = "mcp3202", .driver_data = mcp3202 }, + { .name = "mcp3204", .driver_data = mcp3204 }, + { .name = "mcp3208", .driver_data = mcp3208 }, + { .name = "mcp3301", .driver_data = mcp3301 }, + { .name = "mcp3550-50", .driver_data = mcp3550_50 }, + { .name = "mcp3550-60", .driver_data = mcp3550_60 }, + { .name = "mcp3551", .driver_data = mcp3551 }, + { .name = "mcp3553", .driver_data = mcp3553 }, { } }; MODULE_DEVICE_TABLE(spi, mcp320x_id); diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 36675563829e..259ac4ebdedf 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -1451,18 +1451,18 @@ static const struct of_device_id mcp3564_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); static const struct spi_device_id mcp3564_id[] = { - { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, - { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, - { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, - { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, - { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, - { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, - { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, - { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, - { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, - { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, - { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, - { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { .name = "mcp3461", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, + { .name = "mcp3462", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, + { .name = "mcp3464", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, + { .name = "mcp3561", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, + { .name = "mcp3562", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, + { .name = "mcp3564", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, + { .name = "mcp3461r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, + { .name = "mcp3462r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, + { .name = "mcp3464r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, + { .name = "mcp3561r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, + { .name = "mcp3562r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, + { .name = "mcp3564r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, { } }; MODULE_DEVICE_TABLE(spi, mcp3564_id); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 5bf74b49bdb5..797424f59f74 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -927,13 +927,13 @@ static const struct of_device_id mcp3911_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); static const struct spi_device_id mcp3911_id[] = { - { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, - { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, - { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, - { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, - { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, - { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, - { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, + { .name = "mcp3910", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, + { .name = "mcp3911", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, + { .name = "mcp3912", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, + { .name = "mcp3913", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, + { .name = "mcp3914", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, + { .name = "mcp3918", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, + { .name = "mcp3919", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, { } }; MODULE_DEVICE_TABLE(spi, mcp3911_id); diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c new file mode 100644 index 000000000000..4f73ac42abb7 --- /dev/null +++ b/drivers/iio/adc/mt6323-auxadc.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Roman Vivchar + * + * Based on drivers/iio/adc/mt6359-auxadc.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7) +#define AUXADC_STRUP_CON10_RSTB_SW BIT(5) + +#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5) + +#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10) +#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8) +#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4) +#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2) + +#define AUXADC_CON27_VREF18_ENB_MD BIT(15) +#define AUXADC_CON27_MD_STATUS BIT(0) + +#define AUXADC_CON19_GPS_STATUS BIT(1) + +#define AUXADC_CON26_VREF18_SELB BIT(1) +#define AUXADC_CON26_DECI_GDLY_SEL BIT(0) + +#define AUXADC_CON11_VBUF_EN BIT(4) + +#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14) +#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1) +#define AUXADC_READY_MASK BIT(15) +#define AUXADC_DATA_MASK GENMASK(14, 0) + +#define AUXADC_CON9_OSR_MASK GENMASK(12, 10) +#define AUXADC_DEFAULT_OSR 3 + +#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .datasheet_name = __stringify(_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +/* + * AUXADC reports everything in mV, including temperature and + * current channels. Channel macros are mapped such that their + * ID matches their respective hardware bit position in CON22. + */ +static const struct iio_chan_spec mt6323_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6), + MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11), + MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5), + MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4), + MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2), + MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3), + MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1), + MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0), + MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7), +}; + +/* + * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy: + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM) + * + * Therefore, PWRAP regmap should be obtained using dev->parent->parent. + */ +struct mt6323_auxadc { + struct regmap *regmap; + /* AUXADC doesn't support reading multiple channels simultaneously. */ + struct mutex lock; +}; + +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read(map, MT6323_AUXADC_CON19, &val); + if (ret) + return ret; + + /* The ADC is idle. */ + if (!(val & AUXADC_CON19_DECI_GDLY_MASK)) + return 0; + + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, + val, !(val & AUXADC_ADC19_BUSY_MASK), + 10, 500); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON19, + AUXADC_CON19_DECI_GDLY_MASK); +} + +static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); + if (ret) + return ret; + + return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel)); +} + +static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel)); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); +} + +static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc, + const struct iio_chan_spec *chan, int *out) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read_poll_timeout(map, chan->address, + val, (val & AUXADC_READY_MASK), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + *out = FIELD_GET(AUXADC_DATA_MASK, val); + + return 0; +} + +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6323_auxadc *auxadc = iio_priv(indio_dev); + int ret, mult; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel == MT6323_AUXADC_ISENSE || + chan->channel == MT6323_AUXADC_BATSNS) + mult = 4; + else + mult = 1; + + /* 1800mV full range with 15-bit resolution. */ + *val = mult * 1800; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&auxadc->lock); + + ret = mt6323_auxadc_prepare_channel(auxadc); + if (ret) + return ret; + + ret = mt6323_auxadc_request(auxadc, chan->channel); + if (ret) + return ret; + + /* Hardware limitation: the AUXADC needs a delay to become ready. */ + fsleep(300); + + ret = mt6323_auxadc_read(auxadc, chan, val); + + if (mt6323_auxadc_release(auxadc, chan->channel)) + dev_err(&indio_dev->dev, + "failed to release channel %d\n", chan->channel); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_STRUP_CON10, + AUXADC_STRUP_CON10_RSTB_SW | + AUXADC_STRUP_CON10_RSTB_SEL); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK); + if (ret) + return ret; + + ret = regmap_update_bits(map, MT6323_AUXADC_CON10, + AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK | + AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK, + FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1)); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON27, + AUXADC_CON27_VREF18_ENB_MD | + AUXADC_CON27_MD_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON26, + AUXADC_CON26_VREF18_SELB | + AUXADC_CON26_DECI_GDLY_SEL); + if (ret) + return ret; + + return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK, + FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR)); +} + +static const struct iio_info mt6323_auxadc_iio_info = { + .read_raw = mt6323_auxadc_read_raw, +}; + +static int mt6323_auxadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mt6323_auxadc *auxadc; + struct regmap *regmap; + struct iio_dev *iio; + int ret; + + regmap = dev_get_regmap(dev->parent->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); + + iio = devm_iio_device_alloc(dev, sizeof(*auxadc)); + if (!iio) + return -ENOMEM; + + auxadc = iio_priv(iio); + auxadc->regmap = regmap; + + ret = devm_mutex_init(dev, &auxadc->lock); + if (ret) + return ret; + + ret = mt6323_auxadc_init(auxadc); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize auxadc\n"); + + iio->name = "mt6323-auxadc"; + iio->info = &mt6323_auxadc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->channels = mt6323_auxadc_channels; + iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels); + + return devm_iio_device_register(dev, iio); +} + +static const struct of_device_id mt6323_auxadc_of_match[] = { + { .compatible = "mediatek,mt6323-auxadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match); + +static struct platform_driver mt6323_auxadc_driver = { + .driver = { + .name = "mt6323-auxadc", + .of_match_table = mt6323_auxadc_of_match, + }, + .probe = mt6323_auxadc_probe, +}; +module_platform_driver(mt6323_auxadc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver"); diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index 68bdd2f30bad..f0bab94462f4 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -1037,7 +1037,7 @@ static irqreturn_t pac1921_trigger_handler(int irq, void *p) iio_for_each_active_channel(idev, bit) { u16 val; - ret = pac1921_read_res(priv, idev->channels[ch].address, &val); + ret = pac1921_read_res(priv, idev->channels[bit].address, &val); if (ret) goto done; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 3a17b3898bf6..d38cf3caeae0 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -159,16 +159,6 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { RCAR_GYROADC_CHAN(7), }; -static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) -{ - struct device *dev = priv->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -200,7 +190,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - ret = rcar_gyroadc_set_power(priv, true); + ret = pm_runtime_resume_and_get(priv->dev); if (ret < 0) { iio_device_release_direct(indio_dev); return ret; @@ -209,7 +199,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, *val = readl(priv->regs + datareg); *val &= BIT(priv->sample_width) - 1; - ret = rcar_gyroadc_set_power(priv, false); + ret = pm_runtime_put_autosuspend(priv->dev); iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c index c4b0bec80794..2506fec7be84 100644 --- a/drivers/iio/adc/rohm-bd79112.c +++ b/drivers/iio/adc/rohm-bd79112.c @@ -529,7 +529,7 @@ static const struct of_device_id bd79112_of_match[] = { MODULE_DEVICE_TABLE(of, bd79112_of_match); static const struct spi_device_id bd79112_id[] = { - { "bd79112" }, + { .name = "bd79112" }, { } }; MODULE_DEVICE_TABLE(spi, bd79112_id); diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index ba525a3c5cc2..2083c4351e43 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -871,8 +871,8 @@ static const struct richtek_dev_data rtq6059_devdata = { }; static const struct i2c_device_id rtq6056_id[] = { - { "rtq6056", (kernel_ulong_t)&rtq6056_devdata }, - { "rtq6059", (kernel_ulong_t)&rtq6059_devdata }, + { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata }, + { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata }, { } }; MODULE_DEVICE_TABLE(i2c, rtq6056_id); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 9664b9bd75d4..00f05e167afc 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -660,11 +660,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, } df_ch->src = val; - ret = of_property_read_u32_index(indio_dev->dev.of_node, - "st,adc-alt-channel", chan_idx, - &df_ch->alt_si); - if (ret < 0) - df_ch->alt_si = 0; + df_ch->alt_si = of_property_present(indio_dev->dev.of_node, + "st,adc-alt-channel"); return 0; } @@ -1815,9 +1812,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) adc->dfsdm->fl_list[adc->fl_id].ford = val; - ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val); - if (!ret) - adc->dfsdm->fl_list[adc->fl_id].sync_mode = val; + adc->dfsdm->fl_list[adc->fl_id].sync_mode = + of_property_read_bool(dev->of_node, "st,filter0-sync"); adc->dev_data = dev_data; ret = dev_data->init(dev, iio); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index e33a2f4bf66c..2df8f1c21c2a 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -189,10 +189,8 @@ static int adc081c_probe(struct i2c_client *client) err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, adc081c_trigger_handler, NULL); - if (err < 0) { - dev_err(&client->dev, "iio triggered buffer setup failed\n"); - return err; - } + if (err < 0) + return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n"); return devm_iio_device_register(&client->dev, iio); } diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index 63d712d5d111..a522d8676034 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -313,10 +313,10 @@ static const struct of_device_id adc0832_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc0832_dt_ids); static const struct spi_device_id adc0832_id[] = { - { "adc0831", adc0831 }, - { "adc0832", adc0832 }, - { "adc0834", adc0834 }, - { "adc0838", adc0838 }, + { .name = "adc0831", .driver_data = adc0831 }, + { .name = "adc0832", .driver_data = adc0832 }, + { .name = "adc0834", .driver_data = adc0834 }, + { .name = "adc0838", .driver_data = adc0838 }, { } }; MODULE_DEVICE_TABLE(spi, adc0832_id); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index 51596b500a90..cf2f6ee59411 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -228,10 +228,8 @@ static int adc084s021_probe(struct spi_device *spi) ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, adc084s021_buffer_trigger_handler, &adc084s021_buffer_setup_ops); - if (ret) { - dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); return devm_iio_device_register(&spi->dev, indio_dev); } @@ -243,7 +241,7 @@ static const struct of_device_id adc084s021_of_match[] = { MODULE_DEVICE_TABLE(of, adc084s021_of_match); static const struct spi_device_id adc084s021_id[] = { - { ADC084S021_DRIVER_NAME, 0 }, + { .name = ADC084S021_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(spi, adc084s021_id); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index d1f61b440959..10959e835cb8 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -276,7 +276,7 @@ static const struct acpi_device_id adc108s102_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); static const struct spi_device_id adc108s102_id[] = { - { "adc108s102", 0 }, + { .name = "adc108s102" }, { } }; MODULE_DEVICE_TABLE(spi, adc108s102_id); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index e5ec4b073daa..279e4999eb49 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -516,9 +516,9 @@ static const struct of_device_id adc12138_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc12138_dt_ids); static const struct spi_device_id adc12138_id[] = { - { "adc12130", adc12130 }, - { "adc12132", adc12132 }, - { "adc12138", adc12138 }, + { .name = "adc12130", .driver_data = adc12130 }, + { .name = "adc12132", .driver_data = adc12132 }, + { .name = "adc12138", .driver_data = adc12138 }, { } }; MODULE_DEVICE_TABLE(spi, adc12138_id); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 2cb68582ee6a..1899813c1aee 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -247,18 +247,18 @@ static const struct of_device_id adc128_of_match[] = { MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", (kernel_ulong_t)&adc128s_config }, - { "adc122s021", (kernel_ulong_t)&adc122s_config }, - { "adc122s051", (kernel_ulong_t)&adc122s_config }, - { "adc122s101", (kernel_ulong_t)&adc122s_config }, - { "adc124s021", (kernel_ulong_t)&adc124s_config }, - { "adc124s051", (kernel_ulong_t)&adc124s_config }, - { "adc124s101", (kernel_ulong_t)&adc124s_config }, - { "bd79100", (kernel_ulong_t)&bd79100_config }, - { "bd79101", (kernel_ulong_t)&bd79101_config }, - { "bd79102", (kernel_ulong_t)&bd79102_config }, - { "bd79103", (kernel_ulong_t)&bd79104_config }, - { "bd79104", (kernel_ulong_t)&bd79104_config }, + { .name = "adc128s052", .driver_data = (kernel_ulong_t)&adc128s_config }, + { .name = "adc122s021", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s051", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s101", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc124s021", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s051", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s101", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "bd79100", .driver_data = (kernel_ulong_t)&bd79100_config }, + { .name = "bd79101", .driver_data = (kernel_ulong_t)&bd79101_config }, + { .name = "bd79102", .driver_data = (kernel_ulong_t)&bd79102_config }, + { .name = "bd79103", .driver_data = (kernel_ulong_t)&bd79104_config }, + { .name = "bd79104", .driver_data = (kernel_ulong_t)&bd79104_config }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 08aa32bd5e4b..422df0685580 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -229,8 +229,8 @@ static const struct of_device_id ti_adc_dt_ids[] = { MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static const struct spi_device_id ti_adc_id[] = { - { "adc141s626", TI_ADC141S626 }, - { "adc161s626", TI_ADC161S626 }, + { .name = "adc141s626", .driver_data = TI_ADC141S626 }, + { .name = "adc161s626", .driver_data = 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 8a272af69f7d..0fbfa4e499aa 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) mode << ADS1015_CFG_MOD_SHIFT); } +static void ads1015_power_down(void *p) +{ + struct ads1015_data *data = p; + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to power down (%pe)\n", ERR_PTR(ret)); +} + static int ads1015_probe(struct i2c_client *client) { const struct ads1015_chip_data *chip; @@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data); + if (ret) + return ret; + data->conv_invalid = true; ret = pm_runtime_set_active(&client->dev); @@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client) return ret; pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - pm_runtime_enable(&client->dev); - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Failed to register IIO device\n"); - return ret; - } - - return 0; -} - -static void ads1015_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ads1015_data *data = iio_priv(indio_dev); - int ret; - - iio_device_unregister(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - /* power down single shot mode */ - ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + ret = devm_pm_runtime_enable(&client->dev); if (ret) - dev_warn(&client->dev, "Failed to power down (%pe)\n", - ERR_PTR(ret)); + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM @@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = { .pm = &ads1015_pm_ops, }, .probe = ads1015_probe, - .remove = ads1015_remove, .id_table = ads1015_id, }; diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index d6624c71a374..d9ec4a8d7ce5 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -717,8 +717,8 @@ static const struct of_device_id ads1018_of_match[] = { MODULE_DEVICE_TABLE(of, ads1018_of_match); static const struct spi_device_id ads1018_spi_match[] = { - { "ads1018", (kernel_ulong_t)&ads1018_chip_info }, - { "ads1118", (kernel_ulong_t)&ads1118_chip_info }, + { .name = "ads1018", .driver_data = (kernel_ulong_t)&ads1018_chip_info }, + { .name = "ads1118", .driver_data = (kernel_ulong_t)&ads1118_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ads1018_spi_match); diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c new file mode 100644 index 000000000000..60eab4852ba2 --- /dev/null +++ b/drivers/iio/adc/ti-ads112c14.c @@ -0,0 +1,1454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for Texas Instruments ADS112C14 and similar ADCs. + * + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2026 Baylibre Inc. + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads122c14.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Arbitrary limit since channels are dynamic. */ +#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16 + +/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */ +#define ADS112C14_DELAY_RESET_US 500 + +#define ADS112C14_CMD_RDATA 0x00 +#define ADS112C14_CMD_RREG 0x40 +#define ADS112C14_CMD_WREG 0x80 + +#define ADS112C14_REG_DEVICE_ID 0x00 +#define ADS112C14_DEVICE_ID_BITS GENMASK(3, 0) + +#define ADS112C14_REG_REVISION_ID 0x01 + +#define ADS112C14_REG_STATUS_MSB 0x02 +#define ADS112C14_STATUS_MSB_RESETN BIT(7) +#define ADS112C14_STATUS_MSB_AVDD_UVN BIT(6) +#define ADS112C14_STATUS_MSB_REF_UVN BIT(5) +#define ADS112C14_STATUS_MSB_REG_MAP_CRC_FAULTN BIT(3) +#define ADS112C14_STATUS_MSB_MEM_FAULTN BIT(2) +#define ADS112C14_STATUS_MSB_REG_WRITE_FAULTN BIT(1) +#define ADS112C14_STATUS_MSB_DRDY BIT(0) + +#define ADS112C14_REG_STATUS_LSB 0x03 +#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4) +#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3) +#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2) +#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1) +#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0) + +#define ADS112C14_REG_CONVERSION_CTRL 0x04 +#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2) +#define ADS112C14_CONVERSION_CTRL_START BIT(1) +#define ADS112C14_CONVERSION_CTRL_STOP BIT(0) + +#define ADS112C14_REG_DEVICE_CFG 0x05 +#define ADS112C14_DEVICE_CFG_PWDN BIT(7) +#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6) +#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4) +#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3) +#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2) +#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0 +#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1 +#define ADS112C14_DEVICE_CFG_SPEED_MODE GENMASK(1, 0) + +#define ADS112C14_REG_DATA_RATE_CFG 0x06 +#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4) +#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3) +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0) + +#define ADS112C14_REG_MUX_CFG 0x07 +#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4) +#define ADS112C14_MUX_CFG_AINN GENMASK(3, 0) +#define ADS112C14_MUX_CFG_AIN_GND 8 + +#define ADS112C14_REG_GAIN_CFG 0x08 +#define ADS112C14_GAIN_CFG_SPARE BIT(7) +#define ADS112C14_GAIN_CFG_SYS_MON GENMASK(6, 4) +#define ADS112C14_GAIN_CFG_GAIN GENMASK(3, 0) + +#define ADS112C14_REG_REFERENCE_CFG 0x09 +#define ADS112C14_REFERENCE_CFG_REF_UV_EN BIT(7) +#define ADS112C14_REFERENCE_CFG_REFP_BUF_EN BIT(5) +#define ADS112C14_REFERENCE_CFG_REFN_BUF_EN BIT(4) +#define ADS112C14_REFERENCE_CFG_REF_VAL BIT(2) +#define ADS112C14_REFERENCE_CFG_REF_VAL_1_25V 0 +#define ADS112C14_REFERENCE_CFG_REF_VAL_2_5V 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL GENMASK(1, 0) +#define ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL 0 +#define ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL_AVDD 2 + +#define ADS112C14_REG_DIGITAL_CFG 0x0A +#define ADS112C14_DIGITAL_CFG_REG_MAP_CRC_EN BIT(6) +#define ADS112C14_DIGITAL_CFG_I2C_CRC_EN BIT(5) +#define ADS112C14_DIGITAL_CFG_STATUS_EN BIT(4) +#define ADS112C14_DIGITAL_CFG_FAULT_PIN_BEHAVIOR BIT(3) +#define ADS112C14_DIGITAL_CFG_CODING BIT(1) + +#define ADS112C14_REG_GPIO_CFG 0x0B +#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6) +#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4) +#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2) +#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0) + +#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0) + +#define ADS112C14_REG_IDAC_MAG_CFG 0x0D +#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4) +#define ADS112C14_IDAC_MAG_CFG_I1MAG GENMASK(3, 0) + +#define ADS112C14_REG_IDAC_MUX_CFG 0x0E +#define ADS112C14_IDAC_MUX_CFG_IUNIT BIT(7) +#define ADS112C14_IDAC_MUX_CFG_I2MUX GENMASK(6, 4) +#define ADS112C14_IDAC_MUX_CFG_I1MUX GENMASK(2, 0) + +#define ADS112C14_REG_REG_MAP_CRC 0x0F + +#define ADS112C14_INT_REF0_mV 1250 +#define ADS112C14_INT_REF1_mV 2500 + +enum { + ADS112C14_VREF_SOURCE_INTERNAL_2_5V, + ADS112C14_VREF_SOURCE_INTERNAL_1_25V, + ADS112C14_VREF_SOURCE_EXTERNAL, + ADS112C14_VREF_SOURCE_AVDD, +}; + +static const char * const ads112c14_vref_source_names[] = { + [ADS112C14_VREF_SOURCE_INTERNAL_2_5V] = "internal-2.5v", + [ADS112C14_VREF_SOURCE_INTERNAL_1_25V] = "internal-1.25v", + [ADS112C14_VREF_SOURCE_EXTERNAL] = "external", + [ADS112C14_VREF_SOURCE_AVDD] = "avdd", +}; + +/* + * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to + * ADS112C14_GAIN_CFG_GAIN values. + */ +static const u32 ads112c14_pga_gains_x10[] = { + 5, 10, 20, 40, 50, 80, 100, 160, /* 0 - 7 */ + 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */ +}; + +#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07 +DECLARE_CRC8_TABLE(ads112c14_crc8_table); + +struct ads112c14_chip_info { + const char *name; + u8 device_id; + u32 resolution_bits; +}; + +/* Fixed channels for system monitor measurements. */ + +#define ADS112C14_SYS_MON_CHANNEL_BASE 100 + +enum { + ADS112C14_SYS_MON_CHANNEL_TEMP = ADS112C14_SYS_MON_CHANNEL_BASE, + ADS112C14_SYS_MON_CHANNEL_EXT_REF, + ADS112C14_SYS_MON_CHANNEL_AVDD, + ADS112C14_SYS_MON_CHANNEL_DVDD, + ADS112C14_SYS_MON_CHANNEL_SHORT, +}; + +static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_TEMP, + .address = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF, + .address = 3, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_AVDD, + .address = 4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_DVDD, + .address = 5, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_SHORT, + .channel2 = ADS112C14_SYS_MON_CHANNEL_SHORT, + .differential = 1, + .address = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +struct ads112c14_measurement { + const char *label; + u32 vref_source; + u8 iunit; + u8 idac1_mag; + u8 idac2_mag; + u8 idac1_mux; + u8 idac2_mux; + u8 iadc_count; + u8 gain_val; + bool global_chop; + bool bipolar; + int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; +}; + +struct ads112c14_data { + const struct ads112c14_chip_info *chip_info; + struct regmap *regmap; + /* Synchronizes access to register value fields. */ + struct mutex lock; + bool i2c_crc_enabled; + u32 avdd_uV; + u32 ext_ref_uV; + bool refp_is_avdd; + bool refn_is_gnd; + u32 ext_ref_ohms; + struct ads112c14_measurement *measurements; + u32 num_measurements; + u8 sys_mon_chan_short_gain_val; + int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; + IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS + + ARRAY_SIZE(ads112c14_sys_mon_channels)); +}; + +static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_DEVICE_ID: + case ADS112C14_REG_REVISION_ID: + case ADS112C14_REG_STATUS_LSB: + return false; + default: + return true; + } +} + +static bool ads112c14_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_STATUS_MSB: + case ADS112C14_REG_STATUS_LSB: + case ADS112C14_REG_CONVERSION_CTRL: + return true; + default: + return false; + } +} + +static const struct reg_default ads112c14_reg_defaults[] = { + { ADS112C14_REG_DEVICE_CFG, 0 }, + { ADS112C14_REG_DATA_RATE_CFG, 0 }, + { ADS112C14_REG_MUX_CFG, 0 }, + { ADS112C14_REG_GAIN_CFG, FIELD_PREP_CONST(ADS112C14_GAIN_CFG_GAIN, 1) }, + { ADS112C14_REG_REFERENCE_CFG, 0 }, + { ADS112C14_REG_DIGITAL_CFG, 0 }, + { ADS112C14_REG_GPIO_CFG, 0 }, + { ADS112C14_REG_GPIO_DATA_OUTPUT, 0 }, + { ADS112C14_REG_IDAC_MAG_CFG, 0 }, + { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) }, +}; + +/** + * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C + * @client: I2C client for the device + * @cmd: Command to send to the device before reading + * @buf: Buffer to store the read bytes + * @len: Number of bytes to read + * @use_crc: Whether to use CRC8 for data integrity check + * + * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check + * on the received data. + */ +static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd, + u8 *buf, u8 len, bool use_crc) +{ + u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */ + u8 rx_len; + int ret; + + rx_len = len + (use_crc ? 1 : 0); + + if (rx_len > sizeof(rx_buf)) + return -EINVAL; + + ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf); + if (ret < 0) + return ret; + + if (use_crc) { + u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE); + + if (crc != rx_buf[len]) + return -EBADMSG; + } + + memcpy(buf, rx_buf, len); + + return 0; +} + +/** + * ads112c14_regmap_bus_read() - Read a register from the device + * @context: Pointer to the device context + * @reg_buf: Register address to read + * @reg_size: Size of the register address (should be 1) + * @val_buf: Buffer to store the read value + * @val_size: Size of the value to read + * + * Custom regmap read function that also does CRC check when enabled. + */ +static int ads112c14_regmap_bus_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *cmd = reg_buf; + + if (reg_size != 1) + return -EINVAL; + + return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size, + data->i2c_crc_enabled); +} + +/** + * ads112c14_regmap_bus_write() - Write a register to the device + * @context: Pointer to the device context + * @data_buf: Buffer containing the register address and value to write + * @count: Number of bytes to write + * + * Custom regmap write function that also does readback with CRC check of + * nonvolatile registers when CRC is enabled. + */ +static int ads112c14_regmap_bus_write(void *context, const void *data_buf, + size_t count) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *tx = data_buf; + u8 reg, readback; + int ret; + + if (count != 2) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]); + if (ret) + return ret; + + reg = tx[0] & ~ADS112C14_CMD_WREG; + + if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg)) + return 0; + + ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG, + &readback, sizeof(readback), true); + if (ret) + return ret; + + if (readback != tx[1]) + return -EIO; + + return 0; +} + +static const struct regmap_bus ads112c14_regmap_bus = { + .read = ads112c14_regmap_bus_read, + .write = ads112c14_regmap_bus_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ads112c14_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = ADS112C14_CMD_RREG, + .write_flag_mask = ADS112C14_CMD_WREG, + .max_register = ADS112C14_REG_REG_MAP_CRC, + .writeable_reg = ads112c14_writeable_reg, + .volatile_reg = ads112c14_volatile_reg, + .reg_defaults = ads112c14_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ads112c14_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index]; + u32 refp_buf_en, refn_buf_en, ref_val, ref_sel; + int ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG, + ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN, + FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) | + FIELD_PREP(ADS112C14_MUX_CFG_AINN, chan->channel2)); + if (ret) + return ret; + + ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING, + !measurement->bipolar); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, 0) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, + measurement->gain_val)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MAG_CFG, + ADS112C14_IDAC_MAG_CFG_I2MAG | + ADS112C14_IDAC_MAG_CFG_I1MAG, + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I2MAG, + measurement->idac2_mag) | + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I1MAG, + measurement->idac1_mag)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MUX_CFG, + ADS112C14_IDAC_MUX_CFG_IUNIT | + ADS112C14_IDAC_MUX_CFG_I2MUX | + ADS112C14_IDAC_MUX_CFG_I1MUX, + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_IUNIT, + measurement->iunit) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I2MUX, + measurement->idac2_mux) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I1MUX, + measurement->idac1_mux)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, + ADS112C14_DATA_RATE_CFG_GC_EN, + FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, + measurement->global_chop)); + if (ret) + return ret; + + refp_buf_en = !data->refp_is_avdd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + refn_buf_en = !data->refn_is_gnd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + + ref_val = measurement->vref_source == ADS112C14_VREF_SOURCE_INTERNAL_2_5V ? + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V : + ADS112C14_REFERENCE_CFG_REF_VAL_1_25V; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_AVDD: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_AVDD; + break; + case ADS112C14_VREF_SOURCE_EXTERNAL: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL; + break; + default: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL; + break; + } + + return regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REFP_BUF_EN | + ADS112C14_REFERENCE_CFG_REFN_BUF_EN | + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFP_BUF_EN, + refp_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFN_BUF_EN, + refn_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ref_val) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ref_sel)); +} + +static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + u32 gain_val; + int ret; + + /* + * NB: IDAC registers are left as-is in case they are generating current + * needed for the external reference measurement. + */ + + /* + * All SYS_MON channels use GAIN of 1 to keep it simple. Other than + * the internal short channel, where it is useful in practice. + */ + gain_val = chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT ? + data->sys_mon_chan_short_gain_val : 1; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, gain_val)); + if (ret) + return ret; + + /* All SYS_MON channels use signed data to keep it simple. */ + ret = regmap_clear_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING); + if (ret) + return ret; + + /* + * REVISIT: if we implement regulator support for the REFOUT pin, we + * might need to make this voltage match what is required by that. In + * that case, we could also adjust GAIN so that we still get the same + * range. + */ + /* + * NB: SYS_MON channels ignore REF_SEL except for the shorted input + * channel, so we set it here to internal reference to be consistent. + * If we ever need to make a measurement of shorted input with other + * reference source, we could add additional channels for that. + */ + ret = regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL)); + if (ret) + return ret; + + return 0; +} + +static int ads112c14_single_conversion(struct ads112c14_data *data, + const struct iio_chan_spec *chan, + u8 *buf, bool for_scan) +{ + struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); + u32 reg_val; + int ret; + + guard(mutex)(&data->lock); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + ret = ads112c14_prepare_measurement_channel(data, chan); + if (ret) + return ret; + } else { + ret = ads112c14_prepare_sys_mon_channel(data, chan); + if (ret) + return ret; + } + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(data->regmap, + ADS112C14_REG_STATUS_MSB, reg_val, + FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + /* + * When doing buffered read, we don't check the CRC, but rather pass it + * along with the raw data. This way, we don't silently drop samples + * with CRC errors, but rather leave it to userspace to decide what to + * do. + */ + if (for_scan) { + u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) + + (data->i2c_crc_enabled ? 1 : 0); + + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, + len, buf); + if (ret < 0) + return ret; + + return 0; + } + + return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf, + BITS_TO_BYTES(data->chip_info->resolution_bits), + data->i2c_crc_enabled); +} + +static int ads112c14_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_measurement *measurement = NULL; + const int *scale_avail; + u32 vref_uV, fsr_bits; + + /* Selecting V_REF source is not implemented yet. */ + vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + measurement = &data->measurements[chan->scan_index]; + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; + } else { + /* All SYS_MON channels are using signed coding. */ + fsr_bits = data->chip_info->resolution_bits - 1; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u8 buf[3]; + int ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ads112c14_single_conversion(data, chan, buf, false); + if (ret) + return ret; + + switch (data->chip_info->resolution_bits) { + case 16: + *val = get_unaligned_be16(buf); + break; + case 24: + *val = get_unaligned_be24(buf); + break; + default: + return -EINVAL; + } + + if (!measurement || measurement->bipolar) + *val = sign_extend32(*val, fsr_bits); + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* TS_TC (typical) = 405 uV/°C */ + *val = MILLI * vref_uV / 405; + *val2 = fsr_bits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + guard(mutex)(&data->lock); + + scale_avail = measurement->scale_available[measurement->gain_val]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + u8 idx; + + guard(mutex)(&data->lock); + + idx = data->sys_mon_chan_short_gain_val; + scale_avail = data->sys_mon_chan_short_scale_available[idx]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + + *val = vref_uV / (MICRO / MILLI); + + /* + * Some SYS_MON channels (ext ref, AVDD, DVDD) need to be + * multiplied by 8 to account for internal attenuation of / 8. + */ + switch (chan->address) { + case 3 ... 5: + *val2 = fsr_bits - 3; + break; + default: + *val2 = fsr_bits; + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset. */ + if (chan->type != IIO_TEMP) + return -EINVAL; + /* + * Die temperature [°C] = 25°C + (Measured voltage – TS_Offset) / TS_TC + * TS_TC (typical) = 405 uV/°C + * TS_Offset (typical) = 119.5 mV + */ + *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads112c14_read_avail(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, const int **vals, + int *type, int *length, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + guard(mutex)(&data->lock); + + measurement = &data->measurements[chan->scan_index]; + *vals = (const int *)measurement->scale_available; + *length = 2 * ARRAY_SIZE(measurement->scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + guard(mutex)(&data->lock); + + *vals = (const int *)data->sys_mon_chan_short_scale_available; + *length = 2 * ARRAY_SIZE(data->sys_mon_chan_short_scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ads112c14_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const int (*scale_avail)[2]; + u8 *gain_val; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->lock); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + scale_avail = measurement->scale_available; + gain_val = &measurement->gain_val; + } else if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + scale_avail = data->sys_mon_chan_short_scale_available; + gain_val = &data->sys_mon_chan_short_gain_val; + } else { + return -EINVAL; + } + + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + if (iio_val_s64_compose(val, val2) == + iio_val_s64_compose(scale_avail[i][0], scale_avail[i][1])) { + *gain_val = i; + return 0; + } + } + + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ads112c14_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_DECIMAL64_PICO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + if (readval) + return regmap_read(data->regmap, reg, readval); + + return regmap_write(data->regmap, reg, writeval); +} + +static int ads112c14_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const char *label_source; + + /* measurement channels */ + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + if (!measurement->label) + return -EINVAL; + + return sysfs_emit(label, "%s\n", measurement->label); + } + + /* System monitor channels. */ + switch (chan->channel) { + case ADS112C14_SYS_MON_CHANNEL_TEMP: + label_source = "Internal temperature sensor"; + break; + case ADS112C14_SYS_MON_CHANNEL_EXT_REF: + label_source = "External reference"; + break; + case ADS112C14_SYS_MON_CHANNEL_AVDD: + label_source = "AVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_DVDD: + label_source = "DVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_SHORT: + label_source = "Internal short (internal reference source)"; + break; + default: + return -EINVAL; + } + + return sysfs_emit(label, "%s\n", label_source); +} + +static irqreturn_t ads112c14_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads112c14_data *data = iio_priv(indio_dev); + u32 offset = 0; + u32 i; + int ret; + + iio_for_each_active_channel(indio_dev, i) { + const struct iio_chan_spec *chan = &indio_dev->channels[i]; + + ret = ads112c14_single_conversion(data, chan, + (u8 *)&data->scan[offset++], + true); + if (ret) { + dev_err_once(indio_dev->dev.parent, + "failed to read channel %d: %pe; additional errors will be suppressed\n", + chan->channel, ERR_PTR(ret)); + goto out; + } + } + + iio_push_to_buffers_with_ts(indio_dev, data->scan, + sizeof(data->scan), pf->timestamp); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info ads112c14_info = { + .read_raw = ads112c14_read_raw, + .read_avail = ads112c14_read_avail, + .write_raw = ads112c14_write_raw, + .write_raw_get_fmt = ads112c14_write_raw_get_fmt, + .debugfs_reg_access = ads112c14_debugfs_reg_access, + .read_label = ads112c14_read_label, +}; + +static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag) +{ + u32 current_uA = current_nA / (NANO / MICRO); + + /* Convert microamps to IMAG bits */ + if (current_uA == 1) + *idac_mag = 1; + else if (in_range(current_uA, 10, 100) && current_uA % 10 == 0) + *idac_mag = current_uA / 10 + 1; + else + return dev_err_probe(NULL, -EINVAL, + "invalid excitation-current-nanoamp value\n"); + + return 0; +} + +static int ads112c14_parse_channels(struct iio_dev *indio_dev, + bool *need_avdd_ref, bool *need_ext_ref) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *channels; + u32 num_child_nodes, i, pair[2]; + int ret; + + *need_avdd_ref = false; + *need_ext_ref = false; + + num_child_nodes = device_get_named_child_node_count(dev, "channel"); + + data->measurements = devm_kcalloc(dev, num_child_nodes, + sizeof(*data->measurements), GFP_KERNEL); + if (!data->measurements) + return -ENOMEM; + + channels = devm_kcalloc(dev, num_child_nodes + + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1, + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + i = 0; + device_for_each_named_child_node_scoped(dev, child, "channel") { + struct ads112c14_measurement *measurement = &data->measurements[i]; + struct iio_chan_spec *spec = &channels[i]; + + spec->indexed = 1; + spec->scan_index = i; + measurement->gain_val = 1; + + if (fwnode_property_present(child, "label")) { + ret = fwnode_property_read_string(child, "label", &measurement->label); + if (ret) + return dev_err_probe(dev, ret, + "failed to read label property\n"); + } + + if (fwnode_property_present(child, "single-channel")) { + ret = fwnode_property_read_u32(child, "single-channel", + &pair[0]); + if (ret) + return dev_err_probe(dev, ret, + "failed to read single-channel property\n"); + + if (pair[0] >= 8) + return dev_err_probe(dev, -EINVAL, + "single-channel value must be between 0 and 7\n"); + + spec->channel = pair[0]; + /* + * NB: channel2 is unused by iio core code in this case. + * Let's us avoid special case for negative input mux + * for single-ended channels when taking measurements. + */ + spec->channel2 = ADS112C14_MUX_CFG_AIN_GND; + } else if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, "diff-channels", + pair, ARRAY_SIZE(pair)); + if (ret) + return dev_err_probe(dev, ret, + "failed to read diff-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "diff-channels values must be between 0 and 7\n"); + + spec->differential = 1; + spec->channel = pair[0]; + spec->channel2 = pair[1]; + } else { + return dev_err_probe(dev, -EINVAL, + "channel node missing channel type property\n"); + } + + if (fwnode_property_present(child, "excitation-channels")) { + ret = fwnode_property_count_u32(child, "excitation-channels"); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (ret < 1 || ret > 2) + return dev_err_probe(dev, -EINVAL, + "excitation-channels property must have 1 or 2 values\n"); + + measurement->iadc_count = ret; + pair[1] = 0; + + ret = fwnode_property_read_u32_array(child, "excitation-channels", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "excitation-channels values must be between 0 and 7\n"); + + measurement->idac1_mux = pair[0]; + measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0; + + ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-current-nanoamp property\n"); + + if (pair[0] <= 100 * (NANO / MICRO) && + (measurement->iadc_count == 1 || pair[1] <= 100 * (NANO / MICRO))) { + /* + * If both values are 100µA or less, then we can + * use IUNIT = 1µA for better precision. + */ + ret = ads112c14_populate_idac_mag(pair[0], + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1], + &measurement->idac2_mag); + if (ret) + return ret; + } + } else { + /* + * Otherwise, IUINT is 10µA (flag set) and so + * IxMAG is 1/10 of the actual current. + */ + measurement->iunit = 1; + + ret = ads112c14_populate_idac_mag(pair[0] / 10, + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1] / 10, + &measurement->idac2_mag); + if (ret) + return ret; + } + } + } + + measurement->bipolar = fwnode_property_read_bool(child, "bipolar"); + measurement->global_chop = fwnode_property_read_bool(child, + "input-chopping"); + + if (fwnode_property_present(child, "reference-sources")) { + ret = fwnode_property_match_property_string(child, + "reference-sources", ads112c14_vref_source_names, + ARRAY_SIZE(ads112c14_vref_source_names)); + if (ret < 0) + return dev_err_probe(dev, ret, + "invalid reference-sources value\n"); + + measurement->vref_source = ret; + } + + if (measurement->vref_source == ADS112C14_VREF_SOURCE_AVDD) + *need_avdd_ref = true; + if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL) + *need_ext_ref = true; + + spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); + spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE); + + /* + * If reference source is resistor rather than voltage supply, + * then the measurement is effectively a resistance measurement. + */ + spec->type = (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL && + data->ext_ref_ohms) ? IIO_RESISTANCE : IIO_VOLTAGE; + + if (spec->type == IIO_RESISTANCE) + spec->differential = 0; + + spec->scan_type = (struct iio_scan_type){ + .format = measurement->bipolar ? + IIO_SCAN_FORMAT_SIGNED_INT : + IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + + i++; + } + + data->num_measurements = i; + if (data->num_measurements > ADS112C14_MAX_MEASUREMENT_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "too many measurement channels defined\n"); + + memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels)); + + for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) { + struct iio_chan_spec *spec = &channels[i]; + + /* Update the template that was already copied with dynamic values. */ + spec->scan_index = i; + spec->scan_type = (struct iio_scan_type){ + .format = IIO_SCAN_FORMAT_SIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + + i++; + } + + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); + i++; + + indio_dev->channels = channels; + indio_dev->num_channels = i; + + return 0; +} + +static void ads112c14_populate_scale_available(int (*scale_avail)[2], + u32 full_scale, u32 fsr_bits) +{ + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + u64 gain_x10 = ads112c14_pga_gains_x10[i]; + s64 scale; + + scale = div64_u64((u64)PICO * 10U * full_scale, + gain_x10 * BIT(fsr_bits)); + + iio_val_s64_decompose(scale, &scale_avail[i][0], + &scale_avail[i][1]); + } +} + +static void ads112c14_populate_tables(struct ads112c14_data *data) +{ + u32 full_scale, fsr_bits; + + for (u32 i = 0; i < data->num_measurements; i++) { + struct ads112c14_measurement *measurement = &data->measurements[i]; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_EXTERNAL: + if (data->ext_ref_ohms) + full_scale = data->ext_ref_ohms; + else + full_scale = data->ext_ref_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_AVDD: + full_scale = data->avdd_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_INTERNAL_1_25V: + full_scale = ADS112C14_INT_REF0_mV; + break; + default: + full_scale = ADS112C14_INT_REF1_mV; + break; + } + + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; + + ads112c14_populate_scale_available(measurement->scale_available, + full_scale, fsr_bits); + } + + /* For now, assuming all sys_mon channels are using 2.5V reference. */ + full_scale = ADS112C14_INT_REF1_mV; + fsr_bits = data->chip_info->resolution_bits - 1; + + ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available, + full_scale, fsr_bits); +} + +static int ads112c14_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct ads112c14_chip_info *info; + struct iio_dev *indio_dev; + struct ads112c14_data *data; + bool need_avdd_ref, need_ext_ref; + u32 refp_uV = 0; + u32 refn_uV = 0; + u32 reg_val; + int ret; + + info = i2c_get_match_data(client); + if (!info) + return dev_err_probe(dev, -ENODEV, "missing match data\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->chip_info = info; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + if (device_property_present(dev, "ti,refp-refn-resistor-ohms")) { + ret = device_property_read_u32(dev, "ti,refp-refn-resistor-ohms", + &data->ext_ref_ohms); + if (ret) + return dev_err_probe(dev, ret, + "failed to read ti,refp-refn-resistor-ohms property\n"); + } + + ret = ads112c14_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); + + if (need_avdd_ref) { + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get avdd voltage\n"); + + data->avdd_uV = ret; + } else { + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + } + + if (device_property_present(dev, "refp-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refp"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refp voltage\n"); + + refp_uV = ret; + + struct fwnode_handle *refp_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "refp-supply", 0); + if (IS_ERR(refp_fwnode)) + return dev_err_probe(dev, PTR_ERR(refp_fwnode), + "failed to get refp fwnode\n"); + + struct fwnode_handle *avdd_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "avdd-supply", 0); + if (IS_ERR(avdd_fwnode)) + return dev_err_probe(dev, PTR_ERR(avdd_fwnode), + "failed to get avdd fwnode\n"); + + /* REFP buffer should not be enabled when connected to AVDD */ + data->refp_is_avdd = refp_fwnode == avdd_fwnode; + } + + if (device_property_present(dev, "refn-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refn"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refn voltage\n"); + + refn_uV = ret; + } else { + data->refn_is_gnd = true; + } + + data->ext_ref_uV = refp_uV - refn_uV; + + if (data->ext_ref_uV && data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "ti,refp-refn-resistor-ohms property should not be present when refp-supply or refn-supply is present\n"); + + if (need_ext_ref && !data->ext_ref_uV && !data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n"); + + /* It takes some time for the internal reference to stabilize. */ + fsleep(10 * USEC_PER_MSEC); + + data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data, + &ads112c14_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + /* + * Write magic reset value (0x16) to ensure known state. The reset may + * cause an error because of failing to get the I2C ACK at the end of + * the message. The device still gets reset so it is safe to ignore the + * return value here. If something else is wrong, later read/write will + * likely have the same error. + */ + regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + FIELD_PREP(ADS112C14_CONVERSION_CTRL_RESET, 0x16)); + + fsleep(ADS112C14_DELAY_RESET_US); + + ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_MSB, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val)) + return dev_err_probe(dev, -EIO, "reset failed\n"); + + /* Default gain after reset is 1. */ + data->sys_mon_chan_short_gain_val = 1; + + /* + * Clear reset bit to prepare for next probe. And clear AVDD fault since + * that happens on every reset. + */ + ret = regmap_write(data->regmap, ADS112C14_REG_STATUS_MSB, + ADS112C14_STATUS_MSB_RESETN | + ADS112C14_STATUS_MSB_AVDD_UVN); + if (ret) + return ret; + + ret = regmap_set_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_I2C_CRC_EN); + if (ret) + return ret; + + data->i2c_crc_enabled = true; + + ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val) != info->device_id) + dev_info(dev, "device ID mismatch, expected 0x%X, got 0x%lX\n", + info->device_id, + FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val)); + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT)); + if (ret) + return ret; + + ads112c14_populate_tables(data); + + indio_dev->name = info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ads112c14_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ads112c14_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct ads112c14_chip_info ads112c14_chip_info = { + .name = "ads112c14", + .device_id = 0xE, + .resolution_bits = 16, +}; + +static const struct ads112c14_chip_info ads122c14_chip_info = { + .name = "ads122c14", + .device_id = 0xF, + .resolution_bits = 24, +}; + +static const struct of_device_id ads112c14_of_match[] = { + { .compatible = "ti,ads112c14", .data = &ads112c14_chip_info }, + { .compatible = "ti,ads122c14", .data = &ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ads112c14_of_match); + +static const struct i2c_device_id ads112c14_id[] = { + { .name = "ads112c14", .driver_data = (kernel_ulong_t)&ads112c14_chip_info }, + { .name = "ads122c14", .driver_data = (kernel_ulong_t)&ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads112c14_id); + +static int ads112c14_i2c_add_driver(struct i2c_driver *driver) +{ + crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL); + + return i2c_add_driver(driver); +} + +static struct i2c_driver ads112c14_driver = { + .driver = { + .name = "ads112c14", + .of_match_table = ads112c14_of_match, + }, + .probe = ads112c14_probe, + .id_table = ads112c14_id, +}; +module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver); + +MODULE_AUTHOR("David Lechner (TI) "); +MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 45de47778809..49fbc63508ac 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -348,8 +348,8 @@ static int ads124s_probe(struct spi_device *spi) } static const struct spi_device_id ads124s_id[] = { - { "ads124s06", ADS124S06_ID }, - { "ads124s08", ADS124S08_ID }, + { .name = "ads124s06", .driver_data = ADS124S06_ID }, + { .name = "ads124s08", .driver_data = ADS124S08_ID }, { } }; MODULE_DEVICE_TABLE(spi, ads124s_id); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 9e47ace16acd..06c6353aa1b2 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -743,7 +743,7 @@ static int ads1298_probe(struct spi_device *spi) } static const struct spi_device_id ads1298_id[] = { - { "ads1298" }, + { .name = "ads1298" }, { } }; MODULE_DEVICE_TABLE(spi, ads1298_id); diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index a585621b0bc3..1d25f887cadb 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -917,9 +917,9 @@ static const struct of_device_id ads131e08_of_match[] = { MODULE_DEVICE_TABLE(of, ads131e08_of_match); 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] }, + { .name = "ads131e04", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, + { .name = "ads131e06", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, + { .name = "ads131e08", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, { } }; MODULE_DEVICE_TABLE(spi, ads131e08_ids); diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c index 36203ce37c89..2f8f75c8216b 100644 --- a/drivers/iio/adc/ti-ads131m02.c +++ b/drivers/iio/adc/ti-ads131m02.c @@ -943,11 +943,11 @@ static const struct of_device_id ads131m_of_match[] = { MODULE_DEVICE_TABLE(of, ads131m_of_match); static const struct spi_device_id ads131m_id[] = { - { "ads131m02", (kernel_ulong_t)&ads131m02_config }, - { "ads131m03", (kernel_ulong_t)&ads131m03_config }, - { "ads131m04", (kernel_ulong_t)&ads131m04_config }, - { "ads131m06", (kernel_ulong_t)&ads131m06_config }, - { "ads131m08", (kernel_ulong_t)&ads131m08_config }, + { .name = "ads131m02", .driver_data = (kernel_ulong_t)&ads131m02_config }, + { .name = "ads131m03", .driver_data = (kernel_ulong_t)&ads131m03_config }, + { .name = "ads131m04", .driver_data = (kernel_ulong_t)&ads131m04_config }, + { .name = "ads131m06", .driver_data = (kernel_ulong_t)&ads131m06_config }, + { .name = "ads131m08", .driver_data = (kernel_ulong_t)&ads131m08_config }, { } }; MODULE_DEVICE_TABLE(spi, ads131m_id); diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c index af87f5f19a0f..da82a6947b2b 100644 --- a/drivers/iio/adc/ti-ads7138.c +++ b/drivers/iio/adc/ti-ads7138.c @@ -227,6 +227,26 @@ static int ads7138_osr_to_bits(int osr) return -EINVAL; } +static int ads7138_read_statistics(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + int ret; + + /* Disable statistics update so the value is not updated mid read */ + ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); + if (ret) + return ret; + + ret = ads7138_i2c_read_block(client, reg, out_values, length); + if (ret) + return ret; + + /* Enable statistics update after read */ + return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); +} + static int ads7138_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -236,28 +256,32 @@ static int ads7138_read_raw(struct iio_dev *indio_dev, u8 values[2]; switch (mask) { + /* + * Reading the statistics registers reinitializes them. This is + * unfortunate but necessary to prevent data races. + */ case IIO_CHAN_INFO_RAW: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_RECENT_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_RECENT_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_PEAK: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MAX_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MAX_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_TROUGH: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MIN_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MIN_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 39a074bce6d5..d27c1f2885fd 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -619,18 +619,18 @@ static int ti_ads7950_probe(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info }, - { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info }, - { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info }, - { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info }, - { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info }, - { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info }, - { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info }, - { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info }, - { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info }, - { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info }, - { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info }, - { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info }, + { .name = "ads7950", .driver_data = (kernel_ulong_t)&ti_ads7950_chip_info }, + { .name = "ads7951", .driver_data = (kernel_ulong_t)&ti_ads7951_chip_info }, + { .name = "ads7952", .driver_data = (kernel_ulong_t)&ti_ads7952_chip_info }, + { .name = "ads7953", .driver_data = (kernel_ulong_t)&ti_ads7953_chip_info }, + { .name = "ads7954", .driver_data = (kernel_ulong_t)&ti_ads7954_chip_info }, + { .name = "ads7955", .driver_data = (kernel_ulong_t)&ti_ads7955_chip_info }, + { .name = "ads7956", .driver_data = (kernel_ulong_t)&ti_ads7956_chip_info }, + { .name = "ads7957", .driver_data = (kernel_ulong_t)&ti_ads7957_chip_info }, + { .name = "ads7958", .driver_data = (kernel_ulong_t)&ti_ads7958_chip_info }, + { .name = "ads7959", .driver_data = (kernel_ulong_t)&ti_ads7959_chip_info }, + { .name = "ads7960", .driver_data = (kernel_ulong_t)&ti_ads7960_chip_info }, + { .name = "ads7961", .driver_data = (kernel_ulong_t)&ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ti_ads7950_id); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index ba5e240aa41a..8ad070b5ec4c 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -453,8 +453,8 @@ static int ads8688_probe(struct spi_device *spi) } static const struct spi_device_id ads8688_id[] = { - { "ads8684", ID_ADS8684 }, - { "ads8688", ID_ADS8688 }, + { .name = "ads8684", .driver_data = ID_ADS8684 }, + { .name = "ads8688", .driver_data = 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 7e57006a8a12..d248bbea0d31 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -357,7 +357,7 @@ static int lmp92064_adc_probe(struct spi_device *spi) } static const struct spi_device_id lmp92064_id_table[] = { - { "lmp92064" }, + { .name = "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 94bbf5afe30e..ea776f80de72 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -240,8 +240,8 @@ static const struct of_device_id tlc4541_dt_ids[] = { MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); static const struct spi_device_id tlc4541_id[] = { - { "tlc3541", TLC3541 }, - { "tlc4541", TLC4541 }, + { .name = "tlc3541", .driver_data = TLC3541 }, + { .name = "tlc4541", .driver_data = TLC4541 }, { } }; MODULE_DEVICE_TABLE(spi, tlc4541_id); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index aba4b10a17ac..125f6d7e2d84 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -121,11 +121,6 @@ struct tsc2046_adc_group_layout { unsigned int skip; }; -struct tsc2046_adc_dcfg { - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct tsc2046_adc_ch_cfg { unsigned int settling_time_us; unsigned int oversampling_ratio; @@ -141,7 +136,6 @@ enum tsc2046_state { struct tsc2046_adc_priv { struct spi_device *spi; - const struct tsc2046_adc_dcfg *dcfg; bool internal_vref; struct iio_trigger *trig; @@ -214,11 +208,6 @@ const struct iio_chan_spec name ## _channels[] = { \ static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12); -static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = { - .channels = tsc2046_adc_channels, - .num_channels = ARRAY_SIZE(tsc2046_adc_channels), -}; - /* * Convert time to a number of samples which can be transferred within this * time. @@ -739,7 +728,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) static int tsc2046_adc_probe(struct spi_device *spi) { - const struct tsc2046_adc_dcfg *dcfg; struct device *dev = &spi->dev; struct tsc2046_adc_priv *priv; struct iio_dev *indio_dev; @@ -752,10 +740,6 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -EINVAL; } - dcfg = spi_get_device_match_data(spi); - if (!dcfg) - return -EINVAL; - spi->mode &= ~SPI_MODE_X_MASK; spi->mode |= SPI_MODE_0; ret = spi_setup(spi); @@ -767,14 +751,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -ENOMEM; priv = iio_priv(indio_dev); - priv->dcfg = dcfg; priv->spi = spi; indio_dev->name = TI_TSC2046_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = dcfg->channels; - indio_dev->num_channels = dcfg->num_channels; + indio_dev->channels = tsc2046_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(tsc2046_adc_channels); indio_dev->info = &tsc2046_adc_info; ret = devm_regulator_get_enable_read_voltage(dev, "vref"); @@ -829,13 +812,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) } static const struct of_device_id ads7950_of_table[] = { - { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e }, + { .compatible = "ti,tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); static const struct spi_device_id tsc2046_adc_spi_ids[] = { - { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e }, + { .name = "tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids); diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c new file mode 100644 index 000000000000..1b55d343982e --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon core driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "versal-sysmon.h" + +/* + * Oversampling ratio values exposed to userspace via IIO. + * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x. + */ +static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + +/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */ +#define SYSMON_TEMP_HYST_MASK BIT(1) + +/* Compute alarm register offset from a channel address */ +#define SYSMON_ALARM_OFFSET(addr) \ + (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE) + +#define SYSMON_CHAN_TEMP(_chan, _address, _name) \ +{ \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = _address, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .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), \ + .datasheet_name = _name, \ +} + +enum sysmon_alarm_bit { + SYSMON_BIT_ALARM0 = 0, + SYSMON_BIT_ALARM1 = 1, + SYSMON_BIT_ALARM2 = 2, + SYSMON_BIT_ALARM3 = 3, + SYSMON_BIT_ALARM4 = 4, + SYSMON_BIT_TEMP = 9, +}; + +/* Temperature event specification: rising threshold + hysteresis only */ +static const struct iio_event_spec sysmon_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Supply event specifications */ +static const struct iio_event_spec sysmon_supply_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +/* + * Static temperature channels (always present). + * + * These are hardware-computed aggregate registers across all active + * temperature satellites: + * temp: current max temperature across all active satellites + * min: current min temperature across all active satellites + * max_max: highest peak recorded since last hardware reset + * min_min: lowest trough recorded since last hardware reset + */ +static const struct iio_chan_spec temp_channels[] = { + SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"), + SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"), + SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"), + SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"), +}; + +static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val) +{ + *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT; +} + +static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val) +{ + *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE; +} + +static void sysmon_supply_rawtoprocessed(int raw_data, int *val) +{ + int mantissa, format, exponent; + + mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data); + exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data); + format = FIELD_GET(SYSMON_FMT_MASK, raw_data); + /* + * When format bit is set the mantissa is two's complement + * (per hardware spec); sign-extend to int for correct arithmetic. + */ + if (format) + mantissa = sign_extend32(mantissa, 15); + + *val = (mantissa * (int)MILLI) >> exponent; +} + +static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data) +{ + int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val); + int format = FIELD_GET(SYSMON_FMT_MASK, reg_val); + int scale, tmp; + + scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent); + tmp = (val * scale) / (int)MILLI; + + if (format) + tmp = clamp(tmp, S16_MIN, S16_MAX); + else + tmp = clamp(tmp, 0, U16_MAX); + + *raw_data = (u16)tmp; +} + +static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir) +{ + if (dir == IIO_EV_DIR_RISING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP; + if (dir == IIO_EV_DIR_FALLING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW; + + return -EINVAL; +} + +static int sysmon_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int regval; + int ret; + + guard(mutex)(&sysmon->lock); + + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) { + *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling : + sysmon->supply_oversampling; + return IIO_VAL_INT; + } + + switch (chan->type) { + case IIO_TEMP: + if (mask == IIO_CHAN_INFO_SCALE) { + /* Q8.7 to millicelsius: raw * 1000 / 128 */ + *val = MILLIDEGREE_PER_DEGREE; + *val2 = BIT(SYSMON_FRACTIONAL_SHIFT); + return IIO_VAL_FRACTIONAL; + } + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, chan->address, ®val); + if (ret) + return ret; + + *val = sign_extend32(regval, 15); + return IIO_VAL_INT; + + case IIO_VOLTAGE: + if (mask != IIO_CHAN_INFO_PROCESSED) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, + chan->address * SYSMON_REG_STRIDE + + SYSMON_SUPPLY_BASE, ®val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(regval, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return BIT(SYSMON_BIT_TEMP); + + return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG); +} + +static int sysmon_read_alarm_config(struct sysmon *sysmon, + unsigned long address) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_test_bits(sysmon->regmap, offset, BIT(shift)); +} + +static int sysmon_write_alarm_config(struct sysmon *sysmon, + unsigned long address, bool enable) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable); +} + +static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int imr; + int config_value; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + + /* IMR bits are 1=masked, invert to get 1=enabled */ + imr = ~imr; + + switch (chan->type) { + case IIO_VOLTAGE: + config_value = sysmon_read_alarm_config(sysmon, chan->address); + if (config_value < 0) + return config_value; + return config_value && (imr & mask); + + case IIO_TEMP: + /* + * Return the administrative state, not the hardware IMR. + * The IRQ handler temporarily masks the interrupt during + * the polling window; reading IMR would show it as disabled. + * temp_mask bit is set when administratively disabled. + */ + return !(sysmon->temp_mask & mask); + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + u32 offset = SYSMON_ALARM_OFFSET(chan->address); + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int alarm_config; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_VOLTAGE: + ret = sysmon_write_alarm_config(sysmon, chan->address, state); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, offset, &alarm_config); + if (ret) + return ret; + + if (alarm_config) + return regmap_write(sysmon->regmap, SYSMON_IER, mask); + + return regmap_write(sysmon->regmap, SYSMON_IDR, mask); + + case IIO_TEMP: + if (state) { + ret = regmap_write(sysmon->regmap, SYSMON_IER, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask &= ~mask; + } else { + ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask |= mask; + } + return 0; + + default: + return -EINVAL; + } +} + +/* + * Recompute the lower threshold register from upper threshold and + * cached hysteresis. Called when either upper threshold or hysteresis + * is written. + */ +static int sysmon_update_temp_lower(struct sysmon *sysmon) +{ + unsigned int upper_reg; + int upper_mc, lower_mc; + u32 raw_val; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992); + sysmon_millicelsius_to_q8p7(&raw_val, lower_mc); + + return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val); +} + +static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, ®_val); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(reg_val, val); + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + *val = sysmon->temp_hysteresis; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(reg_val, val); + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + u32 raw_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + /* Q8.7 signed range: -256000 to +255992 mC */ + if (val < -256000 || val > 255992) + return -EINVAL; + + sysmon_millicelsius_to_q8p7(&raw_val, val); + + ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val); + if (ret) + return ret; + + /* Recompute lower = upper - hysteresis */ + return sysmon_update_temp_lower(sysmon); + + case IIO_EV_INFO_HYSTERESIS: + if (val < 0) + return -EINVAL; + + sysmon->temp_hysteresis = val; + + return sysmon_update_temp_lower(sysmon); + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + /* Clamp to prevent overflow in processedtoraw conversion */ + if (val < -32768 || val > 32767) + return -EINVAL; + + sysmon_supply_processedtoraw(val, reg_val, &raw_val); + + /* + * The hardware threshold register returns FMT and MODE + * bits in the upper 16 bits on read, but only the lower + * 16-bit mantissa is used on write. + */ + return regmap_write(sysmon->regmap, offset, raw_val); + + default: + return -EINVAL; + } +} + +static int sysmon_set_avg_enable(struct sysmon *sysmon, + u32 base, u32 count, u32 val) +{ + struct regmap *map = sysmon->regmap; + int ret; + + for (unsigned int i = 0; i < count; i++) { + ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val); + if (ret) + return ret; + } + + return 0; +} + +static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val) +{ + /* + * HW register encoding is sample_count / 2: + * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based). + */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_TEMP_SAT_OSR, + FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val)); + if (ret) + return ret; + + /* + * Readback fence: the SysMon CONFIG register resides in the + * PMC domain behind the NoC. A posted write may not reach the + * hardware before the next MMIO access. Reading the register + * back forces the interconnect to complete the write, preventing + * a bus hang on the subsequent access. + */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE, + SYSMON_TEMP_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val) +{ + /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_SUPPLY_OSR, + FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val)); + if (ret) + return ret; + + /* Readback fence -- see sysmon_osr_write_temp for details */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE, + SYSMON_SUPPLY_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int i; + int ret; + + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) { + if (val == sysmon_oversampling_avail[i]) + break; + } + if (i == ARRAY_SIZE(sysmon_oversampling_avail)) + return -EINVAL; + + guard(mutex)(&sysmon->lock); + + if (chan->type == IIO_TEMP) { + ret = sysmon_osr_write_temp(sysmon, val); + if (ret) + return ret; + sysmon->temp_oversampling = val; + } else { + ret = sysmon_osr_write_supply(sysmon, val); + if (ret) + return ret; + sysmon->supply_oversampling = val; + } + + return 0; +} + +static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return IIO_VAL_INT; + + return -EINVAL; +} + +static int sysmon_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + *vals = sysmon_oversampling_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sysmon_oversampling_avail); + + return IIO_AVAIL_LIST; +} + +static int sysmon_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->datasheet_name) + return sysfs_emit(label, "%s\n", chan->datasheet_name); + + return -EINVAL; +} + +static const struct iio_info sysmon_iio_info = { + .read_raw = sysmon_read_raw, + .write_raw = sysmon_write_raw, + .write_raw_get_fmt = sysmon_write_raw_get_fmt, + .read_avail = sysmon_read_avail, + .read_label = sysmon_read_label, + .read_event_config = sysmon_read_event_config, + .write_event_config = sysmon_write_event_config, + .read_event_value = sysmon_read_event_value, + .write_event_value = sysmon_write_event_value, +}; + +static void sysmon_push_event(struct iio_dev *indio_dev, u32 address) +{ + const struct iio_chan_spec *chan; + enum iio_event_direction dir; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].address != address) + continue; + + chan = &indio_dev->channels[i]; + /* Temp uses hysteresis mode (rising only), voltage uses window */ + dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_EITHER; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, + chan->channel, + IIO_EV_TYPE_THRESH, + dir), + iio_get_time_ns(indio_dev)); + } +} + +static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event) +{ + u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE; + u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned long alarm_flag_reg; + unsigned int reg_val; + u32 address, bit; + int ret; + + switch (event) { + case SYSMON_BIT_TEMP: + sysmon_push_event(indio_dev, SYSMON_TEMP_MAX); + + ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP)); + if (ret) + return ret; + + sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP); + return 0; + + case SYSMON_BIT_ALARM0: + case SYSMON_BIT_ALARM1: + case SYSMON_BIT_ALARM2: + case SYSMON_BIT_ALARM3: + case SYSMON_BIT_ALARM4: + ret = regmap_read(sysmon->regmap, alarm_flag_offset, ®_val); + if (ret) + return ret; + + alarm_flag_reg = reg_val; + + for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) { + address = bit + SYSMON_ALARM_BITS_PER_REG * event; + sysmon_push_event(indio_dev, address); + ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit)); + if (ret) + return ret; + } + + return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg); + + default: + return -EINVAL; + } +} + +static void sysmon_handle_events(struct iio_dev *indio_dev, + unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS) + sysmon_handle_event(indio_dev, bit); +} + +static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr) +{ + unsigned int status; + u32 ier; + + status = isr & SYSMON_TEMP_INTR_MASK; + + ier = ~status & sysmon->masked_temp; + sysmon->masked_temp &= status; + + /* Only unmask if not administratively disabled by userspace */ + ier &= ~sysmon->temp_mask; + + regmap_write(sysmon->regmap, SYSMON_IER, ier); +} + +/* + * Versal threshold interrupts are level-sensitive. Active threshold + * interrupts are masked in the handler and polled via delayed work + * until the condition clears, then unmasked. + */ +static void sysmon_unmask_worker(struct work_struct *work) +{ + struct sysmon *sysmon = + container_of(work, struct sysmon, sysmon_unmask_work.work); + unsigned int isr; + + /* + * If the ISR read fails, skip processing to avoid acting + * on undefined data. + */ + scoped_guard(spinlock_irq, &sysmon->irq_lock) { + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr)) + break; + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + sysmon_unmask_temp(sysmon, isr); + } + + if (sysmon->masked_temp) + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + else + regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1); +} + +static irqreturn_t sysmon_iio_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int isr, imr; + + guard(spinlock)(&sysmon->irq_lock); + + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) || + regmap_read(sysmon->regmap, SYSMON_IMR, &imr)) + return IRQ_NONE; + + isr &= ~imr; + if (!isr) + return IRQ_NONE; + + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + + sysmon_handle_events(indio_dev, isr); + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + + return IRQ_HANDLED; +} + +static void sysmon_disable_interrupts(void *data) +{ + struct sysmon *sysmon = data; + + regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->masked_temp = 0; + + cancel_delayed_work_sync(&sysmon->sysmon_unmask_work); +} + +static int sysmon_init_interrupt(struct sysmon *sysmon, + struct device *dev, + struct iio_dev *indio_dev, + int irq) +{ + unsigned int imr; + int ret; + + /* Events not supported without IRQ (e.g. I2C path) */ + if (!irq) + return 0; + + INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker); + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK; + + ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon); +} + +/* + * Initialize the cached hysteresis for a temperature channel from the + * current hardware threshold registers: hysteresis = upper - lower. + */ +static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis) +{ + unsigned int upper_reg, lower_reg; + int upper_mc, lower_mc; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc); + *hysteresis = upper_mc - lower_mc; + + return 0; +} + +/** + * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels. + * @indio_dev: IIO device instance + * @dev: Parent device + * @irq: IRQ number (positive enables event channels, 0 disables) + * + * Reads voltage-channels and temperature-channels container nodes from + * firmware and builds the IIO channel array. Static temperature channels + * and event channels are prepended, followed by supply and satellite + * channels from DT. + * + * Event channels and per-channel event specs are only added when the + * device has an IRQ. I2C devices have no interrupt line, and the I2C + * regmap cannot be called from atomic context, so events are not + * supported on that path. + * + * Return: 0 on success, negative errno on failure. + */ +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq) +{ + unsigned int num_chan, num_static, num_supply, num_temp; + unsigned int idx, temp_chan_idx, volt_chan_idx; + struct iio_chan_spec *sysmon_channels; + const char *label; + u32 reg; + int ret; + + struct fwnode_handle *supply_node __free(fwnode_handle) = + device_get_named_child_node(dev, "voltage-channels"); + num_supply = fwnode_get_child_node_count(supply_node); + + struct fwnode_handle *temp_node __free(fwnode_handle) = + device_get_named_child_node(dev, "temperature-channels"); + num_temp = fwnode_get_child_node_count(temp_node); + + num_static = ARRAY_SIZE(temp_channels); + num_chan = size_add(num_temp, size_add(num_static, num_supply)); + sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL); + if (!sysmon_channels) + return -ENOMEM; + + memcpy(sysmon_channels, temp_channels, sizeof(temp_channels)); + + /* Attach event spec to channel 0 when IRQ is available */ + if (irq > 0) { + sysmon_channels[0].event_spec = sysmon_temp_events; + sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events); + } + + idx = num_static; + + /* Supply channels from DT */ + fwnode_for_each_child_node_scoped(supply_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for supply channel\n"); + + if (reg > SYSMON_SUPPLY_IDX_MAX) + return dev_err_probe(dev, -EINVAL, + "supply reg %u exceeds max %u\n", + reg, SYSMON_SUPPLY_IDX_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for supply channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .indexed = 1, + .address = reg, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .event_spec = irq > 0 ? + sysmon_supply_events : NULL, + .num_event_specs = irq > 0 ? + ARRAY_SIZE(sysmon_supply_events) : 0, + .datasheet_name = label, + }; + } + + /* Temperature satellite channels from DT */ + fwnode_for_each_child_node_scoped(temp_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for temp channel\n"); + + if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX) + return dev_err_probe(dev, -EINVAL, + "temp reg %u out of range [1..%u]\n", + reg, SYSMON_TEMP_SAT_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for temp channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .indexed = 1, + .address = SYSMON_TEMP_SAT_BASE + + (reg - 1) * SYSMON_REG_STRIDE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .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), + .datasheet_name = label, + }; + } + + indio_dev->num_channels = idx; + indio_dev->info = &sysmon_iio_info; + + /* + * Assign per-type sequential channel numbers. + * IIO sysfs uses type prefix (in_tempN, in_voltageN) + * so numbers only need to be unique within each type. + */ + temp_chan_idx = 0; + volt_chan_idx = 0; + for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) { + if (sysmon_channels[idx].type == IIO_TEMP) + sysmon_channels[idx].channel = temp_chan_idx++; + else + sysmon_channels[idx].channel = volt_chan_idx++; + } + + indio_dev->channels = sysmon_channels; + + return 0; +} + +/** + * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core + * @dev: Parent device + * @regmap: Register map for hardware access + * + * Return: 0 on success, negative errno on failure. + */ +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) +{ + struct iio_dev *indio_dev; + struct sysmon *sysmon; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon)); + if (!indio_dev) + return -ENOMEM; + + sysmon = iio_priv(indio_dev); + sysmon->regmap = regmap; + sysmon->temp_oversampling = 1; + sysmon->supply_oversampling = 1; + + ret = devm_mutex_init(dev, &sysmon->lock); + if (ret) + return ret; + spin_lock_init(&sysmon->irq_lock); + + /* Disable all interrupts and clear pending status */ + ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq == -EPROBE_DEFER) + return dev_err_probe(dev, irq, "failed to get IRQ\n"); + + indio_dev->name = "versal-sysmon"; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = sysmon_parse_fw(indio_dev, dev, irq); + if (ret) + return ret; + + if (irq > 0) { + /* Set hysteresis mode for temperature threshold */ + ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG, + SYSMON_TEMP_HYST_MASK); + if (ret) + return ret; + + /* Initialize cached hysteresis from hardware registers */ + ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis); + if (ret) + return ret; + + ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Core Driver"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c new file mode 100644 index 000000000000..e9a7629159ab --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-i2c.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon I2C driver + * + * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +#define SYSMON_I2C_INSTR_READ BIT(2) +#define SYSMON_I2C_INSTR_WRITE BIT(3) + +/* + * I2C command frame layout (8 bytes): + * [0..3] data payload (little-endian u32) + * [4..5] register offset >> 2 (little-endian u16) + * [6] instruction (read/write) + * [7] reserved + */ +#define SYSMON_I2C_DATA_OFS 0 +#define SYSMON_I2C_REG_OFS 4 +#define SYSMON_I2C_INSTR_OFS 6 + +static int sysmon_i2c_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + u8 read_buf[4]; + int ret; + + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + ret = i2c_master_recv(client, read_buf, sizeof(read_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(read_buf)) + return -EIO; + + *val = get_unaligned_le32(read_buf); + + return 0; +} + +static int sysmon_i2c_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + int ret; + + put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]); + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + return 0; +} + +/* + * Almost all registers are volatile (live ADC readings, interrupt + * status). The rest are not accessed often enough to benefit from + * caching. + */ +static const struct regmap_config sysmon_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_i2c_reg_read, + .reg_write = sysmon_i2c_reg_write, +}; + +static int sysmon_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* I2C has no IRQ connection; events are not supported */ + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_i2c_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table); + +static const struct i2c_device_id sysmon_i2c_id_table[] = { + { .name = "versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table); + +static struct i2c_driver sysmon_i2c_driver = { + .probe = sysmon_i2c_probe, + .driver = { + .name = "versal-sysmon-i2c", + .of_match_table = sysmon_i2c_of_match_table, + }, + .id_table = sysmon_i2c_id_table, +}; +module_i2c_driver(sysmon_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Conall O'Griofa "); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c new file mode 100644 index 000000000000..529d0486c9f9 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon MMIO platform driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +struct sysmon_mmio { + void __iomem *base; +}; + +static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sysmon_mmio *mmio = context; + + *val = readl(mmio->base + reg); + return 0; +} + +static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct sysmon_mmio *mmio = context; + + /* NPI must be unlocked before any register write except to NPI_LOCK */ + if (reg != SYSMON_NPI_LOCK) + writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK); + writel(val, mmio->base + reg); + + return 0; +} + +static const struct regmap_config sysmon_mmio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_mmio_reg_read, + .reg_write = sysmon_mmio_reg_write, + .fast_io = true, +}; + +static int sysmon_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sysmon_mmio *mmio; + struct regmap *regmap; + + mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL); + if (!mmio) + return -ENOMEM; + + mmio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio->base)) + return PTR_ERR(mmio->base); + + regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_of_match_table); + +static struct platform_driver sysmon_platform_driver = { + .probe = sysmon_platform_probe, + .driver = { + .name = "versal-sysmon", + .of_match_table = sysmon_of_match_table, + }, +}; +module_platform_driver(sysmon_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h new file mode 100644 index 000000000000..bb9a75bf71c0 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Versal SysMon driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#ifndef _VERSAL_SYSMON_H_ +#define _VERSAL_SYSMON_H_ + +#include +#include +#include +#include + +struct device; +struct regmap; + +/* Register offsets (sorted by address) */ +#define SYSMON_NPI_LOCK 0x000C +#define SYSMON_ISR 0x0044 +#define SYSMON_IMR 0x0048 +#define SYSMON_IER 0x004C +#define SYSMON_IDR 0x0050 +#define SYSMON_CONFIG 0x0100 +#define SYSMON_TEMP_MAX 0x1030 +#define SYSMON_TEMP_MIN 0x1034 +#define SYSMON_SUPPLY_BASE 0x1040 +#define SYSMON_ALARM_FLAG 0x1018 +#define SYSMON_ALARM_REG 0x1940 +#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958 +#define SYSMON_TEMP_TH_LOW 0x1970 +#define SYSMON_TEMP_TH_UP 0x1974 +#define SYSMON_SUPPLY_TH_LOW 0x1980 +#define SYSMON_SUPPLY_TH_UP 0x1C80 +#define SYSMON_TEMP_EV_CFG 0x1F84 +#define SYSMON_TEMP_MIN_MIN 0x1F8C +#define SYSMON_TEMP_MAX_MAX 0x1F90 +#define SYSMON_STATUS_RESET 0x1F94 +#define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_TEMP_EN_AVG_BASE 0x24B4 +#define SYSMON_MAX_REG 0x24C0 + +/* NPI unlock value written to SYSMON_NPI_LOCK */ +#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6 + +/* Register stride: 4 bytes per 32-bit register */ +#define SYSMON_REG_STRIDE 4 + +#define SYSMON_SUPPLY_IDX_MAX 159 +#define SYSMON_TEMP_SAT_MAX 64 +#define SYSMON_NO_OF_EVENTS 32 +#define SYSMON_INTR_ALL_MASK GENMASK(31, 0) + +/* ISR/IMR temperature alarm mask (bit 9) */ +#define SYSMON_TEMP_INTR_MASK BIT(9) + +/* SYSMON_CONFIG: supply oversampling ratio */ +#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14) + +/* SYSMON_CONFIG: temperature satellite oversampling ratio */ +#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24) + +/* Per-channel averaging enable register counts */ +#define SYSMON_SUPPLY_EN_AVG_COUNT 5 +#define SYSMON_TEMP_EN_AVG_COUNT 2 + +/* Supply voltage conversion register fields */ +#define SYSMON_MANTISSA_MASK GENMASK(15, 0) +#define SYSMON_FMT_MASK BIT(16) +#define SYSMON_MODE_MASK GENMASK(18, 17) + +/* Q8.7 fractional shift */ +#define SYSMON_FRACTIONAL_SHIFT 7U +#define SYSMON_SUPPLY_MANTISSA_BITS 16 + +/* Bits per alarm register */ +#define SYSMON_ALARM_BITS_PER_REG 32 + +#define SYSMON_UNMASK_WORK_DELAY_MS 500 + +/** + * struct sysmon - Driver data for Versal SysMon + * @regmap: register map for hardware access + * @lock: protects read-modify-write sequences on threshold registers + * and cached state that spans multiple regmap calls + * @irq_lock: protects interrupt mask register updates (MMIO path only) + * @masked_temp: currently masked temperature alarm bits + * @temp_mask: temperature interrupt configuration mask + * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius + * @sysmon_unmask_work: re-enables events after alarm condition clears + * @temp_oversampling: current temp oversampling ratio + * @supply_oversampling: current supply oversampling ratio + */ +struct sysmon { + struct regmap *regmap; + /* + * Protects read-modify-write sequences on threshold registers + * and cached state (oversampling ratios, hysteresis values) + * that spans multiple regmap calls. + */ + struct mutex lock; + /* + * Protects interrupt mask register updates. Only used on the + * MMIO path (fast_io regmap); I2C has no IRQ and never reaches + * the event code that takes this lock. + */ + spinlock_t irq_lock; + unsigned int masked_temp; + unsigned int temp_mask; + int temp_hysteresis; + struct delayed_work sysmon_unmask_work; + unsigned int temp_oversampling; + unsigned int supply_oversampling; +}; + +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); + +#endif /* _VERSAL_SYSMON_H_ */ diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c index 41e0b1d334cc..9b9d7a12ce87 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -1904,7 +1904,7 @@ static int __init ad74115_register_driver(struct spi_driver *spi) } static const struct spi_device_id ad74115_spi_id[] = { - { "ad74115h" }, + { .name = "ad74115h" }, { } }; diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 43bd2079cf6d..2e5a58e48a89 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1457,7 +1457,7 @@ static int ad74413r_probe(struct spi_device *spi) ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, 0, st->chip_info->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, &iio_pollfunc_store_time, diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 654a3a50eb4f..3a6a09ffa526 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -608,3 +608,4 @@ module_platform_driver(rescale_driver); MODULE_DESCRIPTION("IIO rescale driver"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index affc9c9d8488..e12f8604f59f 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -351,19 +351,19 @@ static int ad8366_probe(struct spi_device *spi) } static const struct spi_device_id ad8366_id[] = { - { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, - { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, - { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, - { "adrf5702", (kernel_ulong_t)&adrf5702_chip_info }, - { "adrf5703", (kernel_ulong_t)&adrf5703_chip_info }, - { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info }, - { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info }, - { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info }, - { "hmc271a", (kernel_ulong_t)&hmc271_chip_info }, - { "hmc792a", (kernel_ulong_t)&hmc792_chip_info }, - { "hmc1018a", (kernel_ulong_t)&hmc1018_chip_info }, - { "hmc1019a", (kernel_ulong_t)&hmc1019_chip_info }, - { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info }, + { .name = "ad8366", .driver_data = (kernel_ulong_t)&ad8366_chip_info }, + { .name = "ada4961", .driver_data = (kernel_ulong_t)&ada4961_chip_info }, + { .name = "adl5240", .driver_data = (kernel_ulong_t)&adl5240_chip_info }, + { .name = "adrf5702", .driver_data = (kernel_ulong_t)&adrf5702_chip_info }, + { .name = "adrf5703", .driver_data = (kernel_ulong_t)&adrf5703_chip_info }, + { .name = "adrf5720", .driver_data = (kernel_ulong_t)&adrf5720_chip_info }, + { .name = "adrf5730", .driver_data = (kernel_ulong_t)&adrf5730_chip_info }, + { .name = "adrf5731", .driver_data = (kernel_ulong_t)&adrf5731_chip_info }, + { .name = "hmc271a", .driver_data = (kernel_ulong_t)&hmc271_chip_info }, + { .name = "hmc792a", .driver_data = (kernel_ulong_t)&hmc792_chip_info }, + { .name = "hmc1018a", .driver_data = (kernel_ulong_t)&hmc1018_chip_info }, + { .name = "hmc1019a", .driver_data = (kernel_ulong_t)&hmc1019_chip_info }, + { .name = "hmc1119", .driver_data = (kernel_ulong_t)&hmc1119_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 71e361af2074..9702de3a5074 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -358,7 +358,7 @@ static int ada4250_probe(struct spi_device *spi) } static const struct spi_device_id ada4250_id[] = { - { "ada4250", 0 }, + { .name = "ada4250" }, { } }; MODULE_DEVICE_TABLE(spi, ada4250_id); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 0c266c216525..b58263bcf3cb 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -154,3 +154,4 @@ EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 98acce909854..ecc02a427b92 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -56,6 +56,23 @@ static void iio_dmaengine_buffer_block_done(void *data, iio_dma_buffer_block_done(block); } +/* + * Cyclic transfers run until abort. Cap active cyclic blocks to one until there + * is a use case for more. + */ +static bool iio_dmaengine_buffer_has_active_cyclic(struct dmaengine_buffer *dmaengine_buffer) +{ + struct iio_dma_buffer_block *block; + + guard(spinlock_irqsave)(&dmaengine_buffer->queue.list_lock); + list_for_each_entry(block, &dmaengine_buffer->active, head) { + if (block->cyclic) + return true; + } + + return false; +} + static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) { @@ -79,7 +96,14 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, else dma_dir = DMA_MEM_TO_DEV; + if (block->cyclic && iio_dmaengine_buffer_has_active_cyclic(dmaengine_buffer)) { + dev_err(queue->dev, "cyclic DMA transfer already active\n"); + return -EBUSY; + } + if (block->sg_table) { + unsigned long flags; + sgl = block->sg_table->sgl; nents = sg_nents_for_len(sgl, block->bytes_used); if (nents < 0) @@ -99,9 +123,19 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl = sg_next(sgl); } + if (block->cyclic) + flags = DMA_PREP_REPEAT; + else + flags = DMA_PREP_INTERRUPT; + + /* + * A new transfer may need to end an already active cyclic transfer + * before it can run, so always set the EOT flag. + */ + flags |= DMA_PREP_LOAD_EOT; desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan, vecs, nents, dma_dir, - DMA_PREP_INTERRUPT); + flags); kfree(vecs); } else { max_size = min(block->size, dmaengine_buffer->max_size); @@ -122,8 +156,10 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, if (!desc) return -ENOMEM; - desc->callback_result = iio_dmaengine_buffer_block_done; - desc->callback_param = block; + if (!block->cyclic) { + desc->callback_result = iio_dmaengine_buffer_block_done; + desc->callback_param = block; + } cookie = dmaengine_submit(desc); if (dma_submit_error(cookie)) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 10e912bbf0c5..d035aa33b357 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -216,3 +216,4 @@ EXPORT_SYMBOL_GPL(iio_hw_consumer_disable); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 1e8adbe1790d..1d4a9919df1a 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -87,7 +86,6 @@ struct atlas_data { struct iio_trigger *trig; const struct atlas_device *chip; struct regmap *regmap; - struct irq_work work; unsigned int interrupt_enabled; /* 96-bit data + 32-bit pad + 64-bit timestamp */ __be32 buffer[6] __aligned(8); @@ -412,7 +410,11 @@ static int atlas_buffer_postenable(struct iio_dev *indio_dev) if (ret) return ret; - return atlas_set_interrupt(data, true); + ret = atlas_set_interrupt(data, true); + if (ret) + pm_runtime_put_autosuspend(&data->client->dev); + + return ret; } static int atlas_buffer_predisable(struct iio_dev *indio_dev) @@ -436,13 +438,6 @@ static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { .predisable = atlas_buffer_predisable, }; -static void atlas_work_handler(struct irq_work *work) -{ - struct atlas_data *data = container_of(work, struct atlas_data, work); - - iio_trigger_poll(data->trig); -} - static irqreturn_t atlas_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; @@ -469,7 +464,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct atlas_data *data = iio_priv(indio_dev); - irq_work_queue(&data->work); + iio_trigger_poll_nested(data->trig); return IRQ_HANDLED; } @@ -665,8 +660,6 @@ static int atlas_probe(struct i2c_client *client) goto unregister_trigger; } - init_irq_work(&data->work, atlas_work_handler); - if (client->irq > 0) { /* interrupt pin toggles on new conversion */ ret = devm_request_threaded_irq(&client->dev, client->irq, diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 4cc0e491621a..c34d67bcb2a3 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -24,10 +24,9 @@ static int bme680_i2c_probe(struct i2c_client *client) const char *name = NULL; regmap = devm_regmap_init_i2c(client, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); if (id) name = id->name; diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 785200a6fd65..2175b9564fa1 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -121,16 +121,15 @@ static int bme680_spi_probe(struct spi_device *spi) regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus, bus_context, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); return bme680_core_probe(&spi->dev, regmap, id->name); } static const struct spi_device_id bme680_spi_id[] = { - {"bme680", 0}, + { .name = "bme680" }, { } }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index ce7187ccd706..d126ee12ffe5 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -484,10 +484,8 @@ static int ccs811_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "ccs811_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err_poweroff; - } data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index bbc96c4c6283..7c1ffda38b99 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -320,7 +320,7 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq) IRQF_NO_THREAD, indio_dev->name, indio_dev->trig); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } diff --git a/drivers/iio/chemical/ens160_spi.c b/drivers/iio/chemical/ens160_spi.c index a674c0e1bf4b..7c11d19291c9 100644 --- a/drivers/iio/chemical/ens160_spi.c +++ b/drivers/iio/chemical/ens160_spi.c @@ -39,7 +39,7 @@ static const struct of_device_id ens160_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ens160_spi_of_match); static const struct spi_device_id ens160_spi_id[] = { - { "ens160" }, + { .name = "ens160" }, { } }; MODULE_DEVICE_TABLE(spi, ens160_spi_id); diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index c5425136814b..770571c21521 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor core driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski */ #include @@ -686,7 +686,7 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } @@ -711,7 +711,11 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT; state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT; state->command = command; - mutex_init(&state->lock); + + ret = devm_mutex_init(dev, &state->lock); + if (ret) + return ret; + init_completion(&state->meas_ready); dev_set_drvdata(dev, indio_dev); @@ -770,6 +774,6 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, } EXPORT_SYMBOL_NS(scd30_probe, "IIO_SCD30"); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index abceccdddc71..bcef1e473e34 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski * * I2C slave address: 0x61 */ @@ -136,7 +136,7 @@ static struct i2c_driver scd30_i2c_driver = { }; module_i2c_driver(scd30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 7fefdadbfa0a..31b0fc957b08 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor serial driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski */ #include #include @@ -257,7 +257,7 @@ static struct serdev_device_driver scd30_serdev_driver = { }; module_serdev_device_driver(scd30_serdev_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index f10bbebc29e4..379c1c4af8d8 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client) data->iaq_thread = kthread_run(sgp_iaq_threadfn, data, "%s-iaq", data->client->name); + if (IS_ERR(data->iaq_thread)) + return dev_err_probe(dev, PTR_ERR(data->iaq_thread), + "failed to start IAQ thread\n"); return 0; } diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 90f1adb8c89f..2d3a876cc06e 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski * * I2C slave address: 0x69 */ @@ -252,7 +252,7 @@ static struct i2c_driver sps30_i2c_driver = { }; module_i2c_driver(sps30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index 80eab9b2e4bf..0cdba8703e93 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor serial driver * - * Copyright (c) 2021 Tomasz Duszynski + * Copyright (c) 2021 Tomasz Duszynski */ #include #include @@ -425,7 +425,7 @@ static struct serdev_device_driver sps30_serial_driver = { }; module_serdev_device_driver(sps30_serial_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index c115a72832b2..9e7c26e37880 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -72,8 +72,7 @@ static const struct { {HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0}, }; -static void simple_div(int dividend, int divisor, int *whole, - int *micro_frac) +static void simple_div(int dividend, int divisor, int *whole, int *micro_frac) { int rem; int exp = 0; @@ -111,7 +110,7 @@ for 10^-2. Negative numbers are 2's complement */ static void convert_from_vtf_format(u32 value, int size, int exp, - int *val1, int *val2) + int *val1, int *val2) { int sign = 1; @@ -176,7 +175,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, "IIO_HID_ATTRIBUTES"); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; @@ -203,7 +202,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, "IIO_HID"); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -238,15 +237,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, "IIO_HID"); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -268,8 +267,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -282,9 +281,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, } EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID"); - int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -293,8 +291,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity.size, - st->sensitivity.unit_expo, - val1, val2); + st->sensitivity.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, st->sensitivity.index, sizeof(value), &value); @@ -303,8 +301,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -324,8 +322,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity_rel.size, - st->sensitivity_rel.unit_expo, - val1, val2); + st->sensitivity_rel.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, st->sensitivity_rel.index, sizeof(value), &value); @@ -334,8 +332,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -356,8 +354,8 @@ EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); * 1.001745329 ->exp:4-> val0[10017]val1[453290000] * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_nano(int *val0, int *val1, int scale0, - int scale1, int exp) +static void adjust_exponent_nano(int *val0, int *val1, + int scale0, int scale1, int exp) { int divisor; int i; @@ -404,8 +402,8 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0, } int hid_sensor_format_scale(u32 usage_id, - struct hid_sensor_hub_attribute_info *attr_info, - int *val0, int *val1) + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) { int i; int exp; @@ -415,12 +413,11 @@ int hid_sensor_format_scale(u32 usage_id, for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { if (unit_conversion[i].usage_id == usage_id && - unit_conversion[i].unit == attr_info->units) { - exp = hid_sensor_convert_exponent( - attr_info->unit_expo); + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent(attr_info->unit_expo); adjust_exponent_nano(val0, val1, - unit_conversion[i].scale_val0, - unit_conversion[i].scale_val1, exp); + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); break; } } @@ -438,8 +435,8 @@ EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, "IIO_HID"); static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st) + u32 usage_id, + struct hid_sensor_common *st) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -473,9 +470,10 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st) int ret; int value; - ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id, - st->report_latency.index, sizeof(value), - &value); + ret = sensor_hub_get_feature(st->hsdev, + st->report_latency.report_id, + st->report_latency.index, + sizeof(value), &value); if (ret < 0) return ret; @@ -498,10 +496,10 @@ bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, "IIO_HID_ATTRIBUTES"); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st, - const u32 *sensitivity_addresses, - u32 sensitivity_addresses_len) + u32 usage_id, + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; @@ -527,7 +525,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, - &st->sensitivity); + &st->sensitivity); sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -578,8 +576,9 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, timestamp.index, timestamp.report_id); ret = sensor_hub_get_feature(hsdev, - st->power_state.report_id, - st->power_state.index, sizeof(value), &value); + st->power_state.report_id, + st->power_state.index, + sizeof(value), &value); if (ret < 0) return ret; if (value < 0) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 417c4ab8c1b2..60808e2430ca 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -233,7 +233,7 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb) + struct hid_sensor_common *attrb) { const struct iio_dev_attr **fifo_attrs; int ret; @@ -266,7 +266,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); - if (trig == NULL) { + if (!trig) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); return -ENOMEM; } @@ -314,7 +314,9 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; } @@ -322,6 +324,7 @@ static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + return _hid_sensor_power_state(attrb, true); } diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index f94fca4f1edf..589de858e369 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -15,7 +15,7 @@ struct iio_dev; extern const struct dev_pm_ops hid_sensor_pm_ops; int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb); + struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb); int hid_sensor_power_state(struct hid_sensor_common *st, bool state); diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index e0b10366ed2b..88a82d1370c5 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -10,18 +10,19 @@ #include -/* compute jitter, min and max following jitter in per mille */ -#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \ - (div_s64((_val) * (_jitter), 1000)) +/* compute min and max following jitter in per mille */ #define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \ (((_val) * (1000 - (_jitter))) / 1000) #define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ (((_val) * (1000 + (_jitter))) / 1000) +/* minimum timestamp delta between 2 interrupts for measuring period (20ms) */ +#define INV_SENSORS_MIN_IT_DELTA (20 * NSEC_PER_MSEC) + /* Add a new value inside an accumulator and update the estimate value */ -static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val) +static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, u32 val) { - uint64_t sum = 0; + u64 sum = 0; size_t i; acc->values[acc->idx++] = val; @@ -58,9 +59,9 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, "IIO_INV_SENSORS_TIMESTAMP"); int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, - uint32_t period, bool fifo) + u32 period, bool fifo) { - uint32_t mult; + u32 mult; /* when FIFO is on, prevent odr change if one is already pending */ if (fifo && ts->new_mult != 0) @@ -78,9 +79,9 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, "IIO_INV_SENSORS_TIMESTAMP"); -static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period) +static bool inv_validate_period(struct inv_sensors_timestamp *ts, u32 period) { - uint32_t period_min, period_max; + u32 period_min, period_max; /* check that period is acceptable */ period_min = ts->min_period * ts->mult; @@ -92,9 +93,9 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio } static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, - uint32_t period) + u32 period) { - uint32_t new_chip_period; + u32 new_chip_period; if (!inv_validate_period(ts, period)) return false; @@ -102,78 +103,84 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, /* update chip internal period estimation */ new_chip_period = period / ts->mult; inv_update_acc(&ts->chip_period, new_chip_period); - ts->period = ts->mult * ts->chip_period.val; return true; } -static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) +static u32 inv_align_timestamp_it(struct inv_sensors_timestamp *ts, + unsigned int sample_nb) { - const int64_t period_min = (int64_t)ts->min_period * ts->mult; - const int64_t period_max = (int64_t)ts->max_period * ts->mult; - int64_t add_max, sub_max; - int64_t delta, jitter; - int64_t adjust; + const s64 period_min = (s64)ts->min_period * ts->mult; + const s64 period_max = (s64)ts->max_period * ts->mult; + s64 new_period; - /* delta time between last sample and last interrupt */ - delta = ts->it.lo - ts->timestamp; + /* compute new period aligning last timestamp with interrupt timestamp */ + new_period = div_s64(ts->it.up - ts->timestamp, sample_nb); - /* adjust timestamp while respecting jitter */ - add_max = period_max - (int64_t)ts->period; - sub_max = period_min - (int64_t)ts->period; - jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); - if (delta > jitter) - adjust = add_max; - else if (delta < -jitter) - adjust = sub_max; - else - adjust = 0; - - ts->timestamp += adjust; + /* ensure that period never overflows the jitter */ + return clamp(new_period, period_min, period_max); } void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - size_t sample_nb, int64_t timestamp) + size_t sample_nb, s64 timestamp) { struct inv_sensors_timestamp_interval *it; - int64_t delta, interval; - uint32_t period; + s64 delta, delta_threshold, interval; + u32 period; bool valid = false; if (sample_nb == 0) return; + /* no previous data, compute theoretical value from interrupt */ + if (ts->timestamp == 0) { + /* elapsed time: sensor period * sensor samples number */ + interval = (s64)ts->period * (s64)sample_nb; + ts->timestamp = timestamp - interval; + } + + /* update delta timestamps and estimated period */ + it = &ts->delta; + ts->delta_counter += sample_nb; + delta = timestamp - it->up; + delta_threshold = INV_SENSORS_TIMESTAMP_MIN(INV_SENSORS_MIN_IT_DELTA, ts->chip.jitter); + if (delta >= delta_threshold) { + it->lo = it->up; + it->up = timestamp; + if (it->lo != 0) { + /* compute period: delta time divided by number of samples */ + delta = it->up - it->lo; + period = div_s64(delta, ts->delta_counter); + inv_update_chip_period(ts, period); + } + ts->delta_counter = 0; + } + /* update interrupt timestamp and compute chip and sensor periods */ it = &ts->it; it->lo = it->up; it->up = timestamp; delta = it->up - it->lo; if (it->lo != 0) { - /* compute period: delta time divided by number of samples */ + /* compute period and check validity */ period = div_s64(delta, sample_nb); - valid = inv_update_chip_period(ts, period); - } - - /* no previous data, compute theoretical value from interrupt */ - if (ts->timestamp == 0) { - /* elapsed time: sensor period * sensor samples number */ - interval = (int64_t)ts->period * (int64_t)sample_nb; - ts->timestamp = it->up - interval; - return; + valid = inv_validate_period(ts, period); } /* if interrupt interval is valid, sync with interrupt timestamp */ if (valid) - inv_align_timestamp_it(ts); + ts->period = inv_align_timestamp_it(ts, sample_nb); + else + ts->period = ts->mult * ts->chip_period.val; } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP"); void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, + u32 fifo_period, size_t fifo_nb, unsigned int fifo_no) { - int64_t interval; - uint32_t fifo_mult; + s64 interval; + u32 fifo_mult; if (ts->new_mult == 0) return; @@ -194,7 +201,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, fifo_mult = fifo_period / ts->chip.clock_period; fifo_period = fifo_mult * ts->chip_period.val; /* computes time interval between interrupt and this sample */ - interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; + interval = (s64)(fifo_nb - fifo_no) * (s64)fifo_period; ts->timestamp = ts->it.up - interval; } } diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 8a8ab688d798..7b3e9732cc3e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -211,10 +211,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, irq_trig, sdata->trig->name, sdata->trig); - if (err) { - dev_err(parent, "failed to request trigger IRQ.\n"); + if (err) return err; - } err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542..3b3c77d8b3e1 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -11,8 +11,11 @@ config AD3530R depends on SPI select REGMAP_SPI help - Say yes here to build support for Analog Devices AD3530R, AD3531R - Digital to Analog Converter. + Say yes here to build support for the following Analog Devices + Digital to Analog Converters: + - AD3530/AD3530R (8-channel) + - AD3531/AD3531R (4-channel) + - AD3532/AD3532R (16-channel) To compile this driver as a module, choose M here: the module will be called ad3530r. @@ -240,15 +243,26 @@ config LTC2688 config AD5686 tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER config AD5686_SPI tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" depends on SPI select AD5686 help - Say yes here to build support for Analog Devices AD5672R, AD5674R, - AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, - AD5686R Voltage Output Digital to Analog Converter. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5310R, AD5681R, AD5682R, AD5683, AD5683R + - Dual-channel: + AD5313R, AD5687, AD5687R, AD5689, AD5689R + - Quad-channel: + AD5317R, AD5684, AD5684R, AD5685R, AD5686, AD5686R + - 8-channel: + AD5672R, AD5676, AD5676R + - 16-channel: + AD5674, AD5674R, AD5679, AD5679R To compile this driver as a module, choose M here: the module will be called ad5686. @@ -258,10 +272,18 @@ config AD5696_I2C depends on I2C select AD5686 help - Say yes here to build support for Analog Devices AD5311R, AD5337, - AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to - Analog converters. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5311R, AD5691R, AD5692R, AD5693, AD5693R + - Dual-channel: + AD5338R, AD5697R + - Quad-channel: + AD5316R, AD5694, AD5694R, AD5695R, AD5696, AD5696R + - 8-channel: + AD5671R, AD5675, AD5675R + - 16-channel: + AD5673R, AD5677R To compile this driver as a module, choose M here: the module will be called ad5696. @@ -527,6 +549,17 @@ config MAX5821 Say yes here to build support for Maxim MAX5821 10 bits DAC. +config MCF54415_DAC + tristate "NXP MCF54415 DAC driver" + depends on M5441x || COMPILE_TEST + select REGMAP_MMIO + help + Say yes here if you want to build support for NXP ColdFire + MCF54415/6/7/8 12-bit DAC module. + + To compile this driver as a module, choose M here: the module + will be called mcf54415_dac. + config MCP4725 tristate "MCP4725/6 DAC driver" depends on I2C @@ -549,9 +582,20 @@ config MCP4728 To compile this driver as a module, choose M here: the module will be called mcp4728. +config MCP47A1 + tristate "MCP47A1 DAC driver" + depends on I2C + help + Say Y here if you want to build a driver for the Microchip + MCP47A1 digital-to-analog converter with an I2C interface. + + To compile this driver as a module, choose M here: the module + will be called mcp47a1. + config MCP47FEB02 tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" depends on I2C + select REGMAP_I2C help Say yes here if you want to build the driver for the Microchip: - 8-bit DAC: diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 003431798498..992f8930f95c 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -52,8 +52,10 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX22007) += max22007.o obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o +obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o +obj-$(CONFIG_MCP47A1) += mcp47a1.o obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 4e911bfb6fc5..06b7f280f61b 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -2,6 +2,7 @@ /* * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver + * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver * * Copyright 2025 Analog Devices Inc. */ @@ -38,6 +39,25 @@ #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +/* AD3532R/AD3532 bank 0 registers (channels 0-7) */ +#define AD3532R_INTERFACE_CONFIG_A_0 0x1000 +#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 +#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021 +#define AD3532R_OUTPUT_CONTROL_0 0x102A +#define AD3532R_REFERENCE_CONTROL_0 0x103C +#define AD3532R_SW_LDAC_TRIG_0 0x10E5 +#define AD3532R_INPUT_CH_0 0x10EB + +/* AD3532R/AD3532 bank 1 registers (channels 8-15) */ +#define AD3532R_INTERFACE_CONFIG_A_1 0x3000 +#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020 +#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021 +#define AD3532R_OUTPUT_CONTROL_1 0x302A +#define AD3532R_REFERENCE_CONTROL_1 0x303C +#define AD3532R_SW_LDAC_TRIG_1 0x30E5 +#define AD3532R_INPUT_CH_1 0x30EB +#define AD3532R_MAX_REG_ADDR 0x30F9 + #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) @@ -49,8 +69,10 @@ #define AD3530R_LDAC_PULSE_US 100 #define AD3530R_DAC_MAX_VAL GENMASK(15, 0) -#define AD3530R_MAX_CHANNELS 8 +#define AD3530R_CH_PER_REG 4 +#define AD3530R_CH_PER_BANK 8 #define AD3531R_MAX_CHANNELS 4 +#define AD3532R_MAX_CHANNELS 16 enum ad3530r_mode { AD3530R_NORMAL_OP, @@ -67,9 +89,16 @@ struct ad3530r_chan { struct ad3530r_chip_info { const char *name; const struct iio_chan_spec *channels; + const struct regmap_config *regmap_config; int (*input_ch_reg)(unsigned int channel); + int (*sw_ldac_trig_reg)(unsigned int channel); + const unsigned int *interface_config_a; + const unsigned int *output_control; + const unsigned int *reference_control; + const unsigned int *op_mode; unsigned int num_channels; - unsigned int sw_ldac_trig_reg; + unsigned int num_banks; + unsigned int num_op_mode_regs; bool internal_ref_support; }; @@ -77,7 +106,7 @@ struct ad3530r_state { struct regmap *regmap; /* lock to protect against multiple access to the device and shared data */ struct mutex lock; - struct ad3530r_chan chan[AD3530R_MAX_CHANNELS]; + struct ad3530r_chan chan[AD3532R_MAX_CHANNELS]; const struct ad3530r_chip_info *chip_info; struct gpio_desc *ldac_gpio; int vref_mV; @@ -98,6 +127,14 @@ static int ad3531r_input_ch_reg(unsigned int channel) return 2 * channel + AD3531R_INPUT_CH; } +static int ad3532r_input_ch_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + unsigned int local_ch = channel % AD3530R_CH_PER_BANK; + + return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0); +} + static const char * const ad3530r_powerdown_modes[] = { "1kohm_to_gnd", "7.7kohm_to_gnd", @@ -110,6 +147,12 @@ static const char * const ad3531r_powerdown_modes[] = { "16kohm_to_gnd", }; +static const char * const ad3532r_powerdown_modes[] = { + "1kohm_to_gnd", + "10kohm_to_gnd", + "three_state", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -145,6 +188,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3532r_powerdown_mode_enum = { + .items = ad3532r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3532r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -189,6 +239,62 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int bank, local_ch, reg_in_bank, ch_in_reg; + unsigned int reg, mask, val; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + bank = chan->channel / AD3530R_CH_PER_BANK; + local_ch = chan->channel % AD3530R_CH_PER_BANK; + reg_in_bank = local_ch / AD3530R_CH_PER_REG; + ch_in_reg = local_ch % AD3530R_CH_PER_REG; + + reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 : + AD3532R_OUTPUT_OPERATING_MODE_0); + mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg); + + guard(mutex)(&st->lock); + if (powerdown) { + val = field_prep(mask, st->chan[chan->channel].powerdown_mode); + ret = regmap_update_bits(st->regmap, reg, mask, val); + } else { + ret = regmap_clear_bits(st->regmap, reg, mask); + } + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + +static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3530R_SW_LDAC_TRIG_A; +} + +static int ad3531r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3531R_SW_LDAC_TRIG_A; +} + +static int ad3532r_trigger_sw_ldac_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + + return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -214,7 +320,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, if (st->ldac_gpio) return ad3530r_trigger_hw_ldac(st->ldac_gpio); - return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan), AD3530R_SLD_TRIG_A); } @@ -301,6 +407,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { { } }; +static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3532r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3532r_powerdown_mode_enum), + { } +}; + #define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ @@ -330,48 +449,215 @@ static const struct iio_chan_spec ad3531r_channels[] = { AD3530R_CHAN(3, ad3531r_ext_info), }; +static const struct iio_chan_spec ad3532r_channels[] = { + AD3530R_CHAN(0, ad3532r_ext_info), + AD3530R_CHAN(1, ad3532r_ext_info), + AD3530R_CHAN(2, ad3532r_ext_info), + AD3530R_CHAN(3, ad3532r_ext_info), + AD3530R_CHAN(4, ad3532r_ext_info), + AD3530R_CHAN(5, ad3532r_ext_info), + AD3530R_CHAN(6, ad3532r_ext_info), + AD3530R_CHAN(7, ad3532r_ext_info), + AD3530R_CHAN(8, ad3532r_ext_info), + AD3530R_CHAN(9, ad3532r_ext_info), + AD3530R_CHAN(10, ad3532r_ext_info), + AD3530R_CHAN(11, ad3532r_ext_info), + AD3530R_CHAN(12, ad3532r_ext_info), + AD3530R_CHAN(13, ad3532r_ext_info), + AD3530R_CHAN(14, ad3532r_ext_info), + AD3530R_CHAN(15, ad3532r_ext_info), +}; + +static const unsigned int ad3530r_if_config[] = { + AD3530R_INTERFACE_CONFIG_A, +}; + +static const unsigned int ad3530r_out_ctrl[] = { + AD3530R_OUTPUT_CONTROL_0, +}; + +static const unsigned int ad3530r_ref_ctrl[] = { + AD3530R_REFERENCE_CONTROL_0, +}; + +static const unsigned int ad3530r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, + AD3530R_OUTPUT_OPERATING_MODE_1, +}; + +static const unsigned int ad3531r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, +}; + +static const unsigned int ad3532r_if_config[] = { + AD3532R_INTERFACE_CONFIG_A_0, + AD3532R_INTERFACE_CONFIG_A_1, +}; + +static const unsigned int ad3532r_out_ctrl[] = { + AD3532R_OUTPUT_CONTROL_0, + AD3532R_OUTPUT_CONTROL_1, +}; + +static const unsigned int ad3532r_ref_ctrl[] = { + AD3532R_REFERENCE_CONTROL_0, + AD3532R_REFERENCE_CONTROL_1, +}; + +static const unsigned int ad3532r_op_mode[] = { + AD3532R_OUTPUT_OPERATING_MODE_0, + AD3532R_OUTPUT_OPERATING_MODE_1, + AD3532R_OUTPUT_OPERATING_MODE_2, + AD3532R_OUTPUT_OPERATING_MODE_3, +}; + +static const struct regmap_config ad3530r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3530R_MAX_REG_ADDR, +}; + +static const struct regmap_config ad3532r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3532R_MAX_REG_ADDR, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = true, }; static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = true, }; +static const struct ad3530r_chip_info ad3532_chip = { + .name = "ad3532", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3532r_chip = { + .name = "ad3532r", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = true, +}; + +static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int mask) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_set_bits(st->regmap, regs[i], mask); + if (ret) + return ret; + } + + return 0; +} + +static int ad3530r_write_reg_banks(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int val) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_write(st->regmap, regs[i], val); + if (ret) + return ret; + } + + return 0; +} + static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) { + const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; - int i, ret; u8 range_multiplier, val; + int ret; reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) @@ -384,8 +670,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) gpiod_set_value_cansleep(reset_gpio, 0); } else { /* Perform software reset */ - ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, - AD3530R_SW_RESET, AD3530R_SW_RESET); + ret = ad3530r_set_reg_bank_bits(st, chip_info->interface_config_a, + chip_info->num_banks, + AD3530R_SW_RESET); if (ret) return ret; } @@ -394,8 +681,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) range_multiplier = 1; if (device_property_read_bool(dev, "adi,range-double")) { - ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, - AD3530R_OUTPUT_CONTROL_RANGE); + ret = ad3530r_set_reg_bank_bits(st, chip_info->output_control, + chip_info->num_banks, + AD3530R_OUTPUT_CONTROL_RANGE); if (ret) return ret; @@ -405,8 +693,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) if (external_vref_uV) { st->vref_mV = range_multiplier * external_vref_uV / MILLI; } else { - ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, - AD3530R_REFERENCE_CONTROL_SEL); + ret = ad3530r_set_reg_bank_bits(st, chip_info->reference_control, + chip_info->num_banks, + AD3530R_REFERENCE_CONTROL_SEL); if (ret) return ret; @@ -419,18 +708,12 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode, + st->chip_info->num_op_mode_regs, val); if (ret) return ret; - if (st->chip_info->num_channels > 4) { - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, - val); - if (ret) - return ret; - } - - for (i = 0; i < st->chip_info->num_channels; i++) + for (unsigned int i = 0; i < st->chip_info->num_channels; i++) st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); @@ -441,12 +724,6 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) return 0; } -static const struct regmap_config ad3530r_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .max_register = AD3530R_MAX_REG_ADDR, -}; - static const struct iio_info ad3530r_info = { .read_raw = ad3530r_read_raw, .write_raw = ad3530r_write_raw, @@ -467,7 +744,11 @@ static int ad3530r_probe(struct spi_device *spi) st = iio_priv(indio_dev); - st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config); + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, st->chip_info->regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to init regmap"); @@ -476,10 +757,6 @@ static int ad3530r_probe(struct spi_device *spi) if (ret) return ret; - st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), regulators); if (ret) @@ -509,10 +786,12 @@ static int ad3530r_probe(struct spi_device *spi) } static const struct spi_device_id ad3530r_id[] = { - { "ad3530", (kernel_ulong_t)&ad3530_chip }, - { "ad3530r", (kernel_ulong_t)&ad3530r_chip }, - { "ad3531", (kernel_ulong_t)&ad3531_chip }, - { "ad3531r", (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3530", .driver_data = (kernel_ulong_t)&ad3530_chip }, + { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip }, + { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip }, + { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip }, + { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -522,6 +801,8 @@ static const struct of_device_id ad3530r_of_match[] = { { .compatible = "adi,ad3530r", .data = &ad3530r_chip }, { .compatible = "adi,ad3531", .data = &ad3531_chip }, { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, + { .compatible = "adi,ad3532", .data = &ad3532_chip }, + { .compatible = "adi,ad3532r", .data = &ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(of, ad3530r_of_match); diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index 02a124ac4855..f865843aa439 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -590,7 +590,7 @@ static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, int i; for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { - len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", + len += scnprintf(buf + len, sizeof(buf) - len, "%s ", dbgfs_attr_source[i]); } buf[len - 1] = '\n'; diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index b5ec33f5c317..298f3a703571 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -920,22 +920,22 @@ static int ad5064_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5064_spi_ids[] = { - {"ad5024", ID_AD5024}, - {"ad5025", ID_AD5025}, - {"ad5044", ID_AD5044}, - {"ad5045", ID_AD5045}, - {"ad5064", ID_AD5064}, - {"ad5064-1", ID_AD5064_1}, - {"ad5065", ID_AD5065}, - {"ad5628-1", ID_AD5628_1}, - {"ad5628-2", ID_AD5628_2}, - {"ad5648-1", ID_AD5648_1}, - {"ad5648-2", ID_AD5648_2}, - {"ad5666-1", ID_AD5666_1}, - {"ad5666-2", ID_AD5666_2}, - {"ad5668-1", ID_AD5668_1}, - {"ad5668-2", ID_AD5668_2}, - {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ + { .name = "ad5024", .driver_data = ID_AD5024 }, + { .name = "ad5025", .driver_data = ID_AD5025 }, + { .name = "ad5044", .driver_data = ID_AD5044 }, + { .name = "ad5045", .driver_data = ID_AD5045 }, + { .name = "ad5064", .driver_data = ID_AD5064 }, + { .name = "ad5064-1", .driver_data = ID_AD5064_1 }, + { .name = "ad5065", .driver_data = ID_AD5065 }, + { .name = "ad5628-1", .driver_data = ID_AD5628_1 }, + { .name = "ad5628-2", .driver_data = ID_AD5628_2 }, + { .name = "ad5648-1", .driver_data = ID_AD5648_1 }, + { .name = "ad5648-2", .driver_data = ID_AD5648_2 }, + { .name = "ad5666-1", .driver_data = ID_AD5666_1 }, + { .name = "ad5666-2", .driver_data = ID_AD5666_2 }, + { .name = "ad5668-1", .driver_data = ID_AD5668_1 }, + { .name = "ad5668-2", .driver_data = ID_AD5668_2 }, + { .name = "ad5668-3", .driver_data = ID_AD5668_2 }, /* similar enough to ad5668-2 */ { } }; MODULE_DEVICE_TABLE(spi, ad5064_spi_ids); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 20316fd568e6..c1553f18a26a 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -523,14 +523,14 @@ static void ad5360_remove(struct spi_device *spi) } static const struct spi_device_id ad5360_ids[] = { - { "ad5360", ID_AD5360 }, - { "ad5361", ID_AD5361 }, - { "ad5362", ID_AD5362 }, - { "ad5363", ID_AD5363 }, - { "ad5370", ID_AD5370 }, - { "ad5371", ID_AD5371 }, - { "ad5372", ID_AD5372 }, - { "ad5373", ID_AD5373 }, + { .name = "ad5360", .driver_data = ID_AD5360 }, + { .name = "ad5361", .driver_data = ID_AD5361 }, + { .name = "ad5362", .driver_data = ID_AD5362 }, + { .name = "ad5363", .driver_data = ID_AD5363 }, + { .name = "ad5370", .driver_data = ID_AD5370 }, + { .name = "ad5371", .driver_data = ID_AD5371 }, + { .name = "ad5372", .driver_data = ID_AD5372 }, + { .name = "ad5373", .driver_data = ID_AD5373 }, { } }; MODULE_DEVICE_TABLE(spi, ad5360_ids); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 2e587bdd3214..4a5ad419f4ea 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -446,22 +446,22 @@ static int ad5380_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5380_spi_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, + { .name = "ad5380-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5380-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5381-3", .driver_data = ID_AD5381_3 }, + { .name = "ad5381-5", .driver_data = ID_AD5381_5 }, + { .name = "ad5382-3", .driver_data = ID_AD5382_3 }, + { .name = "ad5382-5", .driver_data = ID_AD5382_5 }, + { .name = "ad5383-3", .driver_data = ID_AD5383_3 }, + { .name = "ad5383-5", .driver_data = ID_AD5383_5 }, + { .name = "ad5384-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5384-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5390-3", .driver_data = ID_AD5390_3 }, + { .name = "ad5390-5", .driver_data = ID_AD5390_5 }, + { .name = "ad5391-3", .driver_data = ID_AD5391_3 }, + { .name = "ad5391-5", .driver_data = ID_AD5391_5 }, + { .name = "ad5392-3", .driver_data = ID_AD5392_3 }, + { .name = "ad5392-5", .driver_data = ID_AD5392_5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); diff --git a/drivers/iio/dac/ad5446-i2c.c b/drivers/iio/dac/ad5446-i2c.c index 9797fc3e57a9..286695e240c3 100644 --- a/drivers/iio/dac/ad5446-i2c.c +++ b/drivers/iio/dac/ad5446-i2c.c @@ -83,7 +83,7 @@ static const struct of_device_id ad5446_i2c_of_ids[] = { { .compatible = "adi,ad5622", .data = &ad5622_chip_info }, { } }; -MODULE_DEVICE_TABLE(OF, ad5446_i2c_of_ids); +MODULE_DEVICE_TABLE(of, ad5446_i2c_of_ids); static struct i2c_driver ad5446_i2c_driver = { .driver = { diff --git a/drivers/iio/dac/ad5446-spi.c b/drivers/iio/dac/ad5446-spi.c index 54bd0e113f40..85bb8829046a 100644 --- a/drivers/iio/dac/ad5446-spi.c +++ b/drivers/iio/dac/ad5446-spi.c @@ -163,38 +163,38 @@ static const struct ad5446_chip_info ad5662_chip_info = { }; static const struct spi_device_id ad5446_spi_ids[] = { - {"ad5300", (kernel_ulong_t)&ad5300_chip_info}, - {"ad5310", (kernel_ulong_t)&ad5310_chip_info}, - {"ad5320", (kernel_ulong_t)&ad5320_chip_info}, - {"ad5444", (kernel_ulong_t)&ad5444_chip_info}, - {"ad5446", (kernel_ulong_t)&ad5446_chip_info}, - {"ad5450", (kernel_ulong_t)&ad5450_chip_info}, - {"ad5451", (kernel_ulong_t)&ad5451_chip_info}, - {"ad5452", (kernel_ulong_t)&ad5444_chip_info}, /* ad5452 is compatible to the ad5444 */ - {"ad5453", (kernel_ulong_t)&ad5446_chip_info}, /* ad5453 is compatible to the ad5446 */ - {"ad5512a", (kernel_ulong_t)&ad5512a_chip_info}, - {"ad5541a", (kernel_ulong_t)&ad5541a_chip_info}, - {"ad5542", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542 are compatible */ - {"ad5542a", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542a are compatible */ - {"ad5543", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5543 are compatible */ - {"ad5553", (kernel_ulong_t)&ad5553_chip_info}, - {"ad5600", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5600 are compatible */ - {"ad5601", (kernel_ulong_t)&ad5601_chip_info}, - {"ad5611", (kernel_ulong_t)&ad5611_chip_info}, - {"ad5621", (kernel_ulong_t)&ad5621_chip_info}, - {"ad5641", (kernel_ulong_t)&ad5641_chip_info}, - {"ad5620-2500", (kernel_ulong_t)&ad5620_2500_chip_info}, /* AD5620/40/60: */ + { .name = "ad5300", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, + { .name = "ad5310", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "ad5320", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "ad5444", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, + { .name = "ad5446", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, + { .name = "ad5450", .driver_data = (kernel_ulong_t)&ad5450_chip_info }, + { .name = "ad5451", .driver_data = (kernel_ulong_t)&ad5451_chip_info }, + { .name = "ad5452", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, /* ad5452 is compatible to the ad5444 */ + { .name = "ad5453", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, /* ad5453 is compatible to the ad5446 */ + { .name = "ad5512a", .driver_data = (kernel_ulong_t)&ad5512a_chip_info }, + { .name = "ad5541a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, + { .name = "ad5542", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542 are compatible */ + { .name = "ad5542a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542a are compatible */ + { .name = "ad5543", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5543 are compatible */ + { .name = "ad5553", .driver_data = (kernel_ulong_t)&ad5553_chip_info }, + { .name = "ad5600", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5600 are compatible */ + { .name = "ad5601", .driver_data = (kernel_ulong_t)&ad5601_chip_info }, + { .name = "ad5611", .driver_data = (kernel_ulong_t)&ad5611_chip_info }, + { .name = "ad5621", .driver_data = (kernel_ulong_t)&ad5621_chip_info }, + { .name = "ad5641", .driver_data = (kernel_ulong_t)&ad5641_chip_info }, + { .name = "ad5620-2500", .driver_data = (kernel_ulong_t)&ad5620_2500_chip_info }, /* AD5620/40/60: */ /* part numbers may look differently */ - {"ad5620-1250", (kernel_ulong_t)&ad5620_1250_chip_info}, - {"ad5640-2500", (kernel_ulong_t)&ad5640_2500_chip_info}, - {"ad5640-1250", (kernel_ulong_t)&ad5640_1250_chip_info}, - {"ad5660-2500", (kernel_ulong_t)&ad5660_2500_chip_info}, - {"ad5660-1250", (kernel_ulong_t)&ad5660_1250_chip_info}, - {"ad5662", (kernel_ulong_t)&ad5662_chip_info}, - {"dac081s101", (kernel_ulong_t)&ad5300_chip_info}, /* compatible Texas Instruments chips */ - {"dac101s101", (kernel_ulong_t)&ad5310_chip_info}, - {"dac121s101", (kernel_ulong_t)&ad5320_chip_info}, - {"dac7512", (kernel_ulong_t)&ad5320_chip_info}, + { .name = "ad5620-1250", .driver_data = (kernel_ulong_t)&ad5620_1250_chip_info }, + { .name = "ad5640-2500", .driver_data = (kernel_ulong_t)&ad5640_2500_chip_info }, + { .name = "ad5640-1250", .driver_data = (kernel_ulong_t)&ad5640_1250_chip_info }, + { .name = "ad5660-2500", .driver_data = (kernel_ulong_t)&ad5660_2500_chip_info }, + { .name = "ad5660-1250", .driver_data = (kernel_ulong_t)&ad5660_1250_chip_info }, + { .name = "ad5662", .driver_data = (kernel_ulong_t)&ad5662_chip_info }, + { .name = "dac081s101", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, /* compatible Texas Instruments chips */ + { .name = "dac101s101", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "dac121s101", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "dac7512", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index d8c325260259..19e66bc69676 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -330,13 +330,13 @@ static void ad5449_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad5449_spi_ids[] = { - { "ad5415", ID_AD5449 }, - { "ad5426", ID_AD5426 }, - { "ad5429", ID_AD5429 }, - { "ad5432", ID_AD5432 }, - { "ad5439", ID_AD5439 }, - { "ad5443", ID_AD5443 }, - { "ad5449", ID_AD5449 }, + { .name = "ad5415", .driver_data = ID_AD5449 }, + { .name = "ad5426", .driver_data = ID_AD5426 }, + { .name = "ad5429", .driver_data = ID_AD5429 }, + { .name = "ad5432", .driver_data = ID_AD5432 }, + { .name = "ad5439", .driver_data = ID_AD5439 }, + { .name = "ad5443", .driver_data = ID_AD5443 }, + { .name = "ad5449", .driver_data = ID_AD5449 }, { } }; MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 5e586185d857..ad1a74678cf2 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -319,8 +319,8 @@ static int ad5504_probe(struct spi_device *spi) } static const struct spi_device_id ad5504_id[] = { - {"ad5504", ID_AD5504}, - {"ad5501", ID_AD5501}, + { .name = "ad5504", .driver_data = ID_AD5504 }, + { .name = "ad5501", .driver_data = ID_AD5501 }, { } }; MODULE_DEVICE_TABLE(spi, ad5504_id); diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 13aefe769bad..56aea4b1c167 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -260,12 +260,12 @@ static int ad5624r_probe(struct spi_device *spi) } static const struct spi_device_id ad5624r_id[] = { - {"ad5624r3", ID_AD5624R3}, - {"ad5644r3", ID_AD5644R3}, - {"ad5664r3", ID_AD5664R3}, - {"ad5624r5", ID_AD5624R5}, - {"ad5644r5", ID_AD5644R5}, - {"ad5664r5", ID_AD5664R5}, + { .name = "ad5624r3", .driver_data = ID_AD5624R3 }, + { .name = "ad5644r3", .driver_data = ID_AD5644R3 }, + { .name = "ad5664r3", .driver_data = ID_AD5664R3 }, + { .name = "ad5624r5", .driver_data = ID_AD5624R5 }, + { .name = "ad5644r5", .driver_data = ID_AD5644R5 }, + { .name = "ad5664r5", .driver_data = ID_AD5664R5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5624r_id); diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 8abfaf8f0c46..69cd9753f339 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -1,69 +1,109 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5672R, AD5674R, AD5676, AD5676R, AD5679R, - * AD5681R, AD5682R, AD5683, AD5683R, AD5684, - * AD5684R, AD5685R, AD5686, AD5686R - * Digital to analog converters driver + * SPI driver for AD5686 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ #include +#include #include #include +#include #include #include #include "ad5686.h" +/** + * struct ad5686_spi_data - SPI bus specific data + * @msg: SPI message used for transfers + * @size: number of transfers currently in the message + * @capacity: maximum number of transfers that can be added to the message + * @xfers: array of SPI transfers, allocated with the provided capacity + */ +struct ad5686_spi_data { + struct spi_message msg; + unsigned int size; + unsigned int capacity; + struct spi_transfer xfers[] __counted_by(capacity); +}; + static int ad5686_spi_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - struct spi_device *spi = to_spi_device(st->dev); - u8 tx_len, *buf; + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer; + if (bus_data->size >= bus_data->capacity) + return -E2BIG; + + /* + * This function stores spi transfers to a spi message to be sent over + * the bus when sync() op is called. If there are already transfers in + * the spi message, set the cs_change flag on the last transfer to + * ensure that the chip select is deasserted between transfers. If this + * is the first transfer, initialize the spi message. Later on, the + * current transfer is added to the message with spi_message_add_tail(). + */ + if (bus_data->size) + bus_data->xfers[bus_data->size - 1].cs_change = 1; + else + spi_message_init(&bus_data->msg); + + xfer = &bus_data->xfers[bus_data->size]; + auto buf = &st->data[bus_data->size]; switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) | - val); - buf = &st->data[0].d8[0]; - tx_len = 2; + buf->d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) | + FIELD_PREP(AD5310_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d16, + .len = sizeof(buf->d16), + }; break; case AD5683_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5683_DATA(val)); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5683_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; case AD5686_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; default: return -EINVAL; } - return spi_write(spi, buf, tx_len); + spi_message_add_tail(xfer, &bus_data->msg); + bus_data->size++; + + return 0; +} + +static int ad5686_spi_sync(struct ad5686_state *st) +{ + struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + + bus_data->size = 0; /* always reset, even on sync failure */ + return spi_sync(spi, &bus_data->msg); } static int ad5686_spi_read(struct ad5686_state *st, u8 addr) { - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer = &bus_data->xfers[0]; u8 cmd = 0; int ret; @@ -80,12 +120,25 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) return -EINVAL; } - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); + st->data[1].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP)); - ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); - if (ret < 0) + xfer[0] = (struct spi_transfer) { + .tx_buf = &st->data[0].d8[1], + .len = sizeof(st->data[0].d8) - 1, + .cs_change = 1, + }; + xfer[1] = (struct spi_transfer) { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = sizeof(st->data[1].d8) - 1, + }; + + spi_message_init_with_transfers(&bus_data->msg, xfer, 2); + + ret = spi_sync(spi, &bus_data->msg); + if (ret) return ret; return be32_to_cpu(st->data[2].d32); @@ -94,20 +147,42 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) static const struct ad5686_bus_ops ad5686_spi_ops = { .write = ad5686_spi_write, .read = ad5686_spi_read, + .sync = ad5686_spi_sync, }; static int ad5686_spi_probe(struct spi_device *spi) { - return ad5686_probe(&spi->dev, spi_get_device_match_data(spi), - spi->modalias, &ad5686_spi_ops); + const struct ad5686_chip_info *info; + struct ad5686_spi_data *bus_data; + struct device *dev = &spi->dev; + unsigned int capacity; + + info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; + + /* read operation requires at least 2 transfers */ + capacity = max(info->num_channels, 2); + bus_data = devm_kzalloc(dev, struct_size(bus_data, xfers, capacity), + GFP_KERNEL); + if (!bus_data) + return -ENOMEM; + + bus_data->capacity = capacity; + + return ad5686_probe(dev, info, spi->modalias, &ad5686_spi_ops, bus_data); } static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5310r", .driver_data = (kernel_ulong_t)&ad5310r_chip_info }, + { .name = "ad5313r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, + { .name = "ad5317r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5672r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, + { .name = "ad5674", .driver_data = (kernel_ulong_t)&ad5674_chip_info }, { .name = "ad5674r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, { .name = "ad5676", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5676r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, + { .name = "ad5679", .driver_data = (kernel_ulong_t)&ad5679_chip_info }, { .name = "ad5679r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5681r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, { .name = "ad5682r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info }, @@ -119,16 +194,24 @@ static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5685r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5686", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5686r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5687", .driver_data = (kernel_ulong_t)&ad5687_chip_info }, + { .name = "ad5687r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, + { .name = "ad5689", .driver_data = (kernel_ulong_t)&ad5689_chip_info }, + { .name = "ad5689r", .driver_data = (kernel_ulong_t)&ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5310r", .data = &ad5310r_chip_info }, + { .compatible = "adi,ad5313r", .data = &ad5338r_chip_info }, + { .compatible = "adi,ad5317r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5672r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5674", .data = &ad5674_chip_info }, { .compatible = "adi,ad5674r", .data = &ad5674r_chip_info }, { .compatible = "adi,ad5676", .data = &ad5676_chip_info }, { .compatible = "adi,ad5676r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5679", .data = &ad5679_chip_info }, { .compatible = "adi,ad5679r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5681r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5682r", .data = &ad5682r_chip_info }, @@ -139,6 +222,10 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5685r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5686", .data = &ad5686_chip_info }, { .compatible = "adi,ad5686r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5687", .data = &ad5687_chip_info }, + { .compatible = "adi,ad5687r", .data = &ad5687r_chip_info }, + { .compatible = "adi,ad5689", .data = &ad5689_chip_info }, + { .compatible = "adi,ad5689r", .data = &ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 5840fda4b011..0cc1c9c22a28 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5686R, AD5685R, AD5684R Digital to analog converters driver + * Core driver for AD5686 and similar Digital to Analog Converters * * Copyright 2011 Analog Devices Inc. */ @@ -8,15 +8,27 @@ #include #include #include +#include +#include +#include #include #include +#include #include +#include #include +#include #include +#include #include +#include #include +#include #include +#include +#include +#include #include "ad5686.h" @@ -31,8 +43,9 @@ static int ad5310_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5310_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static int ad5683_control_sync(struct ad5686_state *st) @@ -40,8 +53,9 @@ static int ad5683_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5683_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) @@ -173,20 +187,25 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, struct ad5686_state *st = iio_priv(indio_dev); int ret; + guard(mutex)(&st->lock); + switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&st->lock); ret = ad5686_read(st, chan->address); - mutex_unlock(&st->lock); if (ret < 0) return ret; *val = (ret >> chan->scan_type.shift) & GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = st->vref_mv; - *val2 = chan->scan_type.realbits; - return IIO_VAL_FRACTIONAL_LOG2; + if (st->double_scale) { + *val = st->scale_avail[2]; + *val2 = st->scale_avail[3]; + } else { + *val = st->scale_avail[0]; + *val2 = st->scale_avail[1]; + } + return IIO_VAL_INT_PLUS_NANO; } return -EINVAL; } @@ -198,28 +217,104 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, long mask) { struct ad5686_state *st = iio_priv(indio_dev); + bool double_scale; int ret; + guard(mutex)(&st->lock); + switch (mask) { case IIO_CHAN_INFO_RAW: if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&st->lock); - ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val << chan->scan_type.shift); - mutex_unlock(&st->lock); - break; - default: - ret = -EINVAL; - } + return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val << chan->scan_type.shift); + case IIO_CHAN_INFO_SCALE: + if (val == st->scale_avail[0] && val2 == st->scale_avail[1]) + double_scale = false; + else if (val == st->scale_avail[2] && val2 == st->scale_avail[3]) + double_scale = true; + else + return -EINVAL; - return ret; + if (st->double_scale == double_scale) + return 0; /* no change */ + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) + return -EINVAL; /* GAIN pin is board-strapped */ + + st->double_scale = double_scale; + switch (st->chip_info->regmap_type) { + case AD5310_REGMAP: + ret = ad5310_control_sync(st); + break; + case AD5683_REGMAP: + ret = ad5683_control_sync(st); + break; + case AD5686_REGMAP: + ret = gpiod_set_value_cansleep(st->gain_gpio, + st->double_scale ? 1 : 0); + break; + default: + ret = -EINVAL; + } + if (ret) + st->double_scale = !double_scale; /* revert on failure */ + return ret; + default: + return -EINVAL; + } +} + +static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad5686_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) { + /* + * GAIN pin is board-strapped, so only the current + * scale is available. + */ + *vals = st->double_scale ? &st->scale_avail[2] : + &st->scale_avail[0]; + *length = 2; + return IIO_AVAIL_LIST; + } + + *vals = st->scale_avail; + *length = ARRAY_SIZE(st->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } } static const struct iio_info ad5686_info = { .read_raw = ad5686_read_raw, .write_raw = ad5686_write_raw, + .write_raw_get_fmt = ad5686_write_raw_get_fmt, + .read_avail = ad5686_read_avail, }; static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { @@ -241,7 +336,9 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .channel = chan, \ .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),\ .address = addr, \ + .scan_index = chan, \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ @@ -312,8 +409,11 @@ DECLARE_AD5683_CHANNELS(ad5683r_channels, 16, 0); /* dual-channel */ DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); +DECLARE_AD5338_CHANNELS(ad5687r_channels, 12, 4); +DECLARE_AD5338_CHANNELS(ad5689r_channels, 16, 0); /* quad-channel */ +DECLARE_AD5686_CHANNELS(ad5317r_channels, 10, 6); DECLARE_AD5686_CHANNELS(ad5684r_channels, 12, 4); DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); DECLARE_AD5686_CHANNELS(ad5686r_channels, 16, 0); @@ -389,6 +489,44 @@ const struct ad5686_chip_info ad5338r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5338r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5687_chip_info = { + .channels = ad5687r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5687r_chip_info = { + .channels = ad5687r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689_chip_info = { + .channels = ad5689r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689r_chip_info = { + .channels = ad5689r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5317r_chip_info = { + .channels = ad5317r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5317r_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5684_chip_info = { .channels = ad5684r_channels, .num_channels = 4, @@ -450,6 +588,13 @@ const struct ad5686_chip_info ad5676r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5676r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5674_chip_info = { + .channels = ad5674r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5674_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5674r_chip_info = { .channels = ad5674r_channels, .int_vref_mv = 2500, @@ -458,6 +603,13 @@ const struct ad5686_chip_info ad5674r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5674r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5679_chip_info = { + .channels = ad5679r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5679_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5679r_chip_info = { .channels = ad5679r_channels, .int_vref_mv = 2500, @@ -466,10 +618,74 @@ const struct ad5686_chip_info ad5679r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); +static void ad5686_init_scale_avail(struct ad5686_state *st) +{ + int realbits = st->chip_info->channels[0].scan_type.realbits; + s64 tmp = 2ULL * st->vref_mv * NANO >> realbits; + + st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]); + st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]); +} + +static void do_ad5686_trigger_handler(struct iio_dev *indio_dev) +{ + struct ad5686_state *st = iio_priv(indio_dev); + u16 val[AD5686_MAX_CHANNELS] = { }; + unsigned int scan_count, ch, i; + bool async_update; + u8 cmd; + + if (iio_pop_from_buffer(indio_dev->buffer, val)) + return; + + guard(mutex)(&st->lock); + + scan_count = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + async_update = st->ldac_gpio && scan_count > 1; + if (async_update) { + /* use LDAC to update all channels simultaneously */ + cmd = AD5686_CMD_WRITE_INPUT_N; + gpiod_set_value_cansleep(st->ldac_gpio, 0); + } else { + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N; + } + + i = 0; + iio_for_each_active_channel(indio_dev, ch) { + if (st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++])) + break; + } + + /* + * If sync() is available, it is called here regardless of write + * failure to allow bus implementation to reset. In that case, partial + * writes are unlikely as the write operations would just queue up + * the transfers. + */ + if (st->ops->sync) + st->ops->sync(st); + + if (async_update) + gpiod_set_value_cansleep(st->ldac_gpio, 1); +} + +static irqreturn_t ad5686_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + do_ad5686_trigger_handler(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops) + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data) { + struct reset_control *rstc; struct ad5686_state *st; struct iio_dev *indio_dev; int ret, i; @@ -482,14 +698,56 @@ int ad5686_probe(struct device *dev, st->dev = dev; st->ops = ops; + st->bus_data = bus_data; st->chip_info = chip_info; - ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "Failed to get reset control\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); + + ret = devm_regulator_get_enable(dev, "vlogic"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vlogic supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */ + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); if (ret < 0 && ret != -ENODEV) - return ret; + return dev_err_probe(dev, ret, "failed to read vref voltage\n"); st->use_internal_vref = ret == -ENODEV; st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000; + if (!st->vref_mv) + return dev_err_probe(dev, -EINVAL, + "invalid or not provided vref voltage\n"); + + /* 4.5us power-up time: Datasheet Table 4: Timing Characteristics */ + fsleep(5); + + /* 1us >> 30ns reset pulse activation time: Datasheet Table 4 */ + reset_control_assert(rstc); + fsleep(1); + reset_control_deassert(rstc); + + st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); + if (IS_ERR(st->ldac_gpio)) + return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), + "Failed to get LDAC GPIO\n"); + + st->double_scale = device_property_read_bool(dev, "adi,range-double"); + st->gain_gpio = devm_gpiod_get_optional(dev, "gain", + st->double_scale ? GPIOD_OUT_HIGH : + GPIOD_OUT_LOW); + if (IS_ERR(st->gain_gpio)) + return dev_err_probe(dev, PTR_ERR(st->gain_gpio), + "Failed to get GAIN GPIO\n"); + + ad5686_init_scale_avail(st); /* Initialize masks to all ones */ st->pwr_down_mask = ~0; @@ -526,7 +784,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: ret = ad5686_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); + st->use_internal_vref ? 0 : AD5686_DATA_REF_MSK); if (ret) return ret; break; @@ -534,6 +792,13 @@ int ad5686_probe(struct device *dev, return -EINVAL; } + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + &ad5686_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, NULL); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index a06fe7d89305..482bc70515f7 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -14,15 +14,18 @@ #include -#define AD5310_CMD(x) ((x) << 12) +#define AD5310_CMD_MSK GENMASK(15, 12) +#define AD5310_DATA_MSK GENMASK(11, 0) -#define AD5683_DATA(x) ((x) << 4) +#define AD5683_DATA_MSK GENMASK(19, 4) -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) +#define AD5686_CMD_MSK GENMASK(23, 20) +#define AD5686_ADDR_MSK GENMASK(19, 16) +#define AD5686_DATA_MSK GENMASK(15, 0) #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) #define AD5686_ADDR_ALL_DAC 0xF +#define AD5686_MAX_CHANNELS 16 #define AD5686_CMD_NOOP 0x0 #define AD5686_CMD_WRITE_INPUT_N 0x1 @@ -38,15 +41,17 @@ #define AD5686_CMD_CONTROL_REG 0x4 #define AD5686_CMD_READBACK_ENABLE_V2 0x5 -#define AD5310_REF_BIT_MSK BIT(8) -#define AD5310_PD_MSK GENMASK(10, 9) +#define AD5310_DATA_GAIN_MSK BIT(7) +#define AD5310_DATA_REF_MSK BIT(8) +#define AD5310_DATA_PD_MSK GENMASK(10, 9) -#define AD5683_REF_BIT_MSK BIT(12) -#define AD5683_PD_MSK GENMASK(14, 13) +#define AD5683_DATA_GAIN_MSK BIT(11) /* DB15 */ +#define AD5683_DATA_REF_MSK BIT(12) /* DB16 */ +#define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */ + +#define AD5686_DATA_REF_MSK BIT(0) -#define AD5686_REF_BIT_MSK BIT(0) #define AD5686_PD_MSK GENMASK(1, 0) - #define AD5686_PD_MODE_1K_TO_GND 0x1 #define AD5686_PD_MODE_100K_TO_GND 0x2 #define AD5686_PD_MODE_THREE_STATE 0x3 @@ -60,16 +65,20 @@ enum ad5686_regmap_type { AD5686_REGMAP, }; +struct gpio_desc; + struct ad5686_state; /** * struct ad5686_bus_ops - bus specific read/write operations * @read: read a register value at the given address * @write: write a command, address and value to the device + * @sync: ensure the completion of the write operation (optional) */ struct ad5686_bus_ops { int (*read)(struct ad5686_state *st, u8 addr); int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val); + int (*sync)(struct ad5686_state *st); }; /** @@ -97,8 +106,13 @@ extern const struct ad5686_chip_info ad5683r_chip_info; /* dual-channel instances */ extern const struct ad5686_chip_info ad5337r_chip_info; extern const struct ad5686_chip_info ad5338r_chip_info; +extern const struct ad5686_chip_info ad5687_chip_info; +extern const struct ad5686_chip_info ad5687r_chip_info; +extern const struct ad5686_chip_info ad5689_chip_info; +extern const struct ad5686_chip_info ad5689r_chip_info; /* quad-channel instances */ +extern const struct ad5686_chip_info ad5317r_chip_info; extern const struct ad5686_chip_info ad5684_chip_info; extern const struct ad5686_chip_info ad5684r_chip_info; extern const struct ad5686_chip_info ad5685r_chip_info; @@ -111,7 +125,9 @@ extern const struct ad5686_chip_info ad5676_chip_info; extern const struct ad5686_chip_info ad5676r_chip_info; /* 16-channel instances */ +extern const struct ad5686_chip_info ad5674_chip_info; extern const struct ad5686_chip_info ad5674r_chip_info; +extern const struct ad5686_chip_info ad5679_chip_info; extern const struct ad5686_chip_info ad5679r_chip_info; /** @@ -119,23 +135,33 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * @dev: device instance * @chip_info: chip model specific constants, available modes etc * @ops: bus specific operations - * @vref_mv: actual reference voltage used + * @ldac_gpio: LDAC pin GPIO descriptor + * @gain_gpio: GAIN pin GPIO descriptor * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode + * @scale_avail: pre-calculated available scale values + * @vref_mv: actual reference voltage used + * @double_scale: flag to indicate the gain multiplier is applied * @use_internal_vref: set to true if the internal reference voltage is used * @lock: lock to protect access to state fields, which includes * the data buffer during regmap ops + * @bus_data: bus specific data * @data: transfer buffers */ struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; const struct ad5686_bus_ops *ops; - unsigned short vref_mv; + struct gpio_desc *ldac_gpio; + struct gpio_desc *gain_gpio; unsigned int pwr_down_mask; unsigned int pwr_down_mode; + int scale_avail[4]; + unsigned short vref_mv; + bool double_scale; bool use_internal_vref; struct mutex lock; + void *bus_data; /* * DMA (thus cache coherency maintenance) may require the @@ -146,17 +172,24 @@ struct ad5686_state { __be32 d32; __be16 d16; u8 d8[4]; - } data[3] __aligned(IIO_DMA_MINALIGN); + } data[AD5686_MAX_CHANNELS] __aligned(IIO_DMA_MINALIGN); }; int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops); + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data); static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - return st->ops->write(st, cmd, addr, val); + int ret; + + ret = st->ops->write(st, cmd, addr, val); + if (ret) + return ret; + + return st->ops->sync ? st->ops->sync(st) : 0; } static inline int ad5686_read(struct ad5686_state *st, u8 addr) diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index d49946adbde3..f91abfcdbada 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R - * Digital to analog converters driver + * I2C driver for AD5696 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ +#include #include #include #include @@ -34,9 +33,8 @@ static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) }; int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | - AD5686_ADDR(addr) | - 0x00); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); ret = i2c_transfer(i2c->adapter, msg, 2); if (ret < 0) @@ -51,8 +49,9 @@ static int ad5686_i2c_write(struct ad5686_state *st, struct i2c_client *i2c = to_i2c_client(st->dev); int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) - | val); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); if (ret < 0) @@ -68,16 +67,23 @@ static const struct ad5686_bus_ops ad5686_i2c_ops = { static int ad5686_i2c_probe(struct i2c_client *i2c) { - return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c), - i2c->name, &ad5686_i2c_ops); + const struct ad5686_chip_info *info; + + info = i2c_get_match_data(i2c); + if (!info) + return -ENODATA; + + return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops, NULL); } static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5311r", .driver_data = (kernel_ulong_t)&ad5311r_chip_info }, + { .name = "ad5316r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5337r", .driver_data = (kernel_ulong_t)&ad5337r_chip_info }, { .name = "ad5338r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, { .name = "ad5671r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, { .name = "ad5673r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, + { .name = "ad5675", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5675r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, { .name = "ad5677r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5691r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, @@ -89,16 +95,21 @@ static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5695r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5696", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5696r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5697r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5311r", .data = &ad5311r_chip_info }, + { .compatible = "adi,ad5316r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5337r", .data = &ad5337r_chip_info }, { .compatible = "adi,ad5338r", .data = &ad5338r_chip_info }, { .compatible = "adi,ad5671r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5673r", .data = &ad5674r_chip_info }, + { .compatible = "adi,ad5675", .data = &ad5676_chip_info }, { .compatible = "adi,ad5675r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5677r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5691r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5692r", .data = &ad5682r_chip_info }, { .compatible = "adi,ad5693", .data = &ad5683_chip_info }, @@ -108,6 +119,7 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5695r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5696", .data = &ad5686_chip_info }, { .compatible = "adi,ad5696r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5697r", .data = &ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c index e4e48ca7ad34..06c4860a7eba 100644 --- a/drivers/iio/dac/ad5706r.c +++ b/drivers/iio/dac/ad5706r.c @@ -232,7 +232,7 @@ static const struct of_device_id ad5706r_of_match[] = { MODULE_DEVICE_TABLE(of, ad5706r_of_match); static const struct spi_device_id ad5706r_id[] = { - { "ad5706r" }, + { .name = "ad5706r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5706r_id); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index cc6d56140d66..0828b9909099 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -849,11 +849,11 @@ static int ad5755_probe(struct spi_device *spi) } static const struct spi_device_id ad5755_id[] = { - { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, - { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, - { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, + { .name = "ad5755", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5755-1", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5757", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, + { .name = "ad5735", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, + { .name = "ad5737", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, { } }; MODULE_DEVICE_TABLE(spi, ad5755_id); diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index bb30842e7080..44efa373cd80 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -878,7 +878,7 @@ static int ad5758_probe(struct spi_device *spi) } static const struct spi_device_id ad5758_id[] = { - { "ad5758", 0 }, + { .name = "ad5758" }, { } }; MODULE_DEVICE_TABLE(spi, ad5758_id); diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index b5d20f04f070..c49568b943d6 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -342,10 +342,10 @@ static int ad5761_probe(struct spi_device *spi) } static const struct spi_device_id ad5761_id[] = { - {"ad5721", ID_AD5721}, - {"ad5721r", ID_AD5721R}, - {"ad5761", ID_AD5761}, - {"ad5761r", ID_AD5761R}, + { .name = "ad5721", .driver_data = ID_AD5721 }, + { .name = "ad5721r", .driver_data = ID_AD5721R }, + { .name = "ad5761", .driver_data = ID_AD5761 }, + { .name = "ad5761r", .driver_data = ID_AD5761R }, { } }; MODULE_DEVICE_TABLE(spi, ad5761_id); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index fbbd7105a80c..2be4bee7eb01 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -342,10 +342,10 @@ static void ad5764_remove(struct spi_device *spi) } static const struct spi_device_id ad5764_ids[] = { - { "ad5744", ID_AD5744 }, - { "ad5744r", ID_AD5744R }, - { "ad5764", ID_AD5764 }, - { "ad5764r", ID_AD5764R }, + { .name = "ad5744", .driver_data = ID_AD5744 }, + { .name = "ad5744r", .driver_data = ID_AD5744R }, + { .name = "ad5764", .driver_data = ID_AD5764 }, + { .name = "ad5764r", .driver_data = ID_AD5764R }, { } }; MODULE_DEVICE_TABLE(spi, ad5764_ids); diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index f6a0a0d84fef..061b43fc0345 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -651,8 +651,8 @@ static const struct of_device_id ad5766_dt_match[] = { MODULE_DEVICE_TABLE(of, ad5766_dt_match); static const struct spi_device_id ad5766_spi_ids[] = { - { "ad5766", ID_AD5766 }, - { "ad5767", ID_AD5767 }, + { .name = "ad5766", .driver_data = ID_AD5766 }, + { .name = "ad5767", .driver_data = ID_AD5767 }, { } }; MODULE_DEVICE_TABLE(spi, ad5766_spi_ids); diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 6027e8d88b27..bdc48e57013f 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -642,7 +642,7 @@ static const struct of_device_id ad5770r_of_id[] = { MODULE_DEVICE_TABLE(of, ad5770r_of_id); static const struct spi_device_id ad5770r_id[] = { - { "ad5770r", 0 }, + { .name = "ad5770r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5770r_id); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index ae7297f08398..a5eda135c9bd 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -586,11 +586,11 @@ static const struct of_device_id ad5791_of_match[] = { MODULE_DEVICE_TABLE(of, ad5791_of_match); static const struct spi_device_id ad5791_id[] = { - { "ad5760", (kernel_ulong_t)&ad5760_chip_info }, - { "ad5780", (kernel_ulong_t)&ad5780_chip_info }, - { "ad5781", (kernel_ulong_t)&ad5781_chip_info }, - { "ad5790", (kernel_ulong_t)&ad5790_chip_info }, - { "ad5791", (kernel_ulong_t)&ad5791_chip_info }, + { .name = "ad5760", .driver_data = (kernel_ulong_t)&ad5760_chip_info }, + { .name = "ad5780", .driver_data = (kernel_ulong_t)&ad5780_chip_info }, + { .name = "ad5781", .driver_data = (kernel_ulong_t)&ad5781_chip_info }, + { .name = "ad5790", .driver_data = (kernel_ulong_t)&ad5790_chip_info }, + { .name = "ad5791", .driver_data = (kernel_ulong_t)&ad5791_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5791_id); diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index 03acf7c114b0..33a49813ece0 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -869,7 +869,7 @@ static int ad7293_probe(struct spi_device *spi) } static const struct spi_device_id ad7293_id[] = { - { "ad7293", 0 }, + { .name = "ad7293" }, { } }; MODULE_DEVICE_TABLE(spi, ad7293_id); diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 6451fc586a67..83153e7d7fc8 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -270,7 +270,7 @@ static const struct of_device_id ad7303_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); static const struct spi_device_id ad7303_spi_ids[] = { - { "ad7303", 0 }, + { .name = "ad7303" }, { } }; MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index ddec62b6e57b..a2292810e4cf 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -954,3 +954,4 @@ MODULE_AUTHOR("Mariel Tinaco "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index 6e80b49f4665..90ffb58e7927 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -224,8 +224,8 @@ static const struct of_device_id ltc1660_dt_ids[] = { MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); static const struct spi_device_id ltc1660_id[] = { - {"ltc1660", ID_LTC1660}, - {"ltc1665", ID_LTC1665}, + { .name = "ltc1660", .driver_data = ID_LTC1660 }, + { .name = "ltc1665", .driver_data = ID_LTC1665 }, { } }; MODULE_DEVICE_TABLE(spi, ltc1660_id); diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index d6a3d290e7a8..3800aee8dd6e 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -362,28 +362,28 @@ static int ltc2632_probe(struct spi_device *spi) } static const struct spi_device_id ltc2632_id[] = { - { "ltc2632-l12", (kernel_ulong_t)<c2632l12_chip_info }, - { "ltc2632-l10", (kernel_ulong_t)<c2632l10_chip_info }, - { "ltc2632-l8", (kernel_ulong_t)<c2632l8_chip_info }, - { "ltc2632-h12", (kernel_ulong_t)<c2632h12_chip_info }, - { "ltc2632-h10", (kernel_ulong_t)<c2632h10_chip_info }, - { "ltc2632-h8", (kernel_ulong_t)<c2632h8_chip_info }, - { "ltc2634-l12", (kernel_ulong_t)<c2634l12_chip_info }, - { "ltc2634-l10", (kernel_ulong_t)<c2634l10_chip_info }, - { "ltc2634-l8", (kernel_ulong_t)<c2634l8_chip_info }, - { "ltc2634-h12", (kernel_ulong_t)<c2634h12_chip_info }, - { "ltc2634-h10", (kernel_ulong_t)<c2634h10_chip_info }, - { "ltc2634-h8", (kernel_ulong_t)<c2634h8_chip_info }, - { "ltc2636-l12", (kernel_ulong_t)<c2636l12_chip_info }, - { "ltc2636-l10", (kernel_ulong_t)<c2636l10_chip_info }, - { "ltc2636-l8", (kernel_ulong_t)<c2636l8_chip_info }, - { "ltc2636-h12", (kernel_ulong_t)<c2636h12_chip_info }, - { "ltc2636-h10", (kernel_ulong_t)<c2636h10_chip_info }, - { "ltc2636-h8", (kernel_ulong_t)<c2636h8_chip_info }, - { "ltc2654-l16", (kernel_ulong_t)<c2654l16_chip_info }, - { "ltc2654-l12", (kernel_ulong_t)<c2634l12_chip_info }, - { "ltc2654-h16", (kernel_ulong_t)<c2654h16_chip_info }, - { "ltc2654-h12", (kernel_ulong_t)<c2634h12_chip_info }, + { .name = "ltc2632-l12", .driver_data = (kernel_ulong_t)<c2632l12_chip_info }, + { .name = "ltc2632-l10", .driver_data = (kernel_ulong_t)<c2632l10_chip_info }, + { .name = "ltc2632-l8", .driver_data = (kernel_ulong_t)<c2632l8_chip_info }, + { .name = "ltc2632-h12", .driver_data = (kernel_ulong_t)<c2632h12_chip_info }, + { .name = "ltc2632-h10", .driver_data = (kernel_ulong_t)<c2632h10_chip_info }, + { .name = "ltc2632-h8", .driver_data = (kernel_ulong_t)<c2632h8_chip_info }, + { .name = "ltc2634-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2634-l10", .driver_data = (kernel_ulong_t)<c2634l10_chip_info }, + { .name = "ltc2634-l8", .driver_data = (kernel_ulong_t)<c2634l8_chip_info }, + { .name = "ltc2634-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, + { .name = "ltc2634-h10", .driver_data = (kernel_ulong_t)<c2634h10_chip_info }, + { .name = "ltc2634-h8", .driver_data = (kernel_ulong_t)<c2634h8_chip_info }, + { .name = "ltc2636-l12", .driver_data = (kernel_ulong_t)<c2636l12_chip_info }, + { .name = "ltc2636-l10", .driver_data = (kernel_ulong_t)<c2636l10_chip_info }, + { .name = "ltc2636-l8", .driver_data = (kernel_ulong_t)<c2636l8_chip_info }, + { .name = "ltc2636-h12", .driver_data = (kernel_ulong_t)<c2636h12_chip_info }, + { .name = "ltc2636-h10", .driver_data = (kernel_ulong_t)<c2636h10_chip_info }, + { .name = "ltc2636-h8", .driver_data = (kernel_ulong_t)<c2636h8_chip_info }, + { .name = "ltc2654-l16", .driver_data = (kernel_ulong_t)<c2654l16_chip_info }, + { .name = "ltc2654-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2654-h16", .driver_data = (kernel_ulong_t)<c2654h16_chip_info }, + { .name = "ltc2654-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c index c48b9efc8280..35bec55ebde1 100644 --- a/drivers/iio/dac/ltc2664.c +++ b/drivers/iio/dac/ltc2664.c @@ -708,8 +708,8 @@ static int ltc2664_probe(struct spi_device *spi) } static const struct spi_device_id ltc2664_id[] = { - { "ltc2664", (kernel_ulong_t)<c2664_chip }, - { "ltc2672", (kernel_ulong_t)<c2672_chip }, + { .name = "ltc2664", .driver_data = (kernel_ulong_t)<c2664_chip }, + { .name = "ltc2672", .driver_data = (kernel_ulong_t)<c2672_chip }, { } }; MODULE_DEVICE_TABLE(spi, ltc2664_id); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index a575ef8371dc..0301bb7dce31 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -991,7 +991,7 @@ static const struct of_device_id ltc2688_of_id[] = { MODULE_DEVICE_TABLE(of, ltc2688_of_id); static const struct spi_device_id ltc2688_id[] = { - { "ltc2688" }, + { .name = "ltc2688" }, { } }; MODULE_DEVICE_TABLE(spi, ltc2688_id); diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 7e80c0eb5cc1..60bb672b70e2 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -32,6 +32,7 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) { struct m62332_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; + bool enabling, disabling; u8 outbuf[2]; int res; @@ -43,7 +44,10 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) mutex_lock(&data->mutex); - if (val) { + enabling = val && !data->raw[channel]; + disabling = !val && data->raw[channel]; + + if (enabling) { res = regulator_enable(data->vcc); if (res) goto out; @@ -52,14 +56,17 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf)); if (res >= 0 && res != ARRAY_SIZE(outbuf)) res = -EIO; - if (res < 0) + if (res < 0) { + if (enabling) + regulator_disable(data->vcc); goto out; + } + + if (disabling) + regulator_disable(data->vcc); data->raw[channel] = val; - if (!val) - regulator_disable(data->vcc); - mutex_unlock(&data->mutex); return 0; diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c index d747901df5a3..e2c7e2ad37ba 100644 --- a/drivers/iio/dac/max22007.c +++ b/drivers/iio/dac/max22007.c @@ -464,7 +464,7 @@ static int max22007_probe(struct spi_device *spi) } static const struct spi_device_id max22007_id[] = { - { "max22007" }, + { .name = "max22007" }, { } }; MODULE_DEVICE_TABLE(spi, max22007_id); diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 1459ba132df6..f3415748c765 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -24,15 +24,8 @@ #define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A) -struct max5522_chip_info { - const char *name; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct max5522_state { struct regmap *regmap; - const struct max5522_chip_info *chip_info; unsigned short dac_cache[2]; int vref_mV; }; @@ -57,18 +50,6 @@ static const struct iio_chan_spec max5522_channels[] = { MAX5522_CHANNEL(1), }; -enum max5522_type { - ID_MAX5522, -}; - -static const struct max5522_chip_info max5522_chip_info_tbl[] = { - [ID_MAX5522] = { - .name = "max5522", - .channels = max5522_channels, - .num_channels = 2, - }, -}; - static inline int max5522_info_to_reg(struct iio_chan_spec const *chan) { return MAX5522_REG_DATA(chan->channel); @@ -139,9 +120,6 @@ static int max5522_spi_probe(struct spi_device *spi) } state = iio_priv(indio_dev); - state->chip_info = spi_get_device_match_data(spi); - if (!state->chip_info) - return -EINVAL; ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin"); if (ret < 0) @@ -158,22 +136,19 @@ static int max5522_spi_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max5522_channels; indio_dev->num_channels = ARRAY_SIZE(max5522_channels); - indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name; + indio_dev->name = "max5522"; return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id max5522_ids[] = { - { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, + { .name = "max5522" }, { } }; MODULE_DEVICE_TABLE(spi, max5522_ids); static const struct of_device_id max5522_of_match[] = { - { - .compatible = "maxim,max5522", - .data = &max5522_chip_info_tbl[ID_MAX5522], - }, + { .compatible = "maxim,max5522" }, { } }; MODULE_DEVICE_TABLE(of, max5522_of_match); diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c new file mode 100644 index 000000000000..e2c12241a534 --- /dev/null +++ b/drivers/iio/dac/mcf54415_dac.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP mcf54415 DAC driver + * + * Copyright 2026 BayLibre - adureghello@baylibre.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MCF54415_DAC_CR 0x00 +#define MCF54415_DAC_CR_PDN BIT(0) +#define MCF54415_DAC_CR_HSLS BIT(6) +#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8) +#define MCF54415_DAC_CR_FILT BIT(12) + +#define MCF54415_DAC_DATA 0x02 + +struct mcf54415_dac { + struct regmap *map; + struct clk *clk; +}; + +static const struct regmap_config mcf54415_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */ + .val_format_endian = REGMAP_ENDIAN_BIG, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int mcf54415_dac_init(struct mcf54415_dac *info) +{ + u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1); + int ret; + + /* Fixed defaults and enable DAC (bit 0 set to 0) */ + ret = regmap_write(info->map, MCF54415_DAC_CR, val); + if (ret) + return ret; + + /* DAC is ready after 12us, from RM table 40-3 */ + fsleep(12); + + return 0; +} + +static void mcf54415_dac_exit(void *data) +{ + struct mcf54415_dac *info = data; + + regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN); +} + +static const struct iio_chan_spec mcf54415_dac_iio_channel = { + .type = IIO_VOLTAGE, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcf54415_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + unsigned int reg; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->map, MCF54415_DAC_DATA, ®); + if (ret) + return ret; + *val = reg & GENMASK(11, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Reference voltage as per ColdFire datasheet is 3.3V */ + *val = 3300 /* mV */; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcf54415_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */ + if (val < 0 || val > 4095) + return -EINVAL; + return regmap_write(info->map, MCF54415_DAC_DATA, val); + default: + return -EINVAL; + } +} + +static const struct iio_info mcf54415_dac_iio_info = { + .read_raw = &mcf54415_read_raw, + .write_raw = &mcf54415_write_raw, +}; + +static int mcf54415_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct mcf54415_dac *info; + void __iomem *regs; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n"); + + info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config); + if (IS_ERR(info->map)) + return PTR_ERR(info->map); + + info->clk = devm_clk_get_enabled(dev, "dac"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n"); + + indio_dev->name = "mcf54415"; + indio_dev->info = &mcf54415_dac_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &mcf54415_dac_iio_channel; + indio_dev->num_channels = 1; + + ret = mcf54415_dac_init(info); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id mcf54415_dac_ids[] = { + { .name = "mcfdac" }, + { } +}; +MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids); + +static struct platform_driver mcf54415_dac_driver = { + .driver = { + .name = "mcf54415_dac", + }, + .probe = mcf54415_dac_probe, + .id_table = mcf54415_dac_ids, +}; +module_platform_driver(mcf54415_dac_driver); + +MODULE_AUTHOR("Angelo Dureghello "); +MODULE_DESCRIPTION("NXP MCF54415 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/mcp47a1.c b/drivers/iio/dac/mcp47a1.c new file mode 100644 index 000000000000..0bf994aa0e4f --- /dev/null +++ b/drivers/iio/dac/mcp47a1.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip MCP47A1 DAC driver + * + * Copyright (c) 2026 Joshua Crofts + * + * Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MCP47A1_CMD_CODE 0x00 +#define MCP47A1_MAX_STEPS 64 + +struct mcp47a1_data { + struct i2c_client *client; + int vref_mV; +}; + +static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 }; + +static const struct iio_chan_spec mcp47a1_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcp47a1_write(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!in_range(val, 0, MCP47A1_MAX_STEPS)) + return -EINVAL; + + return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE, + val); + default: + return -EINVAL; + } +} + +static int mcp47a1_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(data->client, MCP47A1_CMD_CODE); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = data->vref_mV; + *val2 = MCP47A1_MAX_STEPS; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int mcp47a1_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_RAW: + *vals = mcp47a1_raw_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_info mcp47a1_info = { + .write_raw = mcp47a1_write, + .read_raw = mcp47a1_read, + .read_avail = mcp47a1_read_avail, +}; + +static int mcp47a1_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mcp47a1_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + /* Delay after device exits reset state (see AC/DC characteristics) */ + fsleep(20); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read vref\n"); + + data->vref_mV = ret / (MICRO / MILLI); + + indio_dev->name = "mcp47a1"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp47a1_info; + indio_dev->channels = &mcp47a1_channel; + indio_dev->num_channels = 1; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mcp47a1_of_match[] = { + { .compatible = "microchip,mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp47a1_of_match); + +static const struct i2c_device_id mcp47a1_id[] = { + { .name = "mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp47a1_id); + +static struct i2c_driver mcp47a1_driver = { + .driver = { + .name = "mcp47a1", + .of_match_table = mcp47a1_of_match, + }, + .probe = mcp47a1_probe, + .id_table = mcp47a1_id, +}; +module_i2c_driver(mcp47a1_driver); + +MODULE_AUTHOR("Joshua Crofts "); +MODULE_DESCRIPTION("Microchip MCP47A1 DAC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index d2e7c930c848..8d8c00cd60e9 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -297,12 +297,12 @@ static const struct of_device_id mcp4821_of_table[] = { MODULE_DEVICE_TABLE(of, mcp4821_of_table); static const struct spi_device_id mcp4821_id_table[] = { - { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, - { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, - { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, - { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, - { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, - { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, + { .name = "mcp4801", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801] }, + { .name = "mcp4802", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802] }, + { .name = "mcp4811", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811] }, + { .name = "mcp4812", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812] }, + { .name = "mcp4821", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821] }, + { .name = "mcp4822", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822] }, { } }; MODULE_DEVICE_TABLE(spi, mcp4821_id_table); diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index 74f338afcab9..15072a9081ff 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -157,10 +157,10 @@ static int mcp4922_probe(struct spi_device *spi) static const struct spi_device_id mcp4922_id[] = { - {"mcp4902", ID_MCP4902}, - {"mcp4912", ID_MCP4912}, - {"mcp4921", ID_MCP4921}, - {"mcp4922", ID_MCP4922}, + { .name = "mcp4902", .driver_data = ID_MCP4902 }, + { .name = "mcp4912", .driver_data = ID_MCP4912 }, + { .name = "mcp4921", .driver_data = ID_MCP4921 }, + { .name = "mcp4922", .driver_data = ID_MCP4922 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4922_id); diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c index e91090e4a66d..6e2e7787f8a4 100644 --- a/drivers/iio/dac/rohm-bd79703.c +++ b/drivers/iio/dac/rohm-bd79703.c @@ -214,10 +214,10 @@ static int bd79703_probe(struct spi_device *spi) } static const struct spi_device_id bd79703_id[] = { - { "bd79700", (kernel_ulong_t)&bd79700_chip_data }, - { "bd79701", (kernel_ulong_t)&bd79701_chip_data }, - { "bd79702", (kernel_ulong_t)&bd79702_chip_data }, - { "bd79703", (kernel_ulong_t)&bd79703_chip_data }, + { .name = "bd79700", .driver_data = (kernel_ulong_t)&bd79700_chip_data }, + { .name = "bd79701", .driver_data = (kernel_ulong_t)&bd79701_chip_data }, + { .name = "bd79702", .driver_data = (kernel_ulong_t)&bd79702_chip_data }, + { .name = "bd79703", .driver_data = (kernel_ulong_t)&bd79703_chip_data }, { } }; MODULE_DEVICE_TABLE(spi, bd79703_id); diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 6e62c1f302c8..afa7d9bad5de 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -334,12 +334,12 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac082s085", dual_8bit }, - { "dac102s085", dual_10bit }, - { "dac122s085", dual_12bit }, - { "dac084s085", quad_8bit }, - { "dac104s085", quad_10bit }, - { "dac124s085", quad_12bit }, + { .name = "dac082s085", .driver_data = dual_8bit }, + { .name = "dac102s085", .driver_data = dual_10bit }, + { .name = "dac122s085", .driver_data = dual_12bit }, + { .name = "dac084s085", .driver_data = quad_8bit }, + { .name = "dac104s085", .driver_data = quad_10bit }, + { .name = "dac124s085", .driver_data = quad_12bit }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 5c1c5213962f..e5843bd35259 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -311,9 +311,9 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac5311", ID_DAC5311 }, - { "dac6311", ID_DAC6311 }, - { "dac7311", ID_DAC7311 }, + { .name = "dac5311", .driver_data = ID_DAC5311 }, + { .name = "dac6311", .driver_data = ID_DAC6311 }, + { .name = "dac7311", .driver_data = ID_DAC7311 }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c index c308eca02b88..9065663cd609 100644 --- a/drivers/iio/dac/ti-dac7612.c +++ b/drivers/iio/dac/ti-dac7612.c @@ -165,7 +165,7 @@ static int dac7612_probe(struct spi_device *spi) } static const struct spi_device_id dac7612_id[] = { - {"ti-dac7612"}, + { .name = "ti-dac7612" }, { } }; MODULE_DEVICE_TABLE(spi, dac7612_id); diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index 8bcd298e1f3d..b88689e60cc8 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -789,7 +789,7 @@ static int admv8818_probe(struct spi_device *spi) } static const struct spi_device_id admv8818_id[] = { - { "admv8818", 0 }, + { .name = "admv8818" }, { } }; MODULE_DEVICE_TABLE(spi, admv8818_id); diff --git a/drivers/iio/flow/Kconfig b/drivers/iio/flow/Kconfig new file mode 100644 index 000000000000..e0e1a8e3654a --- /dev/null +++ b/drivers/iio/flow/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Liquid / gas flow sensor drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Flow sensors" + +config SENSIRION_SLF3S + tristate "Sensirion SLF3S liquid flow sensor" + depends on I2C + select CRC8 + help + Say yes here to build support for the Sensirion SLF3S family of + digital liquid-flow sensors (SLF3S-0600F, SLF3S-1300F and + SLF3S-4000B). The driver reports the volumetric flow rate and the + embedded temperature reading via the standard IIO interface. + + To compile this driver as a module, choose M here: the module + will be called slf3s. + +endmenu diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile new file mode 100644 index 000000000000..3cf4ab95c69c --- /dev/null +++ b/drivers/iio/flow/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O flow sensor drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_SENSIRION_SLF3S) += slf3s.o diff --git a/drivers/iio/flow/slf3s.c b/drivers/iio/flow/slf3s.c new file mode 100644 index 000000000000..dfa7c1409045 --- /dev/null +++ b/drivers/iio/flow/slf3s.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SLF3S liquid flow sensor driver. + * + * Supports the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B liquid-flow + * sensors over I2C. Each measurement frame returns a 16-bit signed + * flow value, a 16-bit signed temperature value and a status word, + * each protected by a CRC-8 byte. + * + * The active calibration medium (water or isopropyl alcohol) is + * runtime-switchable via the in_volumeflow_medium sysfs attribute and + * defaults to water. + * + * Datasheet: https://sensirion.com/products/catalog/SLF3S-0600F/ + * + * Copyright (C) 2026 CMBlu Energy GmbH + * Author: Wadim Mueller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SLF3S_CRC8_POLY 0x31 +#define SLF3S_CRC8_INIT 0xff + +#define SLF3S_PRODUCT_ID_LEN 18 +#define SLF3S_PRODUCT_FAMILY_BYTE 1 +#define SLF3S_PRODUCT_SUBTYPE_BYTE 3 +#define SLF3S_PRODUCT_FAMILY_ID 0x03 + +/* Datasheet section 2.2: tPU = 25 ms max from power-on to first cmd. */ +#define SLF3S_POWER_UP_DELAY_US (25 * USEC_PER_MSEC) +/* Datasheet section 2.2: tw = 60 ms typical until first valid sample. */ +#define SLF3S_MEAS_START_DELAY_US (60 * USEC_PER_MSEC) + +static const u8 slf3s_cmd_prep_pid[] = { 0x36, 0x7c }; +static const u8 slf3s_cmd_read_pid[] = { 0xe1, 0x02 }; +static const u8 slf3s_cmd_start_water[] = { 0x36, 0x08 }; +static const u8 slf3s_cmd_start_ipa[] = { 0x36, 0x15 }; +static const u8 slf3s_cmd_stop_meas[] = { 0x3f, 0xf9 }; + +enum slf3s_medium { + SLF3S_MEDIUM_WATER, + SLF3S_MEDIUM_IPA, +}; + +static const char * const slf3s_medium_modes[] = { + [SLF3S_MEDIUM_WATER] = "water", + [SLF3S_MEDIUM_IPA] = "ipa", +}; + +enum slf3s_variant_id { + SLF3S_0600F, + SLF3S_1300F, + SLF3S_4000B, +}; + +/** + * struct slf3s_variant - per-variant calibration constants + * @sub_type: product-info sub-type byte returned by the sensor + * @name: name reported via @iio_dev.name + * @scale: flow scale in l/s per LSB + */ +struct slf3s_variant { + u8 sub_type; + const char *name; + struct s32_fract scale; +}; + +static const struct slf3s_variant slf3s_variants[] = { + [SLF3S_0600F] = { + .sub_type = 0x03, + .name = "slf3s-0600f", + .scale = { .numerator = 1, .denominator = 600 * MICRO }, + }, + [SLF3S_1300F] = { + .sub_type = 0x02, + .name = "slf3s-1300f", + .scale = { .numerator = 1, .denominator = 30 * MICRO }, + }, + [SLF3S_4000B] = { + .sub_type = 0x05, + .name = "slf3s-4000b", + .scale = { .numerator = 1, .denominator = 1920 * MILLI }, + }, +}; + +/** + * struct slf3s_data - per-device state + * @client: I2C client this instance is bound to + * @vdd: supply regulator, disabled while suspended + * @variant: pointer into @slf3s_variants for the detected device + * @medium: currently active calibration medium + * @lock: serialises the multi-step command/response exchanges + * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY + */ +struct slf3s_data { + struct i2c_client *client; + struct regulator *vdd; + const struct slf3s_variant *variant; + enum slf3s_medium medium; + struct mutex lock; + u8 crc_table[CRC8_TABLE_SIZE]; +}; + +static int slf3s_send_cmd(struct i2c_client *client, const u8 *cmd) +{ + int ret; + + ret = i2c_master_send(client, cmd, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + return 0; +} + +/* Start continuous measurement and wait until the first sample is valid. */ +static int slf3s_start_meas(struct slf3s_data *sf, enum slf3s_medium medium) +{ + const u8 *cmd = (medium == SLF3S_MEDIUM_IPA) ? slf3s_cmd_start_ipa + : slf3s_cmd_start_water; + int ret; + + ret = slf3s_send_cmd(sf->client, cmd); + if (ret) + return ret; + + fsleep(SLF3S_MEAS_START_DELAY_US); + + return 0; +} + +static bool slf3s_crc_valid(const struct slf3s_data *sf, const u8 *block) +{ + return crc8(sf->crc_table, block, 2, SLF3S_CRC8_INIT) == block[2]; +} + +/* + * Read the product-info block and pick the matching variant. The + * sub-type byte returned by the sensor is the source of truth; a + * DT-supplied compatible only seeds an initial guess and is overridden + * on mismatch (with an informational message so misconfigured device + * trees are easy to spot). + * + * Bus / CRC failures are real errors and fail probe. An unknown + * sub-type byte falls back to the variant named in the device tree / + * I2C table, so a drop-in replacement part that lists one of the known + * compatibles keeps working on an older kernel that does not know its + * sub-type yet. Without any match data probe fails since no + * meaningful scale can be published. + */ +static int slf3s_detect_variant(struct slf3s_data *sf) +{ + struct i2c_client *client = sf->client; + u8 buf[SLF3S_PRODUCT_ID_LEN]; + int ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_prep_pid); + if (ret) + return ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_read_pid); + if (ret) + return ret; + + ret = i2c_master_recv(client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < SLF3S_PRODUCT_ID_LEN; i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + if (buf[SLF3S_PRODUCT_FAMILY_BYTE] != SLF3S_PRODUCT_FAMILY_ID) + dev_info(&client->dev, + "unexpected family byte 0x%02x (expected 0x%02x)\n", + buf[SLF3S_PRODUCT_FAMILY_BYTE], + SLF3S_PRODUCT_FAMILY_ID); + + for (unsigned int i = 0; i < ARRAY_SIZE(slf3s_variants); i++) { + if (buf[SLF3S_PRODUCT_SUBTYPE_BYTE] != + slf3s_variants[i].sub_type) + continue; + + if (sf->variant && sf->variant != &slf3s_variants[i]) + dev_info(&client->dev, + "DT compatible says %s but sensor reports %s; using the latter\n", + sf->variant->name, + slf3s_variants[i].name); + + sf->variant = &slf3s_variants[i]; + + return 0; + } + + if (sf->variant) { + dev_warn(&client->dev, + "unknown SLF3S sub-type 0x%02x, assuming %s\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE], sf->variant->name); + return 0; + } + + dev_err(&client->dev, "unknown SLF3S sub-type 0x%02x\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE]); + + return -ENODEV; +} + +static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp) +{ + /* + * A measurement frame is flow, temperature and a signaling-flags + * word, each followed by a CRC byte. Only flow and temperature are + * used, so the read is stopped after their two words (6 bytes). + */ + u8 buf[6]; + int ret; + + ret = i2c_master_recv(sf->client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < sizeof(buf); i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15); + *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15); + + return 0; +} + +static int slf3s_get_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + + return sf->medium; +} + +static int slf3s_set_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + ret = slf3s_start_meas(sf, mode); + if (ret) { + /* + * Try to restart with the previous medium so the sensor is + * not left idle, which would fail all subsequent reads. + */ + if (slf3s_start_meas(sf, sf->medium)) + dev_warn(&sf->client->dev, + "failed to restart measurement, reads will fail until a medium is set\n"); + return ret; + } + + sf->medium = mode; + + return 0; +} + +static const struct iio_enum slf3s_medium_enum = { + .items = slf3s_medium_modes, + .num_items = ARRAY_SIZE(slf3s_medium_modes), + .get = slf3s_get_medium, + .set = slf3s_set_medium, +}; + +static const struct iio_chan_spec_ext_info slf3s_ext_info[] = { + IIO_ENUM("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + IIO_ENUM_AVAILABLE("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + { } +}; + +static const struct iio_chan_spec slf3s_channels[] = { + { + .type = IIO_VOLUMEFLOW, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = slf3s_ext_info, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int slf3s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int flow, temp, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + scoped_guard(mutex, &sf->lock) + ret = slf3s_read_sample(sf, &flow, &temp); + if (ret) + return ret; + + *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLUMEFLOW) { + /* + * The variant scale is the flow per LSB in l/s, but + * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3). + * These values are tiny (~1.67e-12 m^3/s for the + * SLF3S-0600F), so emit a 64-bit fixed-point value with + * femto (1e-15) resolution to preserve precision. + * Converting l/s to m^3/s (/ MILLI) and scaling to femto + * (* FEMTO) leaves a net * (FEMTO / MILLI) factor. + */ + const struct slf3s_variant *v = sf->variant; + s64 num = (s64)v->scale.numerator * (FEMTO / MILLI); + s64 scale = DIV_S64_ROUND_CLOSEST(num, + v->scale.denominator); + + iio_val_s64_decompose(scale, val, val2); + + return IIO_VAL_DECIMAL64_FEMTO; + } + /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */ + *val = MILLIDEGREE_PER_DEGREE / 200; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info slf3s_info = { + .read_raw = slf3s_read_raw, +}; + +static void slf3s_stop_meas(void *data) +{ + struct slf3s_data *sf = data; + + slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); +} + +static void slf3s_disable_vdd(void *data) +{ + struct slf3s_data *sf = data; + + regulator_disable(sf->vdd); +} + +static int slf3s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct slf3s_data *sf; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sf)); + if (!indio_dev) + return -ENOMEM; + + sf = iio_priv(indio_dev); + sf->client = client; + i2c_set_clientdata(client, indio_dev); + sf->variant = i2c_get_match_data(client); + sf->medium = SLF3S_MEDIUM_WATER; + crc8_populate_msb(sf->crc_table, SLF3S_CRC8_POLY); + + ret = devm_mutex_init(dev, &sf->lock); + if (ret) + return ret; + + sf->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(sf->vdd)) + return dev_err_probe(dev, PTR_ERR(sf->vdd), + "failed to get vdd supply\n"); + + ret = regulator_enable(sf->vdd); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); + + ret = devm_add_action_or_reset(dev, slf3s_disable_vdd, sf); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + /* + * The sensor may still be in continuous measurement mode from a + * previous boot (warm reboot / kexec); in that case it would NACK + * the product-id command below. Stop it first and ignore the error + * if it was already idle. + */ + slf3s_send_cmd(client, slf3s_cmd_stop_meas); + + ret = slf3s_detect_variant(sf); + if (ret) + return dev_err_probe(dev, ret, "product info read failed\n"); + + ret = slf3s_start_meas(sf, sf->medium); + if (ret) + return dev_err_probe(dev, ret, + "failed to start measurement\n"); + + ret = devm_add_action_or_reset(dev, slf3s_stop_meas, sf); + if (ret) + return ret; + + indio_dev->name = sf->variant->name; + indio_dev->channels = slf3s_channels; + indio_dev->num_channels = ARRAY_SIZE(slf3s_channels); + indio_dev->info = &slf3s_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +/* + * The sensor has no low-power state of its own, so stop the measurement + * and cut the supply while suspended. Resume powers it back up, waits + * out the power-up time and restarts with the medium that was active + * before. + */ +static int slf3s_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + return regulator_disable(sf->vdd); +} + +static int slf3s_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = regulator_enable(sf->vdd); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + return slf3s_start_meas(sf, sf->medium); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(slf3s_pm_ops, slf3s_suspend, slf3s_resume); + +static const struct i2c_device_id slf3s_id[] = { + { + .name = "slf3s-0600f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_0600F], + }, + { + .name = "slf3s-1300f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_1300F], + }, + { + .name = "slf3s-4000b", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, slf3s_id); + +static const struct of_device_id slf3s_of_match[] = { + { + .compatible = "sensirion,slf3s-0600f", + .data = &slf3s_variants[SLF3S_0600F], + }, + { + .compatible = "sensirion,slf3s-1300f", + .data = &slf3s_variants[SLF3S_1300F], + }, + { + .compatible = "sensirion,slf3s-4000b", + .data = &slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(of, slf3s_of_match); + +static struct i2c_driver slf3s_driver = { + .driver = { + .name = "slf3s", + .of_match_table = slf3s_of_match, + .pm = pm_sleep_ptr(&slf3s_pm_ops), + }, + .probe = slf3s_probe, + .id_table = slf3s_id, +}; +module_i2c_driver(slf3s_driver); + +MODULE_AUTHOR("Wadim Mueller "); +MODULE_DESCRIPTION("Sensirion SLF3S liquid flow sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 583cbdf4e8cd..90c6304c4bcd 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -29,6 +29,16 @@ endmenu menu "Phase-Locked Loop (PLL) frequency synthesizers" +config ADF41513 + tristate "Analog Devices ADF41513 PLL Frequency Synthesizer" + depends on SPI + help + Say yes here to build support for Analog Devices ADF41513 + 26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer. + + To compile this driver as a module, choose M here: the + module will be called adf41513. + config ADF4350 tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 70d0e0b70e80..53b4d01414d8 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_ADF41513) += adf41513.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o obj-$(CONFIG_ADF4377) += adf4377.o diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index ea4d2763564a..1e393f40a8c8 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -990,7 +990,6 @@ static int ad9523_probe(struct spi_device *spi) if (IS_ERR(st->sync_gpio)) return PTR_ERR(st->sync_gpio); - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->pdata = pdata; @@ -1009,7 +1008,7 @@ static int ad9523_probe(struct spi_device *spi) } static const struct spi_device_id ad9523_id[] = { - {"ad9523-1", 9523}, + { .name = "ad9523-1" }, { } }; MODULE_DEVICE_TABLE(spi, ad9523_id); diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c new file mode 100644 index 000000000000..c497027012ba --- /dev/null +++ b/drivers/iio/frequency/adf41513.c @@ -0,0 +1,1246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADF41513 SPI PLL Frequency Synthesizer driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Registers */ +#define ADF41513_REG0 0 +#define ADF41513_REG1 1 +#define ADF41513_REG2 2 +#define ADF41513_REG3 3 +#define ADF41513_REG4 4 +#define ADF41513_REG5 5 +#define ADF41513_REG6 6 +#define ADF41513_REG7 7 +#define ADF41513_REG8 8 +#define ADF41513_REG9 9 +#define ADF41513_REG10 10 +#define ADF41513_REG11 11 +#define ADF41513_REG12 12 +#define ADF41513_REG13 13 +#define ADF41513_REG_NUM 14 + +#define ADF41513_SYNC_REG0 BIT(ADF41513_REG0) +#define ADF41513_SYNC_REG1 BIT(ADF41513_REG1) +#define ADF41513_SYNC_REG2 BIT(ADF41513_REG2) +#define ADF41513_SYNC_REG3 BIT(ADF41513_REG3) +#define ADF41513_SYNC_REG4 BIT(ADF41513_REG4) +#define ADF41513_SYNC_REG5 BIT(ADF41513_REG5) +#define ADF41513_SYNC_REG6 BIT(ADF41513_REG6) +#define ADF41513_SYNC_REG7 BIT(ADF41513_REG7) +#define ADF41513_SYNC_REG9 BIT(ADF41513_REG9) +#define ADF41513_SYNC_REG11 BIT(ADF41513_REG11) +#define ADF41513_SYNC_REG12 BIT(ADF41513_REG12) +#define ADF41513_SYNC_REG13 BIT(ADF41513_REG13) +#define ADF41513_SYNC_DIFF 0 +#define ADF41513_SYNC_ALL GENMASK(ADF41513_REG13, ADF41513_REG0) + +/* REG0 Bit Definitions */ +#define ADF41513_REG0_CTRL_BITS_MSK GENMASK(3, 0) +#define ADF41513_REG0_INT_MSK GENMASK(19, 4) +#define ADF41513_REG0_VAR_MOD_MSK BIT(28) + +/* REG1 Bit Definitions */ +#define ADF41513_REG1_FRAC1_MSK GENMASK(28, 4) +#define ADF41513_REG1_DITHER2_MSK BIT(31) + +/* REG2 Bit Definitions */ +#define ADF41513_REG2_PHASE_VAL_MSK GENMASK(15, 4) +#define ADF41513_REG2_PHASE_ADJ_MSK BIT(31) + +/* REG3 Bit Definitions */ +#define ADF41513_REG3_FRAC2_MSK GENMASK(27, 4) + +/* REG4 Bit Definitions */ +#define ADF41513_REG4_MOD2_MSK GENMASK(27, 4) + +/* REG5 Bit Definitions */ +#define ADF41513_REG5_CLK1_DIV_MSK GENMASK(15, 4) +#define ADF41513_REG5_R_CNT_MSK GENMASK(20, 16) +#define ADF41513_REG5_REF_DOUBLER_MSK BIT(21) +#define ADF41513_REG5_RDIV2_MSK BIT(22) +#define ADF41513_REG5_PRESCALER_MSK BIT(23) +#define ADF41513_REG5_LSB_P1_MSK BIT(24) +#define ADF41513_REG5_CP_CURRENT_MSK GENMASK(28, 25) +#define ADF41513_REG5_DLD_MODES_MSK GENMASK(31, 30) + +/* REG6 Bit Definitions */ +#define ADF41513_REG6_COUNTER_RESET_MSK BIT(4) +#define ADF41513_REG6_CP_TRISTATE_MSK BIT(5) +#define ADF41513_REG6_POWER_DOWN_MSK BIT(6) +#define ADF41513_REG6_PD_POLARITY_MSK BIT(7) +#define ADF41513_REG6_LDP_MSK GENMASK(9, 8) +#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK BIT(16) +#define ADF41513_REG6_SD_RESET_MSK BIT(17) +#define ADF41513_REG6_LOL_ENABLE_MSK BIT(18) +#define ADF41513_REG6_ABP_MSK BIT(19) +#define ADF41513_REG6_INT_MODE_MSK BIT(20) +#define ADF41513_REG6_BLEED_ENABLE_MSK BIT(22) +#define ADF41513_REG6_BLEED_POLARITY_MSK BIT(23) +#define ADF41513_REG6_BLEED_CURRENT_MSK GENMASK(31, 24) + +/* REG7 Bit Definitions */ +#define ADF41513_REG7_CLK2_DIV_MSK GENMASK(17, 6) +#define ADF41513_REG7_CLK_DIV_MODE_MSK GENMASK(19, 18) +#define ADF41513_REG7_PS_BIAS_MSK GENMASK(21, 20) +#define ADF41513_REG7_N_DELAY_MSK GENMASK(23, 22) +#define ADF41513_REG7_LD_CLK_SEL_MSK BIT(26) +#define ADF41513_REG7_LD_COUNT_MSK GENMASK(29, 27) + +/* REG9 Bit Definitions */ +#define ADF41513_REG9_LD_BIAS_MSK GENMASK(31, 30) + +/* REG11 Bit Definitions */ +#define ADF41513_REG11_POWER_DOWN_SEL_MSK BIT(31) + +/* REG12 Bit Definitions */ +#define ADF41513_REG12_READBACK_SEL_MSK GENMASK(19, 14) +#define ADF41513_REG12_LE_SELECT_MSK BIT(20) +#define ADF41513_REG12_MASTER_RESET_MSK BIT(22) +#define ADF41513_REG12_LOGIC_LEVEL_MSK BIT(27) +#define ADF41513_REG12_MUXOUT_MSK GENMASK(31, 28) + +/* MUXOUT Selection */ +#define ADF41513_MUXOUT_TRISTATE 0x0 +#define ADF41513_MUXOUT_DVDD 0x1 +#define ADF41513_MUXOUT_DGND 0x2 +#define ADF41513_MUXOUT_R_DIV 0x3 +#define ADF41513_MUXOUT_N_DIV 0x4 +#define ADF41513_MUXOUT_DIG_LD 0x6 +#define ADF41513_MUXOUT_SDO 0x7 +#define ADF41513_MUXOUT_READBACK 0x8 +#define ADF41513_MUXOUT_CLK1_DIV 0xA +#define ADF41513_MUXOUT_R_DIV2 0xD +#define ADF41513_MUXOUT_N_DIV2 0xE + +/* DLD Mode Selection */ +#define ADF41513_DLD_TRISTATE 0x0 +#define ADF41513_DLD_DIG_LD 0x1 +#define ADF41513_DLD_LOW 0x2 +#define ADF41513_DLD_HIGH 0x3 + +/* Prescaler Selection */ +#define ADF41513_PRESCALER_4_5 0 +#define ADF41513_PRESCALER_8_9 1 +#define ADF41513_PRESCALER_AUTO 2 + +/* CLK Divider mode */ +#define ADF41513_CLK_DIV_MODE_OFF 0 +#define ADF41513_CLK_DIV_MODE_PHASE_RESYNC 2 + +/* Specifications */ +#define ADF41510_MAX_RF_FREQ_HZ (10ULL * HZ_PER_GHZ) +#define ADF41513_MIN_RF_FREQ_HZ (1ULL * HZ_PER_GHZ) +#define ADF41513_MAX_RF_FREQ_HZ (26500ULL * HZ_PER_MHZ) + +#define ADF41513_MIN_REF_FREQ_HZ (10 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_HZ (800 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_DOUBLER_HZ (225 * HZ_PER_MHZ) + +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ (250ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ (125ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_FREQ_RESOLUTION_UHZ (100ULL * KILO * MICROHZ_PER_HZ) + +#define ADF41513_MIN_INT_4_5 20 +#define ADF41513_MAX_INT_4_5 511 +#define ADF41513_MIN_INT_8_9 64 +#define ADF41513_MAX_INT_8_9 1023 + +#define ADF41513_MIN_INT_FRAC_4_5 23 +#define ADF41513_MIN_INT_FRAC_8_9 75 + +#define ADF41513_MIN_R_CNT 1 +#define ADF41513_MAX_R_CNT 32 + +#define ADF41513_MIN_R_SET 1800 +#define ADF41513_DEFAULT_R_SET 2700 +#define ADF41513_MAX_R_SET 10000 + +#define ADF41513_MIN_CP_VOLTAGE_mV 810 +#define ADF41513_DEFAULT_CP_VOLTAGE_mV 6480 +#define ADF41513_MAX_CP_VOLTAGE_mV 12960 + +#define ADF41513_MIN_CP_CURRENT_uA 81 +#define ADF41513_MAX_CP_CURRENT_uA 7200 + +#define ADF41513_LD_COUNT_FAST_MIN 2 +#define ADF41513_LD_COUNT_FAST_LIMIT 64 +#define ADF41513_LD_COUNT_MIN 64 +#define ADF41513_LD_COUNT_MAX 8192 + +#define ADF41513_FIXED_MODULUS BIT(25) +#define ADF41513_MAX_MOD2 (BIT(24) - 1) +#define ADF41513_MAX_PHASE_VAL (BIT(12) - 1) +#define ADF41513_MAX_CLK_DIVIDER (BIT(12) - 1) + +#define ADF41513_HZ_DECIMAL_SCALE 6 +#define ADF41513_PS_BIAS_INIT 0x2 +#define ADF41513_MAX_PHASE_MICRORAD ((2 * 314159265UL) / 100) + +enum adf41513_pll_mode { + ADF41513_MODE_INVALID, + ADF41513_MODE_INTEGER_N, + ADF41513_MODE_FIXED_MODULUS, + ADF41513_MODE_VARIABLE_MODULUS, +}; + +struct adf41513_chip_info { + const char *name; + u64 max_rf_freq_hz; + bool has_prescaler_8_9; +}; + +struct adf41513_data { + u64 power_up_frequency_hz; + u64 freq_resolution_uhz; + u32 phase_resync_period_ns; + u32 charge_pump_voltage_mv; + u32 lock_detect_count; + + u8 ref_div_factor; + bool ref_doubler_en; + bool ref_div2_en; + bool phase_detector_polarity; + + bool logic_lvl_1v8_en; + bool le_sync_en; +}; + +struct adf41513_pll_settings { + enum adf41513_pll_mode mode; + + /* reference path parameters */ + u8 r_counter; + u8 ref_doubler; + u8 ref_div2; + u8 prescaler; + + /* frequency parameters */ + u64 target_frequency_uhz; + u64 actual_frequency_uhz; + u64 pfd_frequency_uhz; + + /* pll parameters */ + u32 frac1; + u32 frac2; + u32 mod2; + u16 int_val; +}; + +struct adf41513_state { + const struct adf41513_chip_info *chip_info; + struct spi_device *spi; + struct gpio_desc *lock_detect; + struct clk *ref_clk; + u32 ref_freq_hz; + + /* + * Lock for accessing device registers. Some operations require + * multiple consecutive R/W operations, during which the device + * shouldn't be interrupted. The buffers are also shared across + * all operations so need to be protected on stand alone reads and + * writes. + */ + struct mutex lock; + + /* Cached register values */ + u32 regs[ADF41513_REG_NUM]; + u32 regs_hw[ADF41513_REG_NUM]; + + struct adf41513_data data; + struct adf41513_pll_settings settings; + + bool powerdown; +}; + +static const u16 adf41513_ld_window_x10_ns[] = { + 9, 12, 16, 17, 21, 28, 29, 35, /* 0 - 7 */ + 43, 47, 49, 52, 70, 79, 115, /* 8 - 14 */ +}; + +static const u8 adf41513_ldp_bias[] = { + 0xC, 0xD, 0xE, 0x8, 0x9, 0x4, 0xA, 0x5, /* 0 - 7 */ + 0x0, 0x6, 0xB, 0x1, 0x2, 0x7, 0x3, /* 8 - 14 */ +}; + +static const char * const adf41513_power_supplies[] = { + "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", +}; + +static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask) +{ + __be32 d32; + int ret; + + /* write registers in reverse order (R13 to R0)*/ + for (int i = ADF41513_REG13; i >= ADF41513_REG0; i--) { + if (st->regs_hw[i] == st->regs[i] && !(sync_mask & BIT(i))) + continue; + + d32 = cpu_to_be32(st->regs[i] | i); + ret = spi_write_then_read(st->spi, &d32, sizeof(d32), NULL, 0); + if (ret < 0) + return ret; + st->regs_hw[i] = st->regs[i]; + dev_dbg(&st->spi->dev, "REG%d <= 0x%08X\n", i, st->regs[i] | i); + } + + return 0; +} + +static u64 adf41513_pll_get_rate(struct adf41513_state *st) +{ + struct adf41513_pll_settings *cfg = &st->settings; + + if (cfg->mode != ADF41513_MODE_INVALID) + return cfg->actual_frequency_uhz; + + /* get pll settings from regs_hw */ + cfg->int_val = FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_REG0]); + cfg->frac1 = FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_REG1]); + cfg->frac2 = FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_REG3]); + cfg->mod2 = FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4]); + cfg->r_counter = FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_doubler = FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_div2 = FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513_REG5]); + cfg->prescaler = FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF41513_REG5]); + + if (!cfg->mod2) + cfg->mod2 = 1; + if (!cfg->r_counter) + cfg->r_counter = ADF41513_MAX_R_CNT; + + /* calculate pfd frequency */ + cfg->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (cfg->ref_doubler) + cfg->pfd_frequency_uhz <<= 1; + if (cfg->ref_div2) + cfg->pfd_frequency_uhz >>= 1; + cfg->pfd_frequency_uhz = div_u64(cfg->pfd_frequency_uhz, cfg->r_counter); + cfg->actual_frequency_uhz = (u64)cfg->int_val * cfg->pfd_frequency_uhz; + + /* check if int mode is selected */ + if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) { + cfg->mode = ADF41513_MODE_INTEGER_N; + } else { + cfg->actual_frequency_uhz += mul_u64_u32_div(cfg->pfd_frequency_uhz, + cfg->frac1, + ADF41513_FIXED_MODULUS); + + /* check if variable modulus is selected */ + if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) { + cfg->actual_frequency_uhz += + mul_u64_u64_div_u64(cfg->frac2, + cfg->pfd_frequency_uhz, + (u64)cfg->mod2 * ADF41513_FIXED_MODULUS); + + cfg->mode = ADF41513_MODE_VARIABLE_MODULUS; + } else { + /* LSB_P1 offset */ + if (!FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[ADF41513_REG5])) + cfg->actual_frequency_uhz += + div_u64(cfg->pfd_frequency_uhz, + 2 * ADF41513_FIXED_MODULUS); + cfg->mode = ADF41513_MODE_FIXED_MODULUS; + } + } + + cfg->target_frequency_uhz = cfg->actual_frequency_uhz; + + return cfg->actual_frequency_uhz; +} + +static int adf41513_calc_pfd_frequency(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 fpfd_limit_uhz) +{ + result->ref_div2 = st->data.ref_div2_en; + result->ref_doubler = st->data.ref_doubler_en; + result->r_counter = st->data.ref_div_factor - 1; + + do { + result->r_counter++; + /* f_PFD = REF_IN × ((1 + D)/(R × (1 + T))) */ + result->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (result->ref_doubler) + result->pfd_frequency_uhz <<= 1; + if (result->ref_div2) + result->pfd_frequency_uhz >>= 1; + result->pfd_frequency_uhz = div_u64(result->pfd_frequency_uhz, + result->r_counter); + } while (result->pfd_frequency_uhz > fpfd_limit_uhz); + + if (result->r_counter > ADF41513_MAX_R_CNT) { + dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n"); + return -ERANGE; + } + + return 0; +} + +static int adf41513_calc_integer_n(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u32 max_int = st->chip_info->has_prescaler_8_9 ? + ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5; + u64 freq_error_uhz; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_val < max_int) { + int_val++; + freq_error_uhz = result->pfd_frequency_uhz - freq_error_uhz; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* set prescaler */ + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + result->mode = ADF41513_MODE_INTEGER_N; + result->int_val = int_val; + result->frac1 = 0; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_fixed_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 resolution_uhz = div_u64(result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS); + u64 target_frequency_uhz = result->target_frequency_uhz; + u64 freq_error_uhz; + u32 int_val, frac1; + bool lsb_p1_offset = !FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs[ADF41513_REG5]); + + /* LSB_P1 adds a frequency offset of f_pfd/2^26 */ + if (lsb_p1_offset) + target_frequency_uhz -= resolution_uhz >> 1; + + int_val = div64_u64_rem(target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* compute frac1 and fixed modulus error */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + freq_error_uhz -= mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MODULUS - 1)) { + frac1++; + freq_error_uhz = freq_error_uhz < resolution_uhz ? + resolution_uhz - freq_error_uhz : 0; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + if (lsb_p1_offset) + result->actual_frequency_uhz += (resolution_uhz >> 1); + result->actual_frequency_uhz += mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_FIXED_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_variable_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 freq_error_uhz, mod2; + u32 frac1, frac2; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, + result->pfd_frequency_uhz, &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* calculate required mod2 based on target resolution / 2 */ + mod2 = DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz << 1, + st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS); + /* ensure mod2 is at least 2 for meaningful operation */ + mod2 = clamp(mod2, 2, ADF41513_MAX_MOD2); + + /* calculate frac1 and frac2 */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + frac2 = mul_u64_u64_div_u64(freq_error_uhz, mod2 * ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz) - mod2 * frac1; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + result->actual_frequency_uhz += mul_u64_u64_div_u64(mod2 * frac1 + frac2, + result->pfd_frequency_uhz, + mod2 * ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_VARIABLE_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = frac2; + result->mod2 = mod2; + + return 0; +} + +static int adf41513_calc_pll_settings(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 rf_out_uhz) +{ + u64 max_rf_freq_uhz = st->chip_info->max_rf_freq_hz * MICRO; + u64 min_rf_freq_uhz = ADF41513_MIN_RF_FREQ_HZ * MICRO; + u64 pfd_freq_limit_uhz; + int ret; + + if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) { + dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] uHz\n", + rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz); + return -EINVAL; + } + + result->target_frequency_uhz = rf_out_uhz; + + /* try integer-N first (best phase noise performance) */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5), + ADF41513_MAX_PFD_FREQ_INT_N_UHZ); + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + + if (adf41513_calc_integer_n(st, result) == 0) + return 0; + + /* try fractional-N: recompute pfd frequency if necessary */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5), + ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ); + if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) { + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + } + + /* fixed-modulus attempt */ + if (adf41513_calc_fixed_mod(st, result) == 0) + return 0; + + /* variable-modulus attempt */ + ret = adf41513_calc_variable_mod(st, result); + if (ret) { + dev_err(&st->spi->dev, + "no valid PLL configuration found for %llu uHz\n", + rf_out_uhz); + return ret; + } + + return 0; +} + +static void adf41513_set_bleed_val(struct adf41513_state *st) +{ + u32 bleed_value, cp_index; + + if (!(st->regs[ADF41513_REG6] & ADF41513_REG6_BLEED_ENABLE_MSK)) + return; + + if (st->data.phase_detector_polarity) + bleed_value = 90; + else + bleed_value = 144; + + cp_index = 1 + FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK, + st->regs[ADF41513_REG5]); + bleed_value = div64_u64(st->settings.pfd_frequency_uhz * cp_index * bleed_value, + 1600ULL * MEGA * MICROHZ_PER_HZ); + + FIELD_MODIFY(ADF41513_REG6_BLEED_CURRENT_MSK, &st->regs[ADF41513_REG6], + bleed_value); +} + +static void adf41513_set_ld_window(struct adf41513_state *st) +{ + /* + * The ideal lock detector window size is halfway between the max + * window, set by the phase comparison period t_PFD = (1 / f_PFD), + * and the minimum is set by (I_BLEED/I_CP) × t_PFD + */ + u16 ld_window_10x_ns = div64_u64(10ULL * NSEC_PER_SEC * MICROHZ_PER_HZ, + st->settings.pfd_frequency_uhz << 1); + u8 ld_idx, ldp, ld_bias; + + if (st->settings.mode != ADF41513_MODE_INTEGER_N) { + /* account for bleed current (deduced from eq.6 and eq.7) */ + if (st->data.phase_detector_polarity) + ld_window_10x_ns += 4; + else + ld_window_10x_ns += 6; + } + + ld_idx = find_closest(ld_window_10x_ns, adf41513_ld_window_x10_ns, + ARRAY_SIZE(adf41513_ld_window_x10_ns)); + ldp = (adf41513_ldp_bias[ld_idx] >> 2) & 0x3; + ld_bias = adf41513_ldp_bias[ld_idx] & 0x3; + + FIELD_MODIFY(ADF41513_REG6_LDP_MSK, &st->regs[ADF41513_REG6], ldp); + FIELD_MODIFY(ADF41513_REG9_LD_BIAS_MSK, &st->regs[ADF41513_REG9], ld_bias); +} + +static void adf41513_set_phase_resync(struct adf41513_state *st) +{ + u32 total_div, clk1_div, clk2_div; + + if (!st->data.phase_resync_period_ns) + return; + + /* assuming both clock dividers hold similar values */ + total_div = mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz, + st->data.phase_resync_period_ns, + 1ULL * MICROHZ_PER_HZ * NSEC_PER_SEC); + clk1_div = clamp(int_sqrt(total_div), 1, + ADF41513_MAX_CLK_DIVIDER); + clk2_div = clamp(DIV_ROUND_CLOSEST(total_div, clk1_div), 1, + ADF41513_MAX_CLK_DIVIDER); + + FIELD_MODIFY(ADF41513_REG5_CLK1_DIV_MSK, &st->regs[ADF41513_REG5], + clk1_div); + FIELD_MODIFY(ADF41513_REG7_CLK2_DIV_MSK, &st->regs[ADF41513_REG7], + clk2_div); + + /* enable phase resync */ + FIELD_MODIFY(ADF41513_REG7_CLK_DIV_MODE_MSK, &st->regs[ADF41513_REG7], + ADF41513_CLK_DIV_MODE_PHASE_RESYNC); +} + +static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask) +{ + struct adf41513_pll_settings result; + bool pfd_change = false; + bool mode_change = false; + int ret; + + ret = adf41513_calc_pll_settings(st, &result, freq_uhz); + if (ret < 0) + return ret; + + /* apply computed results to pll settings */ + pfd_change = st->settings.pfd_frequency_uhz != result.pfd_frequency_uhz; + mode_change = st->settings.mode != result.mode; + st->settings = result; + + dev_dbg(&st->spi->dev, + "%s mode: int=%u, frac1=%u, frac2=%u, mod2=%u, fpdf=%llu Hz, prescaler=%s\n", + (result.mode == ADF41513_MODE_INTEGER_N) ? "integer-n" : + (result.mode == ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "variable-modulus", + result.int_val, result.frac1, result.frac2, result.mod2, + div64_u64(result.pfd_frequency_uhz, MICRO), + result.prescaler ? "8/9" : "4/5"); + + st->regs[ADF41513_REG0] = FIELD_PREP(ADF41513_REG0_INT_MSK, + st->settings.int_val); + if (st->settings.mode == ADF41513_MODE_VARIABLE_MODULUS) + st->regs[ADF41513_REG0] |= ADF41513_REG0_VAR_MOD_MSK; + + st->regs[ADF41513_REG1] = FIELD_PREP(ADF41513_REG1_FRAC1_MSK, + st->settings.frac1); + if (st->settings.mode != ADF41513_MODE_INTEGER_N) + st->regs[ADF41513_REG1] |= ADF41513_REG1_DITHER2_MSK; + + st->regs[ADF41513_REG3] = FIELD_PREP(ADF41513_REG3_FRAC2_MSK, + st->settings.frac2); + FIELD_MODIFY(ADF41513_REG4_MOD2_MSK, &st->regs[ADF41513_REG4], + st->settings.mod2); + FIELD_MODIFY(ADF41513_REG5_R_CNT_MSK, &st->regs[ADF41513_REG5], + st->settings.r_counter % ADF41513_MAX_R_CNT); + FIELD_MODIFY(ADF41513_REG5_REF_DOUBLER_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_doubler); + FIELD_MODIFY(ADF41513_REG5_RDIV2_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_div2); + FIELD_MODIFY(ADF41513_REG5_PRESCALER_MSK, &st->regs[ADF41513_REG5], + st->settings.prescaler); + + if (st->settings.mode == ADF41513_MODE_INTEGER_N) { + st->regs[ADF41513_REG6] |= ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_ENABLE_MSK; + } else { + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK; + } + + if (pfd_change) + adf41513_set_phase_resync(st); + + if (pfd_change || mode_change) { + adf41513_set_bleed_val(st); + adf41513_set_ld_window(st); + } + + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); +} + +static int adf41513_suspend(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] |= FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; + return adf41513_sync_config(st, ADF41513_SYNC_DIFF); +} + +static int adf41513_resume(struct adf41513_state *st) +{ + int ret; + + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK; + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_ALL); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; +} + +static ssize_t adf41513_read_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + int vals[2]; + + guard(mutex)(&st->lock); + + iio_val_s64_decompose(st->data.freq_resolution_uhz, &vals[0], &vals[1]); + return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals); +} + +static ssize_t adf41513_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u32 val; + + guard(mutex)(&st->lock); + + val = FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, st->regs_hw[ADF41513_REG6]); + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t adf41513_write_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 freq_uhz; + int ret; + + ret = kstrtoudec64(buf, ADF41513_HZ_DECIMAL_SCALE, &freq_uhz); + if (ret) + return ret; + + if (freq_uhz == 0 || freq_uhz > ADF41513_MAX_FREQ_RESOLUTION_UHZ) + return -EINVAL; + + guard(mutex)(&st->lock); + + st->data.freq_resolution_uhz = freq_uhz; + return len; +} + +static ssize_t adf41513_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + if (val) + ret = adf41513_suspend(st); + else + ret = adf41513_resume(st); + if (ret) + return ret; + + st->powerdown = val; + return len; +} + +static const struct iio_chan_spec_ext_info adf41513_ext_info[] = { + { + .name = "frequency_resolution", + .read = adf41513_read_resolution, + .write = adf41513_write_resolution, + .shared = IIO_SEPARATE, + }, + { + .name = "powerdown", + .read = adf41513_read_powerdown, + .write = adf41513_write_powerdown, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_chan_spec adf41513_chan = { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY) | + BIT(IIO_CHAN_INFO_PHASE), + .ext_info = adf41513_ext_info, +}; + +static int adf41513_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + if (st->lock_detect && + !gpiod_get_value_cansleep(st->lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + return -EBUSY; + } + tmp64 = adf41513_pll_get_rate(st); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case IIO_CHAN_INFO_PHASE: + tmp64 = FIELD_GET(ADF41513_REG2_PHASE_VAL_MSK, + st->regs_hw[ADF41513_REG2]); + tmp64 = (tmp64 * ADF41513_MAX_PHASE_MICRORAD) >> 12; + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64 = iio_val_s64_compose(val, val2); + u16 phase_val; + int ret; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + return adf41513_set_frequency(st, tmp64, ADF41513_SYNC_DIFF); + case IIO_CHAN_INFO_PHASE: + if (tmp64 >= ADF41513_MAX_PHASE_MICRORAD) + return -EINVAL; + + phase_val = DIV_U64_ROUND_CLOSEST(tmp64 << 12, + ADF41513_MAX_PHASE_MICRORAD); + phase_val = min(phase_val, ADF41513_MAX_PHASE_VAL); + st->regs[ADF41513_REG2] |= ADF41513_REG2_PHASE_ADJ_MSK; + FIELD_MODIFY(ADF41513_REG2_PHASE_VAL_MSK, + &st->regs[ADF41513_REG2], phase_val); + ret = adf41513_sync_config(st, ADF41513_SYNC_REG0); + /* clear phase adjust for the next sync */ + st->regs[ADF41513_REG2] &= ~ADF41513_REG2_PHASE_ADJ_MSK; + return ret; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + case IIO_CHAN_INFO_PHASE: + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adf41513_state *st = iio_priv(indio_dev); + + if (reg > ADF41513_REG13) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (!readval) { + if (reg <= ADF41513_REG6) + st->settings.mode = ADF41513_MODE_INVALID; + st->regs[reg] = writeval & ~0xF; /* Clear control bits */ + return adf41513_sync_config(st, BIT(reg)); + } + + *readval = st->regs_hw[reg]; + return 0; +} + +static const struct iio_info adf41513_info = { + .read_raw = adf41513_read_raw, + .write_raw = adf41513_write_raw, + .write_raw_get_fmt = adf41513_write_raw_get_fmt, + .debugfs_reg_access = &adf41513_reg_access, +}; + +static int adf41513_parse_fw(struct adf41513_state *st) +{ + struct device *dev = &st->spi->dev; + u32 tmp, cp_resistance, cp_current; + int ret; + + tmp = ADF41510_MAX_RF_FREQ_HZ / MEGA; + device_property_read_u32(dev, "adi,power-up-frequency-mhz", &tmp); + st->data.power_up_frequency_hz = (u64)tmp * MEGA; + if (st->data.power_up_frequency_hz < ADF41513_MIN_RF_FREQ_HZ || + st->data.power_up_frequency_hz > st->chip_info->max_rf_freq_hz) + return dev_err_probe(dev, -ERANGE, + "power-up frequency %llu Hz out of range\n", + st->data.power_up_frequency_hz); + + tmp = ADF41513_MIN_R_CNT; + device_property_read_u32(dev, "adi,reference-div-factor", &tmp); + if (tmp < ADF41513_MIN_R_CNT || tmp > ADF41513_MAX_R_CNT) + return dev_err_probe(dev, -ERANGE, + "invalid reference div factor %u\n", tmp); + st->data.ref_div_factor = tmp; + + st->data.ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable"); + st->data.ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable"); + + if (st->data.ref_doubler_en && + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER_HZ) { + return dev_err_probe(dev, -ERANGE, + "Ref frequency not supported with doubler enabled\n"); + } + + cp_resistance = ADF41513_DEFAULT_R_SET; + device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &cp_resistance); + if (cp_resistance < ADF41513_MIN_R_SET || cp_resistance > ADF41513_MAX_R_SET) + return dev_err_probe(dev, -ERANGE, "R_SET %u Ohms out of range\n", cp_resistance); + + st->data.charge_pump_voltage_mv = ADF41513_DEFAULT_CP_VOLTAGE_mV; + ret = device_property_read_u32(dev, "adi,charge-pump-current-microamp", &cp_current); + if (!ret) { + if (cp_current < ADF41513_MIN_CP_CURRENT_uA || + cp_current > ADF41513_MAX_CP_CURRENT_uA) + return dev_err_probe(dev, -ERANGE, + "I_CP %u uA out of range\n", cp_current); + + tmp = DIV_ROUND_CLOSEST(cp_current * cp_resistance, MILLI); + if (tmp < ADF41513_MIN_CP_VOLTAGE_mV || tmp > ADF41513_MAX_CP_VOLTAGE_mV) + return dev_err_probe(dev, -ERANGE, "I_CP %u uA (%u Ohms) out of range\n", + cp_current, cp_resistance); + st->data.charge_pump_voltage_mv = tmp; + } + + st->data.phase_detector_polarity = + device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"); + + st->data.phase_resync_period_ns = 0; + ret = device_property_read_u32(dev, "adi,phase-resync-period-ns", &tmp); + if (!ret) + st->data.phase_resync_period_ns = tmp; + + st->data.logic_lvl_1v8_en = device_property_read_bool(dev, "adi,logic-level-1v8-enable"); + + tmp = ADF41513_LD_COUNT_MIN; + device_property_read_u32(dev, "adi,lock-detector-count", &tmp); + if (tmp < ADF41513_LD_COUNT_FAST_MIN || tmp > ADF41513_LD_COUNT_MAX || + !is_power_of_2(tmp)) + return dev_err_probe(dev, -ERANGE, + "invalid lock detect count: %u\n", tmp); + st->data.lock_detect_count = tmp; + + /* load enable sync */ + st->data.le_sync_en = device_property_read_bool(dev, "adi,le-sync-enable"); + st->data.freq_resolution_uhz = MICROHZ_PER_HZ; + + return 0; +} + +static void adf41513_chip_disable(void *data) +{ + gpiod_set_value_cansleep(data, 0); +} + +static void adf41513_close(void *data) +{ + adf41513_suspend(data); +} + +static int adf41513_setup(struct device *dev, struct adf41513_state *st) +{ + u32 tmp; + int ret; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + /* assuming DLD pin is used for lock detection */ + st->regs[ADF41513_REG5] = FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK, + ADF41513_DLD_DIG_LD); + + tmp = DIV_ROUND_CLOSEST(st->data.charge_pump_voltage_mv, ADF41513_MIN_CP_VOLTAGE_mV); + st->regs[ADF41513_REG5] |= FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, tmp - 1); + + st->regs[ADF41513_REG6] = ADF41513_REG6_ABP_MSK | + ADF41513_REG6_LOL_ENABLE_MSK | + ADF41513_REG6_SD_RESET_MSK; + if (st->data.phase_detector_polarity) + st->regs[ADF41513_REG6] |= ADF41513_REG6_PD_POLARITY_MSK; + + st->regs[ADF41513_REG7] = FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK, + ADF41513_PS_BIAS_INIT); + tmp = ilog2(st->data.lock_detect_count); + if (st->data.lock_detect_count < ADF41513_LD_COUNT_FAST_LIMIT) { + tmp -= const_ilog2(ADF41513_LD_COUNT_FAST_MIN); + st->regs[ADF41513_REG7] |= ADF41513_REG7_LD_CLK_SEL_MSK; + } else { + tmp -= const_ilog2(ADF41513_LD_COUNT_MIN); + } + st->regs[ADF41513_REG7] |= FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, tmp); + + st->regs[ADF41513_REG11] = ADF41513_REG11_POWER_DOWN_SEL_MSK; + st->regs[ADF41513_REG12] = FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK, + st->data.logic_lvl_1v8_en ? 0 : 1); + + /* perform initialization sequence with power-up frequency */ + ret = adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, + ADF41513_SYNC_ALL); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, adf41513_close, st); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; +} + +static int adf41513_pm_suspend(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + return adf41513_suspend(st); +} + +static int adf41513_pm_resume(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + if (st->powerdown) + return 0; /* nothing to do */ + + return adf41513_resume(st); +} + +static const struct adf41513_chip_info adf41510_chip_info = { + .name = "adf41510", + .max_rf_freq_hz = ADF41510_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = false, +}; + +static const struct adf41513_chip_info adf41513_chip_info = { + .name = "adf41513", + .max_rf_freq_hz = ADF41513_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = true, +}; + +static int adf41513_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct gpio_desc *chip_enable; + struct iio_dev *indio_dev; + struct adf41513_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; + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + spi_set_drvdata(spi, st); + + st->ref_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->ref_clk)) + return PTR_ERR(st->ref_clk); + + st->ref_freq_hz = clk_get_rate(st->ref_clk); + if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ_HZ || + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_HZ) + return dev_err_probe(dev, -ERANGE, + "reference frequency %u Hz out of range\n", + st->ref_freq_hz); + + ret = adf41513_parse_fw(st); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(adf41513_power_supplies), + adf41513_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable regulators\n"); + + st->lock_detect = devm_gpiod_get_optional(dev, "lock-detect", GPIOD_IN); + if (IS_ERR(st->lock_detect)) + return dev_err_probe(dev, PTR_ERR(st->lock_detect), + "fail to request lock detect GPIO\n"); + + chip_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(chip_enable)) + return dev_err_probe(dev, PTR_ERR(chip_enable), + "fail to request chip enable GPIO\n"); + + ret = devm_add_action_or_reset(dev, adf41513_chip_disable, chip_enable); + if (ret) + return dev_err_probe(dev, ret, "Failed to add disable action\n"); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &adf41513_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &adf41513_chan; + indio_dev->num_channels = 1; + + ret = adf41513_setup(dev, st); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to setup device\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id adf41513_id[] = { + { .name = "adf41510", .driver_data = (kernel_ulong_t)&adf41510_chip_info }, + { .name = "adf41513", .driver_data = (kernel_ulong_t)&adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, adf41513_id); + +static const struct of_device_id adf41513_of_match[] = { + { .compatible = "adi,adf41510", .data = &adf41510_chip_info }, + { .compatible = "adi,adf41513", .data = &adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adf41513_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops, adf41513_pm_suspend, adf41513_pm_resume); + +static struct spi_driver adf41513_driver = { + .driver = { + .name = "adf41513", + .pm = pm_ptr(&adf41513_pm_ops), + .of_match_table = adf41513_of_match, + }, + .probe = adf41513_probe, + .id_table = adf41513_id, +}; +module_spi_driver(adf41513_driver); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 639cac522b44..315317d6eec4 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -691,8 +691,8 @@ static const struct of_device_id adf4350_of_match[] = { MODULE_DEVICE_TABLE(of, adf4350_of_match); static const struct spi_device_id adf4350_id[] = { - {"adf4350", 4350}, - {"adf4351", 4351}, + { .name = "adf4350", .driver_data = 4350 }, + { .name = "adf4351", .driver_data = 4351 }, { } }; MODULE_DEVICE_TABLE(spi, adf4350_id); diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d6dc7827fb41..e20ed718d01d 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -624,8 +624,8 @@ static int adf4371_probe(struct spi_device *spi) } static const struct spi_device_id adf4371_id_table[] = { - { "adf4371", (kernel_ulong_t)&adf4371_chip_info }, - { "adf4372", (kernel_ulong_t)&adf4372_chip_info }, + { .name = "adf4371", .driver_data = (kernel_ulong_t)&adf4371_chip_info }, + { .name = "adf4372", .driver_data = (kernel_ulong_t)&adf4372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index ff6077e29c73..4dd19a9aa994 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -518,14 +519,15 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) u64 clkin_freq; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, 0x12, &ref_div_factor); if (ret) - goto exit; + return ret; ret = regmap_bulk_read(st->regmap, 0x10, st->buf, sizeof(st->buf)); if (ret) - goto exit; + return ret; clkin_freq = clk_get_rate(st->clkin); ref_div_factor = FIELD_GET(ADF4377_0012_R_DIV_MSK, ref_div_factor); @@ -533,10 +535,8 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) get_unaligned_le16(&st->buf)); *freq = div_u64(clkin_freq, ref_div_factor) * n_int; -exit: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adf4377_set_freq(struct adf4377_state *st, u64 freq) @@ -545,26 +545,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) u64 f_vco; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); - if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) { - ret = -EINVAL; - goto exit; - } + if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) + return -EINVAL; ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | ADF4377_001C_EN_DRCLK_MSK, FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 1) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x11, ADF4377_0011_EN_AUTOCAL_MSK | ADF4377_0011_DCLK_DIV2_MSK, FIELD_PREP(ADF4377_0011_EN_AUTOCAL_MSK, 1) | FIELD_PREP(ADF4377_0011_DCLK_DIV2_MSK, st->dclk_div2)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2E, ADF4377_002E_EN_ADC_CNV_MSK | ADF4377_002E_EN_ADC_MSK | @@ -574,56 +572,56 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_002E_ADC_A_CONV_MSK, ADF4377_002E_ADC_A_CONV_VCO_CALIB)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2F, ADF4377_002F_DCLK_DIV1_MSK, FIELD_PREP(ADF4377_002F_DCLK_DIV1_MSK, st->dclk_div1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x24, ADF4377_0024_DCLK_MODE_MSK, FIELD_PREP(ADF4377_0024_DCLK_MODE_MSK, st->dclk_mode)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x27, FIELD_PREP(ADF4377_0027_SYNTH_LOCK_TO_LSB_MSK, st->synth_lock_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x28, ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, FIELD_PREP(ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, st->synth_lock_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x29, FIELD_PREP(ADF4377_0029_VCO_ALC_TO_LSB_MSK, st->vco_alc_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2A, ADF4377_002A_VCO_ALC_TO_MSB_MSK, FIELD_PREP(ADF4377_002A_VCO_ALC_TO_MSB_MSK, st->vco_alc_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x26, FIELD_PREP(ADF4377_0026_VCO_BAND_DIV_MSK, st->vco_band_div)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x2D, FIELD_PREP(ADF4377_002D_ADC_CLK_DIV_MSK, st->adc_clk_div)); if (ret) - goto exit; + return ret; st->clkout_div_sel = 0; @@ -641,24 +639,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_0011_EN_RDBLR_MSK, 0) | FIELD_PREP(ADF4377_0011_N_INT_MSB_MSK, st->n_int >> 8)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x12, ADF4377_0012_R_DIV_MSK | ADF4377_0012_CLKOUT_DIV_MSK, FIELD_PREP(ADF4377_0012_CLKOUT_DIV_MSK, st->clkout_div_sel) | FIELD_PREP(ADF4377_0012_R_DIV_MSK, st->ref_div_factor)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x10, FIELD_PREP(ADF4377_0010_N_INT_LSB_MSK, st->n_int)); if (ret) - goto exit; + return ret; ret = regmap_read_poll_timeout(st->regmap, 0x49, read_val, !(read_val & (ADF4377_0049_FSM_BUSY_MSK)), 200, 200 * 100); if (ret) - goto exit; + return ret; /* Disable EN_DNCLK, EN_DRCLK */ ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | @@ -666,26 +664,21 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 0) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Disable EN_ADC_CLK */ ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Set output Amplitude */ - ret = regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | - ADF4377_0019_CLKOUT1_OP_MSK, - FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, - ADF4377_0019_CLKOUT_420MV) | - FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, - ADF4377_0019_CLKOUT_420MV)); - -exit: - mutex_unlock(&st->lock); - - return ret; + return regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | + ADF4377_0019_CLKOUT1_OP_MSK, + FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, + ADF4377_0019_CLKOUT_420MV) | + FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, + ADF4377_0019_CLKOUT_420MV)); } static void adf4377_gpio_init(struct adf4377_state *st) @@ -919,13 +912,11 @@ static int adf4377_properties_parse(struct adf4377_state *st) static int adf4377_freq_change(struct notifier_block *nb, unsigned long action, void *data) { struct adf4377_state *st = container_of(nb, struct adf4377_state, nb); - int ret; if (action == POST_RATE_CHANGE) { - mutex_lock(&st->lock); - ret = notifier_from_errno(adf4377_init(st)); - mutex_unlock(&st->lock); - return ret; + guard(mutex)(&st->lock); + + return notifier_from_errno(adf4377_init(st)); } return NOTIFY_OK; @@ -1097,8 +1088,8 @@ static int adf4377_probe(struct spi_device *spi) } static const struct spi_device_id adf4377_id[] = { - { "adf4377", (kernel_ulong_t)&adf4377_chip_info }, - { "adf4378", (kernel_ulong_t)&adf4378_chip_info }, + { .name = "adf4377", .driver_data = (kernel_ulong_t)&adf4377_chip_info }, + { .name = "adf4378", .driver_data = (kernel_ulong_t)&adf4378_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4377_id); diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index b823adfb0f70..c9baaab574e3 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -619,7 +619,7 @@ static int admv1013_probe(struct spi_device *spi) } static const struct spi_device_id admv1013_id[] = { - { "admv1013", 0 }, + { .name = "admv1013" }, { } }; MODULE_DEVICE_TABLE(spi, admv1013_id); diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 5d36ac4bb666..d1d528975229 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -781,7 +781,7 @@ static int admv1014_probe(struct spi_device *spi) } static const struct spi_device_id admv1014_id[] = { - { "admv1014", 0 }, + { .name = "admv1014" }, { } }; MODULE_DEVICE_TABLE(spi, admv1014_id); diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index c2dc06f05c21..79a615062172 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -486,7 +486,7 @@ static int adrf6780_probe(struct spi_device *spi) } static const struct spi_device_id adrf6780_id[] = { - { "adrf6780", 0 }, + { .name = "adrf6780" }, { } }; MODULE_DEVICE_TABLE(spi, adrf6780_id); diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 178bba95a709..0c8bc1e569fe 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -212,8 +212,8 @@ static int adis16080_probe(struct spi_device *spi) } static const struct spi_device_id adis16080_ids[] = { - { "adis16080", ID_ADIS16080 }, - { "adis16100", ID_ADIS16100 }, + { .name = "adis16080", .driver_data = ID_ADIS16080 }, + { .name = "adis16100", .driver_data = ID_ADIS16100 }, { } }; MODULE_DEVICE_TABLE(spi, adis16080_ids); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 369c7428e1ef..ff335f81e9ae 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -563,10 +563,10 @@ static int adis16136_probe(struct spi_device *spi) } static const struct spi_device_id adis16136_ids[] = { - { "adis16133", ID_ADIS16133 }, - { "adis16135", ID_ADIS16135 }, - { "adis16136", ID_ADIS16136 }, - { "adis16137", ID_ADIS16137 }, + { .name = "adis16133", .driver_data = ID_ADIS16133 }, + { .name = "adis16135", .driver_data = ID_ADIS16135 }, + { .name = "adis16136", .driver_data = ID_ADIS16136 }, + { .name = "adis16137", .driver_data = ID_ADIS16137 }, { } }; MODULE_DEVICE_TABLE(spi, adis16136_ids); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 91b9c5f18ec4..a79a8edf4c98 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -411,12 +411,12 @@ static int adis16260_probe(struct spi_device *spi) * support for the on chip filtering. */ static const struct spi_device_id adis16260_id[] = { - {"adis16260", ADIS16260}, - {"adis16265", ADIS16260}, - {"adis16266", ADIS16266}, - {"adis16250", ADIS16260}, - {"adis16255", ADIS16260}, - {"adis16251", ADIS16251}, + { .name = "adis16260", .driver_data = ADIS16260 }, + { .name = "adis16265", .driver_data = ADIS16260 }, + { .name = "adis16266", .driver_data = ADIS16266 }, + { .name = "adis16250", .driver_data = ADIS16260 }, + { .name = "adis16255", .driver_data = ADIS16260 }, + { .name = "adis16251", .driver_data = ADIS16251 }, { } }; MODULE_DEVICE_TABLE(spi, adis16260_id); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 35928383d4f2..563de2724396 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -585,9 +585,8 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) ret = devm_request_irq(&st->spi->dev, st->spi->irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig); - if (ret < 0) - return dev_err_probe(&st->spi->dev, ret, - "request irq %d failed\n", st->spi->irq); + if (ret) + return ret; ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); if (ret) { diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c index a1d8d3cb301b..030d042cab8b 100644 --- a/drivers/iio/gyro/adxrs450.c +++ b/drivers/iio/gyro/adxrs450.c @@ -439,8 +439,8 @@ static int adxrs450_probe(struct spi_device *spi) } static const struct spi_device_id adxrs450_id[] = { - {"adxrs450", ID_ADXRS450}, - {"adxrs453", ID_ADXRS453}, + { .name = "adxrs450", .driver_data = ID_ADXRS450 }, + { .name = "adxrs453", .driver_data = ID_ADXRS453 }, { } }; MODULE_DEVICE_TABLE(spi, adxrs450_id); diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c index 6aecc5eb8347..14ae1d03be3d 100644 --- a/drivers/iio/gyro/bmg160_spi.c +++ b/drivers/iio/gyro/bmg160_spi.c @@ -33,12 +33,11 @@ static void bmg160_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmg160_spi_id[] = { - {"bmg160", 0}, - {"bmi055_gyro", 0}, - {"bmi088_gyro", 0}, + { .name = "bmg160" }, + { .name = "bmi055_gyro" }, + { .name = "bmi088_gyro" }, { } }; - MODULE_DEVICE_TABLE(spi, bmg160_spi_id); static const struct of_device_id bmg160_of_match[] = { diff --git a/drivers/iio/gyro/fxas21002c_spi.c b/drivers/iio/gyro/fxas21002c_spi.c index bd5b8678da13..40b1e02b2a5c 100644 --- a/drivers/iio/gyro/fxas21002c_spi.c +++ b/drivers/iio/gyro/fxas21002c_spi.c @@ -39,7 +39,7 @@ static void fxas21002c_spi_remove(struct spi_device *spi) } static const struct spi_device_id fxas21002c_spi_id[] = { - { "fxas21002c", 0 }, + { .name = "fxas21002c" }, { } }; MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index adc52a5267f7..58250a972567 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -84,9 +84,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = { /* Channel read_raw handler */ static int gyro_3d_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) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int report_id = -1; @@ -111,8 +110,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { *val = 0; - hid_sensor_power_state(&gyro_state->common_attributes, - false); + hid_sensor_power_state(&gyro_state->common_attributes, false); return -EINVAL; } hid_sensor_power_state(&gyro_state->common_attributes, false); @@ -145,10 +143,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int gyro_3d_write_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) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int ret = 0; @@ -176,8 +172,7 @@ static const struct iio_info gyro_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -198,9 +193,9 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -233,7 +228,7 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int gyro_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct gyro_3d_state *st) { int ret; @@ -253,10 +248,10 @@ static int gyro_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", - st->gyro[0].index, - st->gyro[0].report_id, - st->gyro[1].index, st->gyro[1].report_id, - st->gyro[2].index, st->gyro[2].report_id); + st->gyro[0].index, + st->gyro[0].report_id, + st->gyro[1].index, st->gyro[1].report_id, + st->gyro[2].index, st->gyro[2].report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_GYRO_3D, @@ -317,32 +312,32 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &gyro_state->common_attributes); + &gyro_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - gyro_state->callbacks.send_event = gyro_3d_proc_event; gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; gyro_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->callbacks); + &gyro_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); return ret; @@ -355,8 +350,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); } diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index d84e04e4b431..07c0f9011c63 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -356,7 +356,7 @@ static int mpu3050_read_raw(struct iio_dev *indio_dev, goto out_read_raw_unlock; } - *val = be16_to_cpu(raw_val); + *val = (s16)be16_to_cpu(raw_val); ret = IIO_VAL_INT; goto out_read_raw_unlock; diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index adfbbc0d37dc..c56331755b5c 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -97,15 +97,15 @@ static int st_gyro_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_gyro_id_table[] = { - { L3G4200D_GYRO_DEV_NAME }, - { LSM330D_GYRO_DEV_NAME }, - { LSM330DL_GYRO_DEV_NAME }, - { LSM330DLC_GYRO_DEV_NAME }, - { L3GD20_GYRO_DEV_NAME }, - { L3GD20H_GYRO_DEV_NAME }, - { L3G4IS_GYRO_DEV_NAME }, - { LSM330_GYRO_DEV_NAME }, - { LSM9DS0_GYRO_DEV_NAME }, + { .name = L3G4200D_GYRO_DEV_NAME }, + { .name = LSM330D_GYRO_DEV_NAME }, + { .name = LSM330DL_GYRO_DEV_NAME }, + { .name = LSM330DLC_GYRO_DEV_NAME }, + { .name = L3GD20_GYRO_DEV_NAME }, + { .name = L3GD20H_GYRO_DEV_NAME }, + { .name = L3G4IS_GYRO_DEV_NAME }, + { .name = LSM330_GYRO_DEV_NAME }, + { .name = LSM9DS0_GYRO_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_gyro_id_table); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index d358f4d5e5da..2a648532242a 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -544,10 +544,8 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4403_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, @@ -568,7 +566,7 @@ static int afe4403_probe(struct spi_device *spi) } static const struct spi_device_id afe4403_ids[] = { - { "afe4403", 0 }, + { .name = "afe4403" }, { } }; MODULE_DEVICE_TABLE(spi, afe4403_ids); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2357e2dd7017..22cd46eca217 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -551,10 +551,8 @@ static int afe4404_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4404_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 97352485f5e2..191540e10c20 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -489,10 +489,8 @@ static int max30100_probe(struct i2c_client *client) NULL, max30100_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30100_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 0eeaa378b10d..c37316c86f14 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -577,10 +577,8 @@ static int max30102_probe(struct i2c_client *client) NULL, max30102_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30102_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 1ae8702ada54..75971c19624a 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -843,8 +843,7 @@ static int hdc3020_probe(struct i2c_client *client) IRQF_ONESHOT, "hdc3020", indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, - "Failed to request IRQ\n"); + return ret; } ret = devm_iio_device_register(&data->client->dev, indio_dev); diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 5267a14d73ec..7cec81ff5685 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec humidity_channels[] = { /* Adjust channel real bits based on report descriptor */ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -54,8 +54,8 @@ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, } static int humidity_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) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -100,8 +100,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev, } static int humidity_write_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) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -126,7 +126,7 @@ static const struct iio_info humidity_info = { /* Callback handler to send event after all samples are received and captured */ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -140,8 +140,9 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -158,10 +159,10 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id */ static int humidity_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned int usage_id, - struct hid_humidity_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct hid_humidity_state *st) { int ret; @@ -214,13 +215,13 @@ static int hid_humidity_probe(struct platform_device *pdev) if (ret) return ret; - humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels, - sizeof(humidity_channels), GFP_KERNEL); + humid_chans = devm_kmemdup(&pdev->dev, humidity_channels, + sizeof(humidity_channels), GFP_KERNEL); if (!humid_chans) return -ENOMEM; ret = humidity_parse_report(pdev, hsdev, humid_chans, - HID_USAGE_SENSOR_HUMIDITY, humid_st); + HID_USAGE_SENSOR_HUMIDITY, humid_st); if (ret) return ret; @@ -233,7 +234,7 @@ static int hid_humidity_probe(struct platform_device *pdev) atomic_set(&humid_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &humid_st->common_attributes); + &humid_st->common_attributes); if (ret) return ret; @@ -241,7 +242,7 @@ static int hid_humidity_probe(struct platform_device *pdev) humidity_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humidity_callbacks); + &humidity_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 4d03db19063e..df12c7a25b2e 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -122,11 +122,8 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) hts221_trigger_handler_thread, irq_type | IRQF_ONESHOT, hw->name, hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger", iio_dev->name); diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index e6fef2acd523..f962842cc71d 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -47,7 +47,7 @@ static const struct of_device_id hts221_spi_of_match[] = { MODULE_DEVICE_TABLE(of, hts221_spi_of_match); static const struct spi_device_id hts221_spi_id_table[] = { - { HTS221_DEV_NAME }, + { .name = HTS221_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, hts221_spi_id_table); diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h index e1a56824e07f..4698a857739f 100644 --- a/drivers/iio/iio_core_trigger.h +++ b/drivers/iio/iio_core_trigger.h @@ -56,4 +56,4 @@ static inline int iio_trigger_detach_poll_func(struct iio_trigger *trig, return 0; } -#endif /* CONFIG_TRIGGER_CONSUMER */ +#endif /* CONFIG_IIO_TRIGGER */ diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 7e0181c27bb6..8bab4616be20 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -109,6 +109,7 @@ config KMX61 be called kmx61. source "drivers/iio/imu/inv_icm42600/Kconfig" +source "drivers/iio/imu/inv_icm42607/Kconfig" source "drivers/iio/imu/inv_icm45600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 13fb7846e9c9..3268dc2371ae 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o obj-y += inv_icm42600/ +obj-y += inv_icm42607/ obj-y += inv_icm45600/ obj-y += inv_mpu6050/ diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 36323ad149e0..4842346f9f0c 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1189,21 +1189,21 @@ static int adis16400_probe(struct spi_device *spi) } static const struct spi_device_id adis16400_id[] = { - { "adis16300", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16305", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16334", (kernel_ulong_t)&adis16334_chip_info }, - { "adis16350", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16354", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16355", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16360", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16362", (kernel_ulong_t)&adis16362_chip_info }, - { "adis16364", (kernel_ulong_t)&adis16364_chip_info }, - { "adis16365", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16367", (kernel_ulong_t)&adis16367_chip_info }, - { "adis16400", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16405", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16445", (kernel_ulong_t)&adis16445_chip_info }, - { "adis16448", (kernel_ulong_t)&adis16448_chip_info }, + { .name = "adis16300", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16305", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16334", .driver_data = (kernel_ulong_t)&adis16334_chip_info }, + { .name = "adis16350", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16354", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16355", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16360", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16362", .driver_data = (kernel_ulong_t)&adis16362_chip_info }, + { .name = "adis16364", .driver_data = (kernel_ulong_t)&adis16364_chip_info }, + { .name = "adis16365", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16367", .driver_data = (kernel_ulong_t)&adis16367_chip_info }, + { .name = "adis16400", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16405", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16445", .driver_data = (kernel_ulong_t)&adis16445_chip_info }, + { .name = "adis16448", .driver_data = (kernel_ulong_t)&adis16448_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adis16400_id); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index ba1887d36577..cadf21c05bbf 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -394,7 +394,7 @@ static int adis16460_probe(struct spi_device *spi) } static const struct spi_device_id adis16460_ids[] = { - { "adis16460", 0 }, + { .name = "adis16460" }, { } }; MODULE_DEVICE_TABLE(spi, adis16460_ids); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 17335386d8e3..5f21dceda8f7 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -2061,33 +2061,33 @@ static const struct of_device_id adis16475_of_match[] = { MODULE_DEVICE_TABLE(of, adis16475_of_match); static const struct spi_device_id adis16475_ids[] = { - { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, - { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, - { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, - { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, - { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, - { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, - { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, - { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, - { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, - { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, - { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, - { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, - { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, - { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, - { "adis16501", (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, - { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, - { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, - { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, - { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, - { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, - { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, - { "adis16575-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, - { "adis16575-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, - { "adis16576-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, - { "adis16576-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, - { "adis16577-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, - { "adis16577-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, + { .name = "adis16470", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, + { .name = "adis16475-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, + { .name = "adis16475-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, + { .name = "adis16475-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, + { .name = "adis16477-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, + { .name = "adis16477-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, + { .name = "adis16477-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, + { .name = "adis16465-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, + { .name = "adis16465-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, + { .name = "adis16465-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, + { .name = "adis16467-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, + { .name = "adis16467-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, + { .name = "adis16467-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, + { .name = "adis16500", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, + { .name = "adis16501", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, + { .name = "adis16505-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, + { .name = "adis16505-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, + { .name = "adis16505-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, + { .name = "adis16507-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, + { .name = "adis16507-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, + { .name = "adis16507-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, + { .name = "adis16575-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, + { .name = "adis16575-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, + { .name = "adis16576-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, + { .name = "adis16576-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, + { .name = "adis16577-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, + { .name = "adis16577-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, { } }; MODULE_DEVICE_TABLE(spi, adis16475_ids); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index e009f3824768..5187566c1876 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -110,7 +110,7 @@ #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) -/* Each filter coefficent bank spans two pages */ +/* Each filter coefficient bank spans two pages */ #define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ ADIS16480_REG((page) + 1, (x) - 60 + 8)) #define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) @@ -1467,7 +1467,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p) * We need to perform the padding to have the buffer * elements naturally aligned in case there are any * 32-bit storage size channels enabled which are added - * in the buffer after the temprature data. In case + * in the buffer after the temperature data. In case * there is no data being added after the temperature * data, the padding is harmless. */ @@ -1806,26 +1806,26 @@ static int adis16480_probe(struct spi_device *spi) } static const struct spi_device_id adis16480_ids[] = { - { "adis16375", ADIS16375 }, - { "adis16480", ADIS16480 }, - { "adis16485", ADIS16485 }, - { "adis16486", ADIS16486 }, - { "adis16487", ADIS16487 }, - { "adis16488", ADIS16488 }, - { "adis16489", ADIS16489 }, - { "adis16490", ADIS16490 }, - { "adis16495-1", ADIS16495_1 }, - { "adis16495-2", ADIS16495_2 }, - { "adis16495-3", ADIS16495_3 }, - { "adis16497-1", ADIS16497_1 }, - { "adis16497-2", ADIS16497_2 }, - { "adis16497-3", ADIS16497_3 }, - { "adis16545-1", ADIS16545_1 }, - { "adis16545-2", ADIS16545_2 }, - { "adis16545-3", ADIS16545_3 }, - { "adis16547-1", ADIS16547_1 }, - { "adis16547-2", ADIS16547_2 }, - { "adis16547-3", ADIS16547_3 }, + { .name = "adis16375", .driver_data = ADIS16375 }, + { .name = "adis16480", .driver_data = ADIS16480 }, + { .name = "adis16485", .driver_data = ADIS16485 }, + { .name = "adis16486", .driver_data = ADIS16486 }, + { .name = "adis16487", .driver_data = ADIS16487 }, + { .name = "adis16488", .driver_data = ADIS16488 }, + { .name = "adis16489", .driver_data = ADIS16489 }, + { .name = "adis16490", .driver_data = ADIS16490 }, + { .name = "adis16495-1", .driver_data = ADIS16495_1 }, + { .name = "adis16495-2", .driver_data = ADIS16495_2 }, + { .name = "adis16495-3", .driver_data = ADIS16495_3 }, + { .name = "adis16497-1", .driver_data = ADIS16497_1 }, + { .name = "adis16497-2", .driver_data = ADIS16497_2 }, + { .name = "adis16497-3", .driver_data = ADIS16497_3 }, + { .name = "adis16545-1", .driver_data = ADIS16545_1 }, + { .name = "adis16545-2", .driver_data = ADIS16545_2 }, + { .name = "adis16545-3", .driver_data = ADIS16545_3 }, + { .name = "adis16547-1", .driver_data = ADIS16547_1 }, + { .name = "adis16547-2", .driver_data = ADIS16547_2 }, + { .name = "adis16547-3", .driver_data = ADIS16547_3 }, { } }; MODULE_DEVICE_TABLE(spi, adis16480_ids); diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1e435d60cd6d..096a612865c4 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -88,25 +88,7 @@ struct adis16550_sync { u16 max_rate; }; -struct adis16550_chip_info { - const struct iio_chan_spec *channels; - const struct adis16550_sync *sync_mode; - char *name; - u32 num_channels; - u32 gyro_max_val; - u32 gyro_max_scale; - u32 accel_max_val; - u32 accel_max_scale; - u32 temp_scale; - u32 deltang_max_val; - u32 deltvel_max_val; - u32 int_clk; - u16 max_dec; - u16 num_sync; -}; - struct adis16550 { - const struct adis16550_chip_info *info; struct adis adis; unsigned long clk_freq_hz; u32 sync_mode; @@ -449,8 +431,8 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) * The optimal sample rate for the supported IMUs is between * int_clk - 1000 and int_clk + 500. */ - u32 max_sample_rate = st->info->int_clk * 1000 + 500000; - u32 min_sample_rate = st->info->int_clk * 1000 - 1000000; + u32 max_sample_rate = 4000 * 1000 + 500000; + u32 min_sample_rate = 4000 * 1000 - 1000000; if (!freq_hz) return -EINVAL; @@ -483,7 +465,7 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) if (dec) dec--; - dec = min(dec, st->info->max_dec); + dec = min(dec, 4095); return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec); } @@ -591,30 +573,30 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - *val = st->info->gyro_max_val; - *val2 = st->info->gyro_max_scale; + *val = 1; + *val2 = IIO_RAD_TO_DEGREE(80 << 16); return IIO_VAL_FRACTIONAL; case IIO_ACCEL: - *val = st->info->accel_max_val; - *val2 = st->info->accel_max_scale; + *val = 1; + *val2 = IIO_M_S_2_TO_G(102400000); return IIO_VAL_FRACTIONAL; case IIO_TEMP: - *val = st->info->temp_scale; + *val = 4; return IIO_VAL_INT; case IIO_DELTA_ANGL: - *val = st->info->deltang_max_val; + *val = IIO_DEGREE_TO_RAD(720); *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; case IIO_DELTA_VELOCITY: - *val = st->info->deltvel_max_val; + *val = 125; *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - /* temperature centered at 25°C */ - *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale); + /* temperature centered at 25°C divided by temp scale */ + *val = 25000 / 4; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBBIAS: ret = adis_read_reg_32(&st->adis, @@ -792,23 +774,6 @@ static const struct adis16550_sync adis16550_sync_modes[] = { { ADIS16550_SYNC_MODE_SCALED, 1, 128 }, }; -static const struct adis16550_chip_info adis16550_chip_info = { - .num_channels = ARRAY_SIZE(adis16550_channels), - .channels = adis16550_channels, - .name = "adis16550", - .gyro_max_val = 1, - .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16), - .accel_max_val = 1, - .accel_max_scale = IIO_M_S_2_TO_G(102400000), - .temp_scale = 4, - .deltang_max_val = IIO_DEGREE_TO_RAD(720), - .deltvel_max_val = 125, - .int_clk = 4000, - .max_dec = 4095, - .sync_mode = adis16550_sync_modes, - .num_sync = ARRAY_SIZE(adis16550_sync_modes), -}; - static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem) { int i; @@ -917,21 +882,21 @@ static int adis16550_config_sync(struct adis16550 *st) if (IS_ERR(clk)) return PTR_ERR(clk); if (!clk) { - st->clk_freq_hz = st->info->int_clk * 1000; + st->clk_freq_hz = 4000000; return 0; } st->clk_freq_hz = clk_get_rate(clk); - for (i = 0; i < st->info->num_sync; i++) { - if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate && - st->clk_freq_hz <= st->info->sync_mode[i].max_rate) { - sync_mode_data = &st->info->sync_mode[i]; + for (i = 0; i < ARRAY_SIZE(adis16550_sync_modes); i++) { + if (st->clk_freq_hz >= adis16550_sync_modes[i].min_rate && + st->clk_freq_hz <= adis16550_sync_modes[i].max_rate) { + sync_mode_data = &adis16550_sync_modes[i]; break; } } - if (i == st->info->num_sync) + if (i == ARRAY_SIZE(adis16550_sync_modes)) return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range", st->clk_freq_hz); @@ -942,7 +907,7 @@ static int adis16550_config_sync(struct adis16550 *st) * of [3000 4500]. */ - sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz); + sync_scale = DIV_ROUND_CLOSEST(4000, st->clk_freq_hz); if (3000 > sync_scale || 4500 < sync_scale) return dev_err_probe(dev, -EINVAL, @@ -954,7 +919,7 @@ static int adis16550_config_sync(struct adis16550 *st) if (ret) return ret; - st->clk_freq_hz = st->info->int_clk; + st->clk_freq_hz = 4000; } st->clk_freq_hz *= 1000; @@ -1063,13 +1028,11 @@ static int adis16550_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - st->info = spi_get_device_match_data(spi); - if (!st->info) - return -EINVAL; + adis = &st->adis; - indio_dev->name = st->info->name; - indio_dev->channels = st->info->channels; - indio_dev->num_channels = st->info->num_channels; + indio_dev->name = "adis16550"; + indio_dev->channels = adis16550_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16550_channels); indio_dev->available_scan_masks = adis16550_channel_masks; indio_dev->info = &adis16550_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1116,13 +1079,13 @@ static int adis16550_probe(struct spi_device *spi) } static const struct spi_device_id adis16550_id[] = { - { "adis16550", (kernel_ulong_t)&adis16550_chip_info}, + { .name = "adis16550" }, { } }; MODULE_DEVICE_TABLE(spi, adis16550_id); static const struct of_device_id adis16550_of_match[] = { - { .compatible = "adi,adis16550", .data = &adis16550_chip_info }, + { .compatible = "adi,adis16550" }, { } }; MODULE_DEVICE_TABLE(of, adis16550_of_match); diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 86f6ecfd64aa..1e73a7f1f017 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -5,7 +5,7 @@ * Copyright (c) 2016, Intel Corporation. * Copyright (c) 2019, Martin Kelly. * - * IIO core driver for BMI160, with support for I2C/SPI busses + * IIO core driver for BMI160, with support for I2C/SPI buses * * TODO: magnetometer, hardware FIFO */ diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index 2f0a578ee40f..107eb3374922 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -34,8 +34,8 @@ static int bmi160_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi160_spi_id[] = { - {"bmi120", 0}, - {"bmi160", 0}, + { .name = "bmi120" }, + { .name = "bmi160" }, { } }; MODULE_DEVICE_TABLE(spi, bmi160_spi_id); diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 2ad230788532..fbcf6bb87acc 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -1434,7 +1434,7 @@ static int bmi270_trigger_probe(struct bmi270_data *data, bmi270_irq_thread_handler, IRQF_ONESHOT, "bmi270-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index dc7fa01421bc..759b56b59e54 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -65,8 +65,8 @@ static int bmi270_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi270_spi_id[] = { - { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, - { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { .name = "bmi260", .driver_data = (kernel_ulong_t)&bmi260_chip_info }, + { .name = "bmi270", .driver_data = (kernel_ulong_t)&bmi270_chip_info }, { } }; @@ -75,6 +75,7 @@ static const struct of_device_id bmi270_of_match[] = { { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, { } }; +MODULE_DEVICE_TABLE(of, bmi270_of_match); static struct spi_driver bmi270_spi_driver = { .driver = { diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index f3d499423399..ebeb1b10c38c 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -1128,7 +1128,7 @@ static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct bmi323_data *data = iio_priv(indio_dev); - val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES); + val = min(val, BMI323_FIFO_FULL_IN_FRAMES); guard(mutex)(&data->mutex); data->watermark = val; @@ -1946,7 +1946,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data, bmi323_irq_thread_handler, IRQF_ONESHOT, "bmi323-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c index 92f1c4bcc192..ed0235cc40da 100644 --- a/drivers/iio/imu/bmi323/bmi323_spi.c +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -64,7 +64,7 @@ static int bmi323_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi323_spi_ids[] = { - { "bmi323" }, + { .name = "bmi323" }, { } }; MODULE_DEVICE_TABLE(spi, bmi323_spi_ids); diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c index 281ebfd9c15a..9d2eb6fbab4a 100644 --- a/drivers/iio/imu/fxos8700_core.c +++ b/drivers/iio/imu/fxos8700_core.c @@ -2,7 +2,7 @@ /* * FXOS8700 - NXP IMU (accelerometer plus magnetometer) * - * IIO core driver for FXOS8700, with support for I2C/SPI busses + * IIO core driver for FXOS8700, with support for I2C/SPI buses * * TODO: Buffer, trigger, and IRQ support */ diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c index 3edf90220bfa..56ad0c5914d9 100644 --- a/drivers/iio/imu/fxos8700_spi.c +++ b/drivers/iio/imu/fxos8700_spi.c @@ -23,7 +23,7 @@ static int fxos8700_spi_probe(struct spi_device *spi) } static const struct spi_device_id fxos8700_spi_id[] = { - {"fxos8700", 0}, + { .name = "fxos8700" }, { } }; MODULE_DEVICE_TABLE(spi, fxos8700_spi_id); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index c8b48a5c5ed0..b55d993f0264 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -6,13 +6,15 @@ #ifndef INV_ICM42600_H_ #define INV_ICM42600_H_ -#include #include -#include +#include #include -#include #include +#include +#include + #include + #include #include "inv_icm42600_buffer.h" @@ -354,6 +356,8 @@ struct inv_icm42600_sensor_state { cpu_to_le16((_wm) & GENMASK(11, 0)) /* FIFO is 2048 bytes, let 12 samples for reading latency */ #define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16) +/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */ +#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232 #define INV_ICM42600_REG_INT_CONFIG1 0x0064 #define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 7df920ef3cf0..4b0e3cd8a506 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -3,25 +3,26 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include +#include +#include #include #include #include -#include -#include -#include #include #include -#include #include #include #include +#include + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ @@ -1186,8 +1187,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 5c3840acf085..998d312f7bde 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -3,17 +3,20 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include #include #include #include #include -#include +#include #include -#include #include +#include + +#include #include "inv_icm42600.h" #include "inv_icm42600_buffer.h" @@ -186,7 +189,7 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, * smallest latency but this is not as simple as choosing the smallest watermark * value. Latency depends on watermark and ODR. It requires several steps: * 1) compute gyro and accel latencies and choose the smallest value. - * 2) adapt the choosen latency so that it is a multiple of both gyro and accel + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel * ones. Otherwise it is possible that you don't meet a requirement. (for * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the * value of 4 will not meet accel latency requirement because 6 is not a @@ -438,6 +441,40 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .postdisable = inv_icm42600_buffer_postdisable, }; +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + unsigned int wm; + + guard(mutex)(&st->lock); + + if (indio_dev == st->indio_accel) + wm = st->fifo.watermark.eff_accel; + else if (indio_dev == st->indio_gyro) + wm = st->fifo.watermark.eff_gyro; + else + return -EINVAL; + + return sysfs_emit(buf, "%u\n", wm); +} + +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1"); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, + __stringify(INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES)); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_enabled, "1"); + +const struct iio_dev_attr *inv_icm42600_buffer_attrs[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, unsigned int max) { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index 88b8b9f780af..e37ee3416337 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -6,8 +6,8 @@ #ifndef INV_ICM42600_BUFFER_H_ #define INV_ICM42600_BUFFER_H_ -#include #include +#include struct inv_icm42600_state; @@ -80,6 +80,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; +extern const struct iio_dev_attr *inv_icm42600_buffer_attrs[]; int inv_icm42600_buffer_init(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 76eb22488e5f..dc97d8a274e3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -3,18 +3,18 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include -#include -#include -#include #include -#include +#include #include #include -#include +#include +#include +#include #include #include #include +#include +#include #include diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index a18dcac93929..253bf571439d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -3,22 +3,23 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include +#include #include #include #include -#include -#include #include -#include #include #include +#include + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ @@ -772,8 +773,9 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 1013aff4f0ab..28552d2db91d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include #include -#include #include -#include +#include +#include #include +#include #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 57e3c448dca1..faf743bc6444 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include #include +#include #include -#include -#include #include +#include +#include #include "inv_icm42600.h" @@ -71,13 +71,13 @@ static int inv_icm42600_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_icm42600_id[] = { - { "icm42600", INV_CHIP_ICM42600 }, - { "icm42602", INV_CHIP_ICM42602 }, - { "icm42605", INV_CHIP_ICM42605 }, - { "icm42686", INV_CHIP_ICM42686 }, - { "icm42622", INV_CHIP_ICM42622 }, - { "icm42688", INV_CHIP_ICM42688 }, - { "icm42631", INV_CHIP_ICM42631 }, + { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, + { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, + { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, + { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm42600_id); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 727b03d541a5..7f80bda471c3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -3,11 +3,12 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include #include +#include #include #include #include + #include #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig new file mode 100644 index 000000000000..23f461f57afc --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config INV_ICM42607 + tristate + select IIO_BUFFER + select IIO_INV_SENSORS_TIMESTAMP + +config INV_ICM42607_I2C + tristate "InvenSense ICM-42607 I2C driver" + depends on I2C + select INV_ICM42607 + select REGMAP_I2C + help + This driver supports the InvenSense ICM-42607 motion tracking + device over I2C. + + This driver can be built as a module. The module will be called + inv-icm42607-i2c. + +config INV_ICM42607_SPI + tristate "InvenSense ICM-42607 SPI driver" + depends on SPI_MASTER + select INV_ICM42607 + select REGMAP_SPI + help + This driver supports the InvenSense ICM-42607 motion tracking + device over SPI. + + This driver can be built as a module. The module will be called + inv-icm42607-spi. diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile new file mode 100644 index 000000000000..7b907e019601 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o +inv-icm42607-y += inv_icm42607_core.o +inv-icm42607-y += inv_icm42607_gyro.o +inv-icm42607-y += inv_icm42607_accel.o +inv-icm42607-y += inv_icm42607_temp.o + +obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o +inv-icm42607-i2c-y += inv_icm42607_i2c.o + +obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o +inv-icm42607-spi-y += inv_icm42607_spi.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h new file mode 100644 index 000000000000..4d51b0da1aa1 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_H_ +#define INV_ICM42607_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Serial bus slew rates. Rates are expressed as range between the two + * values with the midpoint as the typical rate. For the final value of + * 2ns, 2ns is considered the max value with no expressed minimum or + * typical value. + */ +enum inv_icm42607_slew_rate { + INV_ICM42607_SLEW_RATE_20_60NS = 0, + INV_ICM42607_SLEW_RATE_12_36NS = 1, + INV_ICM42607_SLEW_RATE_6_19NS = 2, + INV_ICM42607_SLEW_RATE_4_14NS = 3, + INV_ICM42607_SLEW_RATE_2_6NS = 4, + INV_ICM42607_SLEW_RATE_2NS = 5, + INV_ICM42607_SLEW_RATE_NB +}; + +enum inv_icm42607_sensor_mode { + INV_ICM42607_SENSOR_MODE_OFF = 0, + INV_ICM42607_SENSOR_MODE_STANDBY = 1, + INV_ICM42607_SENSOR_MODE_LOW_POWER = 2, + INV_ICM42607_SENSOR_MODE_LOW_NOISE = 3, + INV_ICM42607_SENSOR_MODE_NB +}; + +/* gyroscope fullscale values */ +enum inv_icm42607_gyro_fs { + INV_ICM42607_GYRO_FS_2000DPS = 0, + INV_ICM42607_GYRO_FS_1000DPS = 1, + INV_ICM42607_GYRO_FS_500DPS = 2, + INV_ICM42607_GYRO_FS_250DPS = 3, + INV_ICM42607_GYRO_FS_NB +}; + +/* accelerometer fullscale values */ +enum inv_icm42607_accel_fs { + INV_ICM42607_ACCEL_FS_16G = 0, + INV_ICM42607_ACCEL_FS_8G = 1, + INV_ICM42607_ACCEL_FS_4G = 2, + INV_ICM42607_ACCEL_FS_2G = 3, + INV_ICM42607_ACCEL_FS_NB +}; + +/* ODR values - Note Gyro does not support ODR less than 12.5Hz */ +enum inv_icm42607_odr { + INV_ICM42607_ODR_1600HZ = 5, + INV_ICM42607_ODR_800HZ = 6, + INV_ICM42607_ODR_400HZ = 7, + INV_ICM42607_ODR_200HZ = 8, + INV_ICM42607_ODR_100HZ = 9, + INV_ICM42607_ODR_50HZ = 10, + INV_ICM42607_ODR_25HZ = 11, + INV_ICM42607_ODR_12_5HZ = 12, + INV_ICM42607_ODR_6_25HZ_LP = 13, + INV_ICM42607_ODR_3_125HZ_LP = 14, + INV_ICM42607_ODR_1_5625HZ_LP = 15, + INV_ICM42607_ODR_NB +}; + +/* Low-Noise mode sensor data filter (bandwidth) */ +enum inv_icm42607_filter_bw { + INV_ICM42607_FILTER_BYPASS = 0, + INV_ICM42607_FILTER_BW_180HZ = 1, + INV_ICM42607_FILTER_BW_121HZ = 2, + INV_ICM42607_FILTER_BW_73HZ = 3, + INV_ICM42607_FILTER_BW_53HZ = 4, + INV_ICM42607_FILTER_BW_34HZ = 5, + INV_ICM42607_FILTER_BW_25HZ = 6, + INV_ICM42607_FILTER_BW_16HZ = 7, + INV_ICM42607_FILTER_BW_NB +}; + +/* Low-Power mode sensor data filter (averaging) */ +enum inv_icm42607_filter_avg { + INV_ICM42607_FILTER_AVG_2X = 0, + INV_ICM42607_FILTER_AVG_4X = 1, + INV_ICM42607_FILTER_AVG_8X = 2, + INV_ICM42607_FILTER_AVG_16X = 3, + INV_ICM42607_FILTER_AVG_32X = 4, + INV_ICM42607_FILTER_AVG_64X = 5, + /* values 6 and 7 also correspond to 64x. */ +}; + +/* Temperature sensor data filter (bandwidth) */ +enum inv_icm42607_temp_filter_bw { + INV_ICM42607_TEMP_FILTER_BYPASS = 0, + INV_ICM42607_TEMP_FILTER_BW_180HZ = 1, + INV_ICM42607_TEMP_FILTER_BW_72HZ = 2, + INV_ICM42607_TEMP_FILTER_BW_34HZ = 3, + INV_ICM42607_TEMP_FILTER_BW_16HZ = 4, + INV_ICM42607_TEMP_FILTER_BW_8HZ = 5, + INV_ICM42607_TEMP_FILTER_BW_4HZ = 6, + /* value 7 also corresponds to 4Hz */ +}; + +/* Signed so that negative values can signify an invalid condition. */ +struct inv_icm42607_sensor_conf { + int mode; + int fs; + int odr; + int filter; +}; +#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 } + +struct inv_icm42607_conf { + struct inv_icm42607_sensor_conf gyro; + struct inv_icm42607_sensor_conf accel; + ktime_t gyro_stop; /* earliest time to stop the gyro */ +}; + +struct inv_icm42607_hw { + const char *name; + const struct inv_icm42607_conf *conf; + u8 whoami; +}; + +/** + * struct inv_icm42607_state - driver state variables + * @hw: Hardware specific data. + * @lock: lock for serializing multiple registers access. + * @map: regmap pointer. + * @indio_accel: accelerometer IIO device. + * @indio_gyro: gyroscope IIO device. + * @vddio_supply: I/O voltage regulator for the chip. + * @vddio_en: I/O voltage status for runtime PM. + * @conf: chip sensors configurations. + * @orientation: sensor chip orientation relative to main hardware. + */ +struct inv_icm42607_state { + const struct inv_icm42607_hw *hw; + struct mutex lock; + struct regmap *map; + struct iio_dev *indio_accel; + struct iio_dev *indio_gyro; + struct regulator *vddio_supply; + bool vddio_en; + struct inv_icm42607_conf conf; + struct iio_mount_matrix orientation; +}; + +/** + * struct inv_icm42607_sensor_state - sensor state variables + * @power_mode: sensor requested power mode (for common frequencies) + * @filter: sensor filter. + */ +struct inv_icm42607_sensor_state { + enum inv_icm42607_sensor_mode power_mode; + int filter; +}; + +/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */ + +/* Register Map for User Bank 0 */ +#define INV_ICM42607_REG_MCLK_RDY 0x00 + +#define INV_ICM42607_REG_DEVICE_CONFIG 0x01 +#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2) +#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0) + +#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02 +#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4) +#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2) + +#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03 +#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04 +#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05 +#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_CONFIG 0x06 +#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5) +#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00 +#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2) +#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00 + +/* all sensor data are 16 bits (2 registers wide) in big-endian */ +#define INV_ICM42607_REG_TEMP_DATA1 0x09 +#define INV_ICM42607_REG_TEMP_DATA0 0x0A +#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B +#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C +#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D +#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E +#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F +#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10 +#define INV_ICM42607_REG_GYRO_DATA_X1 0x11 +#define INV_ICM42607_REG_GYRO_DATA_X0 0x12 +#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13 +#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14 +#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15 +#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16 +#define INV_ICM42607_DATA_INVALID -32768 + +#define INV_ICM42607_REG_TMST_FSYNCH 0x17 +#define INV_ICM42607_REG_TMST_FSYNCL 0x18 + +/* APEX Data Registers */ +#define INV_ICM42607_REG_APEX_DATA0 0x31 +#define INV_ICM42607_REG_APEX_DATA1 0x32 +#define INV_ICM42607_REG_APEX_DATA2 0x33 +#define INV_ICM42607_REG_APEX_DATA3 0x34 +#define INV_ICM42607_REG_APEX_DATA4 0x1D +#define INV_ICM42607_REG_APEX_DATA5 0x1E + +#define INV_ICM42607_REG_PWR_MGMT0 0x1F +#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7) +#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4) +#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2) +#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_GYRO_CONFIG0 0x20 +#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21 +#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_TEMP_CONFIG0 0x22 +#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4) + +#define INV_ICM42607_REG_GYRO_CONFIG1 0x23 +#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24 +#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4) +#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_APEX_CONFIG0 0x25 +#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3) +#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2) +#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0) + +#define INV_ICM42607_REG_APEX_CONFIG1 0x26 +#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6) +#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5) +#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4) +#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3) +#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_WOM_CONFIG 0x27 +#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3) +#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2) +#define INV_ICM42607_WOM_CONFIG_MODE BIT(1) +#define INV_ICM42607_WOM_CONFIG_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG1 0x28 +#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1) +#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG2 0x29 +#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A +#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \ + cpu_to_le16((_wm) & GENMASK(11, 0)) +/* FIFO is 2048 bytes, let 12 samples for reading latency */ +#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16) +#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8 +#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16 + +#define INV_ICM42607_REG_INT_SOURCE0 0x2B +#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7) +#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5) +#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4) +#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2) +#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1) +#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE1 0x2C +#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_SOURCE3 0x2D +#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7) +#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5) +#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4) +#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE4 0x2E +#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F +#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30 + +#define INV_ICM42607_REG_INTF_CONFIG0 0x35 +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6) +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5) +#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS 2 +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS 3 + +#define INV_ICM42607_REG_INTF_CONFIG1 0x36 +#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3) +#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2 + +#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39 +#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS 0x3A +#define INV_ICM42607_INT_STATUS_ST BIT(7) +#define INV_ICM42607_INT_STATUS_FSYNC BIT(6) +#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5) +#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4) +#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2) +#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1) +#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS2 0x3B +#define INV_ICM42607_INT_STATUS2_SMD BIT(3) +#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_STATUS3 0x3C +#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5) +#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4) +#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3) +#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2) + +/* + * FIFO access registers + * FIFO count is 16 bits (2 registers) big-endian + * FIFO data is a continuous read register to read FIFO content + */ +#define INV_ICM42607_REG_FIFO_COUNTH 0x3D +#define INV_ICM42607_REG_FIFO_COUNTL 0x3E +#define INV_ICM42607_REG_FIFO_DATA 0x3F + +#define INV_ICM42607_REG_WHOAMI 0x75 +#define INV_ICM42607P_WHOAMI 0x60 +#define INV_ICM42607_WHOAMI 0x67 + +/* + * Timings as listed in section 3 of datasheet, all values listed in datasheet + * in ms except temp startup time... setting all values in us and using + * USEC_PER_MSEC to convert from values displayed in datasheet. + */ +#define INV_ICM42607_POWER_UP_TIME_US (100 * USEC_PER_MSEC) +#define INV_ICM42607_RESET_TIME_US (1 * USEC_PER_MSEC) +#define INV_ICM42607_ACCEL_STARTUP_TIME_US (10 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STARTUP_TIME_US (30 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC) +#define INV_ICM42607_TEMP_STARTUP_TIME_US 77 + +/* + * Suspend delay assumed from other icm42600 series device, not + * documented in datasheet. + */ +#define INV_ICM42607_SUSPEND_DELAY_MS (2 * MSEC_PER_SEC) + +typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *); + +extern const struct regmap_config inv_icm42607_regmap_config; +extern const struct inv_icm42607_hw inv_icm42607_hw_data; +extern const struct inv_icm42607_hw inv_icm42607p_hw_data; +extern const struct dev_pm_ops inv_icm42607_pm_ops; + +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel); + +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type); + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val); + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup bus_setup); + +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st); + +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st); + +#endif diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file mode 100644 index 000000000000..0b3f035c2da0 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .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_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_accel_scan { + INV_ICM42607_ACCEL_SCAN_X, + INV_ICM42607_ACCEL_SCAN_Y, + INV_ICM42607_ACCEL_SCAN_Z, + INV_ICM42607_ACCEL_SCAN_TEMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_accel_channels[] = { + INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z, + inv_icm42607_accel_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP), +}; + +static const int inv_icm42607_accel_scale_nano[][2] = { + [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 }, + [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 }, + [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 }, + [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 }, +}; + +static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.accel.fs; + + *val = inv_icm42607_accel_scale_nano[idx][0]; + *val2 = inv_icm42607_accel_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_accel_scale_nano[idx][0] && + val2 == inv_icm42607_accel_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_accel_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, + [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 }, + [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 }, + [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 }, +}; + +static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.accel.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_accel_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + *val = inv_icm42607_accel_odr[i][0]; + *val2 = inv_icm42607_accel_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx++) { + if (val == inv_icm42607_accel_odr[idx][0] && + val2 == inv_icm42607_accel_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ACCEL: + break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_accel_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_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: + if (chan->type != IIO_ACCEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_accel_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_accel_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_accel_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + ret = inv_icm42607_accel_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_accel_info = { + .read_raw = inv_icm42607_accel_read_raw, + .read_avail = inv_icm42607_accel_read_avail, + .write_raw = inv_icm42607_accel_write_raw, + .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + struct inv_icm42607_sensor_state *accel_st; + struct iio_dev *indio_dev; + const char *name; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + accel_st = iio_priv(indio_dev); + accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + accel_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_accel_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_accel_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file mode 100644 index 000000000000..190e998f7b8e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "inv_icm42607.h" + +static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_TMST_FSYNCL: + case INV_ICM42607_REG_APEX_DATA4 ... INV_ICM42607_REG_INTF_CONFIG1: + case INV_ICM42607_REG_INT_STATUS_DRDY ... INV_ICM42607_REG_FIFO_DATA: + case INV_ICM42607_REG_WHOAMI: + return true; + } + + return false; +} + +static bool inv_icm42607_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_DEVICE_CONFIG ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_PWR_MGMT0 ... INV_ICM42607_REG_INT_SOURCE4: + case INV_ICM42607_REG_INTF_CONFIG0 ... INV_ICM42607_REG_INTF_CONFIG1: + return true; + } + + return false; +} + +static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY: + case INV_ICM42607_REG_SIGNAL_PATH_RESET: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5: + case INV_ICM42607_REG_APEX_CONFIG0: + case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3: + case INV_ICM42607_REG_INT_STATUS_DRDY: + case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA: + return true; + } + + return false; +} + +const struct regmap_config inv_icm42607_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = inv_icm42607_is_writeable_reg, + .readable_reg = inv_icm42607_is_readable_reg, + .volatile_reg = inv_icm42607_is_volatile_reg, + .max_register = INV_ICM42607_REG_WHOAMI, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607"); + +/* chip initial default configuration */ +static const struct inv_icm42607_conf inv_icm42607_default_conf = { + .gyro = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_GYRO_FS_1000DPS, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, + .accel = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_ACCEL_FS_4G, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, +}; + +const struct inv_icm42607_hw inv_icm42607_hw_data = { + .whoami = INV_ICM42607_WHOAMI, + .name = "icm42607", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607"); + +const struct inv_icm42607_hw inv_icm42607p_hw_data = { + .whoami = INV_ICM42607P_WHOAMI, + .name = "icm42607p", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607"); + +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + + return &st->orientation; +} + +static u32 inv_icm42607_odr_to_period_us(enum inv_icm42607_odr odr) +{ + static const u32 odr_periods[INV_ICM42607_ODR_NB] = { + /* Reserved values */ + 0, 0, 0, 0, 0, + /* 1600Hz */ + 625, + /* 800Hz */ + 1250, + /* 400Hz */ + 2500, + /* 200Hz */ + 5000, + /* 100 Hz */ + 10000, + /* 50Hz */ + 20000, + /* 25Hz */ + 40000, + /* 12.5Hz */ + 80000, + /* 6.25Hz */ + 160000, + /* 3.125Hz */ + 320000, + /* 1.5625Hz */ + 640000, + }; + + odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP); + + return odr_periods[odr]; +} + +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + *gyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + *accel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + + return 0; +} + +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode gyro, + enum inv_icm42607_sensor_mode accel) +{ + enum inv_icm42607_sensor_mode oldaccel, oldgyro; + unsigned int sleepval_us, odr_us; + unsigned int val; + s64 disable_wait; + int ret; + + ret = inv_icm42607_get_pwr_mgmt0(st, &oldgyro, &oldaccel); + if (ret) + return ret; + + if (gyro == oldgyro && accel == oldaccel) + return 0; + + /* + * Datasheet on page 14.26 says we need to ensure the gyro sensor is on + * for a minimum of 45ms. So if we transition from an on state to an + * off state make sure at least 45ms have passed before power off and + * wait if it hasn't. In case some platforms don't respond well to a + * sleep of 0, make sure the fsleep duration is > 0. + */ + if (!gyro && oldgyro) { + disable_wait = clamp(ktime_us_delta(st->conf.gyro_stop, ktime_get()), + 0, INV_ICM42607_GYRO_STOP_TIME_US); + + if (disable_wait > 0) + fsleep(disable_wait); + } + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro) | + FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + /* + * If a state change occurs from off to on, sleep for the startup time + * of the sensor. Since more than one sensor can be transitioned from + * off to on, select the maximum time from each of the sensors changing + * from off to on. Also account for the time it takes for the first + * sample to be ready by looking up the odr value and adding it to the + * sleep time. The startup time for the temp sensor is considerably + * smaller than the startup time for the other sensors and one or more + * are required to be on for the temp sensor to function, so any start + * delay should be enough. + */ + sleepval_us = 0; + odr_us = 0; + if (accel && !oldaccel) { + sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.accel.odr)); + } + + if (gyro && !oldgyro) { + sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.gyro.odr)); + /* Track the earliest we can turn off the gyroscope. */ + st->conf.gyro_stop = ktime_add_us(ktime_get(), + INV_ICM42607_GYRO_STOP_TIME_US); + } + + sleepval_us += odr_us; + + /* + * Only sleep if sleepval_us is greater than 0 in case some platforms + * have issues with a 0 delay. The 0 delay can happen if one or both + * sensors is shut down. + */ + if (sleepval_us > 0) + fsleep(sleepval_us); + + return 0; +} + +/* + * Sanity test between old and new config values, and note if we need + * to update register config0 or config1. + */ +static void inv_icm42607_update_config(struct inv_icm42607_sensor_conf *conf, + struct inv_icm42607_sensor_conf *oldconf, + bool *config0, bool *config1) +{ + if (conf->mode < 0) + conf->mode = oldconf->mode; + if (conf->fs < 0) + conf->fs = oldconf->fs; + if (conf->odr < 0) + conf->odr = oldconf->odr; + if (conf->filter < 0) + conf->filter = oldconf->filter; + + *config0 = (conf->fs != oldconf->fs) || (conf->odr != oldconf->odr); + + *config1 = (conf->filter != oldconf->filter); +} + +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type) +{ + enum inv_icm42607_sensor_mode accel_mode, gyro_mode; + struct inv_icm42607_sensor_conf *oldconf; + bool config0, config1; + unsigned int val; + int ret; + + switch (chan_type) { + case IIO_ACCEL: + oldconf = &st->conf.accel; + break; + case IIO_ANGL_VEL: + oldconf = &st->conf.gyro; + break; + default: + return -EINVAL; + } + + /* + * Since we are only making one-shot calls today, we can avoid needless + * on/off cycles if we rely on the runtime power management to handle + * shutting off the sensors when not in use. The downside is if we + * enable both sensors then only read from one, we have to wait for + * runtime PM to turn it off. So just keep the mode of the sensor we + * are not actively using to whatever it's already set as. + */ + ret = inv_icm42607_get_pwr_mgmt0(st, &gyro_mode, &accel_mode); + if (ret) + return ret; + + inv_icm42607_update_config(conf, oldconf, &config0, &config1); + + if (config0) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + } + if (ret) + return ret; + + oldconf->fs = conf->fs; + oldconf->odr = conf->odr; + } + + if (config1) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + } + if (ret) + return ret; + + oldconf->filter = conf->filter; + } + + oldconf->mode = conf->mode; + + switch (chan_type) { + case IIO_ACCEL: + return inv_icm42607_set_pwr_mgmt0(st, gyro_mode, conf->mode); + case IIO_ANGL_VEL: + return inv_icm42607_set_pwr_mgmt0(st, conf->mode, accel_mode); + default: + return -EINVAL; + } +} + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int reg; + __be16 data; + int ret; + + if ((chan->type != IIO_ANGL_VEL) && (chan->type != IIO_ACCEL)) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_X1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_X1; + break; + case IIO_MOD_Y: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Y1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Y1; + break; + case IIO_MOD_Z: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Z1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Z1; + break; + default: + return -EINVAL; + } + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* enable sensor */ + conf.mode = sensor_st->power_mode; + conf.filter = sensor_st->filter; + ret = inv_icm42607_set_sensor_conf(st, &conf, chan->type); + if (ret) + return ret; + + /* read sensor register data */ + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data)); + if (ret) + return ret; + + *val = be16_to_cpu(data); + if (*val == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + +static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st, + const struct inv_icm42607_conf *conf) +{ + unsigned int val; + int ret; + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode); + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, + INV_ICM42607_TEMP_FILTER_BW_34HZ); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0, + INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val); + if (ret) + return ret; + + st->conf = *conf; + + return 0; +} + +static int inv_icm42607_setup(struct inv_icm42607_state *st, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + const struct device *dev = regmap_get_device(st->map); + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val); + if (ret) + return ret; + + /* Warn, but don't fail. */ + if (val != st->hw->whoami) + dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n", + val, st->hw->whoami, st->hw->name); + + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET); + if (ret) + return ret; + + fsleep(1 * USEC_PER_MSEC); + + /* + * No polling interval specified in datasheet, so use reset time as + * polling interval and 10x reset time as timeout period. + */ + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS, + val, val & INV_ICM42607_INT_STATUS_RESET_DONE, + 1 * USEC_PER_MSEC, 10 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, + "reset error, reset done bit not set\n"); + + /* Sync the regcache again after a reset. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + ret = inv_icm42607_bus_setup(st); + if (ret) + return ret; + + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + val); + if (ret) + return ret; + + return inv_icm42607_set_init_conf(st, st->hw->conf); +} + +static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st) +{ + int ret; + + if (st->vddio_en) + return 0; + + ret = regulator_enable(st->vddio_supply); + if (ret) + return ret; + + fsleep(INV_ICM42607_POWER_UP_TIME_US); + + st->vddio_en = true; + + return 0; +} + +static void inv_icm42607_sensors_off(void *_data) +{ + struct inv_icm42607_state *st = _data; + const struct device *dev = regmap_get_device(st->map); + int ret; + + guard(mutex)(&st->lock); + + st->conf.gyro.mode = INV_ICM42607_SENSOR_MODE_OFF; + st->conf.accel.mode = INV_ICM42607_SENSOR_MODE_OFF; + + ret = inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, + st->conf.accel.mode); + if (ret) + dev_err(dev, "Unable to turn off sensors\n"); +} + +static void inv_icm42607_disable_vddio_reg(void *_data) +{ + struct inv_icm42607_state *st = _data; + + if (!st->vddio_en) + return; + + regulator_disable(st->vddio_supply); + + st->vddio_en = false; +} + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + struct device *dev = regmap_get_device(regmap); + struct inv_icm42607_state *st; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + dev_set_drvdata(dev, st); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->hw = hw; + st->map = regmap; + + ret = iio_read_mount_matrix(dev, &st->orientation); + if (ret) + return dev_err_probe(dev, ret, + "failed to retrieve mounting matrix\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get vdd regulator\n"); + + st->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(st->vddio_supply), + "Failed to get vddio regulator\n"); + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st); + if (ret) + return ret; + + /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */ + ret = inv_icm42607_setup(st, inv_icm42607_bus_setup); + if (ret) + return ret; + + /* + * Ensure if sensors get turned on at some point, they're turned off + * as part of teardown. + */ + ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off, st); + if (ret) + return ret; + + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + /* Initialize IIO device for Accel */ + st->indio_accel = inv_icm42607_accel_init(st); + if (IS_ERR(st->indio_accel)) + return PTR_ERR(st->indio_accel); + + /* Initialize IIO device for Gyro */ + st->indio_gyro = inv_icm42607_gyro_init(st); + if (IS_ERR(st->indio_gyro)) + return PTR_ERR(st->indio_gyro); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); + +static int inv_icm42607_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + inv_icm42607_disable_vddio_reg(st); + + return 0; +} + +static int inv_icm42607_resume(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + /* Sync the regcache again after regulator shutdown. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} + +static int inv_icm42607_runtime_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + + /* + * Set sensors state to off. Since we only support one-shot + * today we can use runtime PM to turn sensors off when not + * in use, and then when needed the reads/writes will + * re-enable the sensors as needed. This reduces complexity, + * however the tradeoff is that an unused sensor won't be + * turned off until the entire chip is no longer in use. + */ + inv_icm42607_sensors_off(st); + return 0; +} + +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = { + SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume) + RUNTIME_PM_OPS(inv_icm42607_runtime_suspend, NULL, NULL) +}; + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c new file mode 100644 index 000000000000..5b4683c2dd1e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .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_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_gyro_scan { + INV_ICM42607_GYRO_SCAN_X, + INV_ICM42607_GYRO_SCAN_Y, + INV_ICM42607_GYRO_SCAN_Z, + INV_ICM42607_GYRO_SCAN_TEMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_gyro_channels[] = { + INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP), +}; + +static const int inv_icm42607_gyro_scale_nano[][2] = { + [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 }, + [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 }, + [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 }, + [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 }, +}; + +static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.gyro.fs; + + *val = inv_icm42607_gyro_scale_nano[idx][0]; + *val2 = inv_icm42607_gyro_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano); + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_gyro_scale_nano[idx][0] && + val2 == inv_icm42607_gyro_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_gyro_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, +}; + +static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.gyro.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_gyro_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + *val = inv_icm42607_gyro_odr[i][0]; + *val2 = inv_icm42607_gyro_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_gyro_odr); idx++) { + if (val == inv_icm42607_gyro_odr[idx][0] && + val2 == inv_icm42607_gyro_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ANGL_VEL: + break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_gyro_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_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: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_gyro_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_gyro_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_gyro_info = { + .read_raw = inv_icm42607_gyro_read_raw, + .read_avail = inv_icm42607_gyro_read_avail, + .write_raw = inv_icm42607_gyro_write_raw, + .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + const char *name; + struct inv_icm42607_sensor_state *gyro_st; + struct iio_dev *indio_dev; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + gyro_st = iio_priv(indio_dev); + gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + gyro_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_gyro_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_gyro_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file mode 100644 index 000000000000..e903106af84a --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, + INV_ICM42607_SLEW_RATE_12_36NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2, + INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct inv_icm42607_hw *hw; + struct regmap *regmap; + + hw = i2c_get_match_data(client); + if (!hw) + return dev_err_probe(dev, -ENODEV, "Failed to get i2c data\n"); + + regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup); +} + +static const struct i2c_device_id inv_icm42607_id[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm42607_id); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct i2c_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-i2c", + .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), + }, + .id_table = inv_icm42607_id, + .probe = inv_icm42607_probe, +}; +module_i2c_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file mode 100644 index 000000000000..cd739375f6b2 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + /* Only support 4-wire mode for now. */ + ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG, + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE); + if (ret) + return ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, + INV_ICM42607_SLEW_RATE_2NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3, + INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct spi_device *spi) +{ + const struct inv_icm42607_hw *hw; + struct device *dev = &spi->dev; + struct regmap *regmap; + + hw = spi_get_device_match_data(spi); + if (!hw) + return dev_err_probe(dev, -ENODATA, "Failed to get SPI data\n"); + + if (spi->mode & SPI_3WIRE) + return dev_err_probe(dev, -ENODEV, "SPI 3-wire mode not supported\n"); + + regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup); +} + +static const struct spi_device_id inv_icm42607_spi_id_table[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, + { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct spi_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-spi", + .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), + }, + .id_table = inv_icm42607_spi_id_table, + .probe = inv_icm42607_probe, +}; +module_spi_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file mode 100644 index 000000000000..53f0484c0845 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct device *dev = regmap_get_device(st->map); + int ret, gyro_mode, accel_mode; + unsigned int val; + u8 raw[2]; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* + * Check if both the gyro and accel are off and if so, enable one + * of them. The temp sensor cannot be read if both the gyro and + * accel sensor are off. Prefer to enable the accel over the gyro + * as the datasheet says the gyro uses 5x more power and it has + * a minimum run time of 45ms. + */ + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + if (!gyro_mode && !accel_mode) { + /* enable accel sensor */ + conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); + if (ret) + return ret; + } + + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, + raw, sizeof(raw)); + if (ret) + return ret; + + *temp = get_unaligned_be16(raw); + if (*temp == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 temp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_temp_read(st, &temp); + if (ret) + return ret; + *val = temp; + return IIO_VAL_INT; + /* + * T°C = (temp / 128) + 25 + * Tm°C = 1000 * ((temp * 100 / 12800) + 25) + * scale: 100000 / 12800 ~= 7.8125 + * offset: 3200 + */ + case IIO_CHAN_INFO_SCALE: + *val = 7; + *val2 = 812500000; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = 3200; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h new file mode 100644 index 000000000000..18499b4d0b94 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_TEMP_H_ +#define INV_ICM42607_TEMP_H_ + +#include + +struct iio_dev; +struct iio_chan_spec; + +#define INV_ICM42607_TEMP_CHAN(_index) \ +{ \ + .type = IIO_TEMP, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h index 1c796d4b2a40..95fa934a42b4 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -190,7 +190,7 @@ struct inv_icm45600_sensor_state { #define INV_ICM45600_REG_IREG_ADDR 0x7C #define INV_ICM45600_REG_IREG_DATA 0x7E -/* Direct acces registers */ +/* Direct access registers */ #define INV_ICM45600_REG_MISC2 0x007F #define INV_ICM45600_MISC2_SOFT_RESET BIT(1) diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c index 2b9ea317385c..42111c543d3c 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -422,8 +422,11 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, if (max > 0 && fifo_nb > max) fifo_nb = max; - /* Try to read all FIFO data in internal buffer. */ - st->fifo.count = fifo_nb * packet_size; + /* + * Read all FIFO data into the internal buffer, clamping the + * device-reported count to the buffer capacity. + */ + st->fifo.count = min(fifo_nb * packet_size, INV_ICM45600_FIFO_SIZE_MAX); ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, st->fifo.data, st->fifo.count); if (ret == -ENOTSUPP || ret == -EFBIG) { diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c index d49053161a65..c1d7aa7e950d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -716,7 +716,7 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi dev_set_drvdata(dev, st); - st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL); + st->fifo.data = devm_kzalloc(dev, INV_ICM45600_FIFO_SIZE_MAX, GFP_KERNEL); if (!st->fifo.data) return -ENOMEM; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c index 81ba1b60f04b..69acf9bac2db 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -22,7 +22,7 @@ static int inv_icm45600_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; - chip_info = device_get_match_data(&client->dev); + chip_info = i2c_get_match_data(client); if (!chip_info) return -ENODEV; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c index 450a0f2abaaa..087e590e04c2 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c @@ -48,14 +48,14 @@ static int inv_icm45600_probe(struct spi_device *spi) * supported by this driver. */ static const struct spi_device_id inv_icm45600_id[] = { - { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, - { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, - { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, - { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, - { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, - { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, - { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, - { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { .name = "icm45605", .driver_data = (kernel_ulong_t)&inv_icm45605_chip_info }, + { .name = "icm45606", .driver_data = (kernel_ulong_t)&inv_icm45606_chip_info }, + { .name = "icm45608", .driver_data = (kernel_ulong_t)&inv_icm45608_chip_info }, + { .name = "icm45634", .driver_data = (kernel_ulong_t)&inv_icm45634_chip_info }, + { .name = "icm45686", .driver_data = (kernel_ulong_t)&inv_icm45686_chip_info }, + { .name = "icm45687", .driver_data = (kernel_ulong_t)&inv_icm45687_chip_info }, + { .name = "icm45688p", .driver_data = (kernel_ulong_t)&inv_icm45688p_chip_info }, + { .name = "icm45689", .driver_data = (kernel_ulong_t)&inv_icm45689_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm45600_id); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 47394594d17a..6b858fdfd1c6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -106,9 +106,9 @@ static int inv_magn_init(struct inv_mpu6050_state *st) return ret; /* - * Sensor sentivity + * Sensor sensitivity * 1 uT = 0.01 G and value is in micron (1e6) - * sensitvity = x uT * 0.01 * 1e6 + * sensitivity = x uT * 0.01 * 1e6 */ switch (st->chip_type) { case INV_MPU9150: diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index b8204eb0b4c6..71e90ddc19dd 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -65,23 +65,23 @@ static int inv_mpu_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_mpu_id[] = { - {"mpu6000", INV_MPU6000}, - {"mpu6500", INV_MPU6500}, - {"mpu6515", INV_MPU6515}, - {"mpu6880", INV_MPU6880}, - {"mpu9250", INV_MPU9250}, - {"mpu9255", INV_MPU9255}, - {"icm20608", INV_ICM20608}, - {"icm20608d", INV_ICM20608D}, - {"icm20609", INV_ICM20609}, - {"icm20689", INV_ICM20689}, - {"icm20600", INV_ICM20600}, - {"icm20602", INV_ICM20602}, - {"icm20690", INV_ICM20690}, - {"iam20380", INV_IAM20380}, - {"iam20680", INV_IAM20680}, - {"iam20680hp", INV_IAM20680HP}, - {"iam20680ht", INV_IAM20680HT}, + { .name = "mpu6000", .driver_data = INV_MPU6000 }, + { .name = "mpu6500", .driver_data = INV_MPU6500 }, + { .name = "mpu6515", .driver_data = INV_MPU6515 }, + { .name = "mpu6880", .driver_data = INV_MPU6880 }, + { .name = "mpu9250", .driver_data = INV_MPU9250 }, + { .name = "mpu9255", .driver_data = INV_MPU9255 }, + { .name = "icm20608", .driver_data = INV_ICM20608 }, + { .name = "icm20608d", .driver_data = INV_ICM20608D }, + { .name = "icm20609", .driver_data = INV_ICM20609 }, + { .name = "icm20689", .driver_data = INV_ICM20689 }, + { .name = "icm20600", .driver_data = INV_ICM20600 }, + { .name = "icm20602", .driver_data = INV_ICM20602 }, + { .name = "icm20690", .driver_data = INV_ICM20690 }, + { .name = "iam20380", .driver_data = INV_IAM20380 }, + { .name = "iam20680", .driver_data = INV_IAM20680 }, + { .name = "iam20680hp", .driver_data = INV_IAM20680HP }, + { .name = "iam20680ht", .driver_data = INV_IAM20680HT }, { } }; diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c index d159ee59acdd..f419296bba88 100644 --- a/drivers/iio/imu/smi240.c +++ b/drivers/iio/imu/smi240.c @@ -593,7 +593,7 @@ static int smi240_probe(struct spi_device *spi) } static const struct spi_device_id smi240_spi_id[] = { - { "smi240" }, + { .name = "smi240" }, { } }; MODULE_DEVICE_TABLE(spi, smi240_spi_id); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index f4edcb73ec8c..360f1365f892 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2852,11 +2852,8 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) st_lsm6dsx_handler_thread, irq_type | IRQF_ONESHOT, "lsm6dsx", hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } return 0; } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c index 2e72c7ba94dd..5321033daebc 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c @@ -112,6 +112,11 @@ int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) return err; } + if (enable) + hw->enable_mask |= BIT(ST_LSM6DSX_ID_FUSION); + else + hw->enable_mask &= ~BIT(ST_LSM6DSX_ID_FUSION); + return st_lsm6dsx_fusion_page_disable(hw); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 3389b15df0bc..ed47d5a6f11c 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -138,30 +138,30 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { - { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, - { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, - { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, - { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, - { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, - { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, - { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, - { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, - { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, - { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, - { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, - { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, - { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, - { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, - { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, - { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, - { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, - { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, - { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, - { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, - { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, - { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, - { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, - { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, + { .name = ST_LSM6DS3_DEV_NAME, .driver_data = ST_LSM6DS3_ID }, + { .name = ST_LSM6DS3H_DEV_NAME, .driver_data = ST_LSM6DS3H_ID }, + { .name = ST_LSM6DSL_DEV_NAME, .driver_data = ST_LSM6DSL_ID }, + { .name = ST_LSM6DSM_DEV_NAME, .driver_data = ST_LSM6DSM_ID }, + { .name = ST_ISM330DLC_DEV_NAME, .driver_data = ST_ISM330DLC_ID }, + { .name = ST_LSM6DSO_DEV_NAME, .driver_data = ST_LSM6DSO_ID }, + { .name = ST_ASM330LHH_DEV_NAME, .driver_data = ST_ASM330LHH_ID }, + { .name = ST_LSM6DSOX_DEV_NAME, .driver_data = ST_LSM6DSOX_ID }, + { .name = ST_LSM6DSR_DEV_NAME, .driver_data = ST_LSM6DSR_ID }, + { .name = ST_LSM6DS3TRC_DEV_NAME, .driver_data = ST_LSM6DS3TRC_ID }, + { .name = ST_ISM330DHCX_DEV_NAME, .driver_data = ST_ISM330DHCX_ID }, + { .name = ST_LSM9DS1_DEV_NAME, .driver_data = ST_LSM9DS1_ID }, + { .name = ST_LSM6DS0_DEV_NAME, .driver_data = ST_LSM6DS0_ID }, + { .name = ST_LSM6DSRX_DEV_NAME, .driver_data = ST_LSM6DSRX_ID }, + { .name = ST_LSM6DST_DEV_NAME, .driver_data = ST_LSM6DST_ID }, + { .name = ST_LSM6DSOP_DEV_NAME, .driver_data = ST_LSM6DSOP_ID }, + { .name = ST_ASM330LHHX_DEV_NAME, .driver_data = ST_ASM330LHHX_ID }, + { .name = ST_LSM6DSTX_DEV_NAME, .driver_data = ST_LSM6DSTX_ID }, + { .name = ST_LSM6DSV_DEV_NAME, .driver_data = ST_LSM6DSV_ID }, + { .name = ST_LSM6DSV16X_DEV_NAME, .driver_data = ST_LSM6DSV16X_ID }, + { .name = ST_LSM6DSO16IS_DEV_NAME, .driver_data = ST_LSM6DSO16IS_ID }, + { .name = ST_ISM330IS_DEV_NAME, .driver_data = ST_ISM330IS_ID }, + { .name = ST_ASM330LHB_DEV_NAME, .driver_data = ST_ASM330LHB_ID }, + { .name = ST_ASM330LHHXG1_DEV_NAME, .driver_data = ST_ASM330LHHXG1_ID }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 8c5d8535e54c..e67f09e1f093 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -32,8 +32,8 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct spi_device_id st_lsm9ds0_id_table[] = { - { LSM303D_IMU_DEV_NAME }, - { LSM9DS0_IMU_DEV_NAME }, + { .name = LSM303D_IMU_DEV_NAME }, + { .name = LSM9DS0_IMU_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 4763e224ebc6..f7a4be8ec320 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -649,7 +649,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, "IIO_BACKEND"); * @len: Buffer length * * This helper is intended to be used by backends that extend an IIO channel - * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * (through iio_backend_extend_chan_spec()) with extended info. In that case, * backends are not supposed to give their own callbacks (as they would not have * a way to get the backend from indio_dev). This is the setter. * @@ -851,7 +851,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND"); * @back: Backend device * @timeout_us: Timeout value in us. * - * When activated, it initates a proccess that aligns the sample's most + * When activated, it initiates a process that aligns the sample's most * significant bit (MSB) based solely on the captured data, without * considering any other external signals. * @@ -1017,7 +1017,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con * @dev: Consumer device for the backend * @name: Backend name * - * Get's the backend associated with @dev. + * Gets the backend associated with @dev. * * RETURNS: * A backend pointer, negative error pointer otherwise. @@ -1050,7 +1050,7 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); * @name: Backend name * @fwnode: Firmware node of the backend consumer * - * Get's the backend associated with a firmware node. + * Gets the backend associated with a firmware node. * * RETURNS: * A backend pointer, negative error pointer otherwise. diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 531fc4ccc15d..2c9ec93dff47 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -47,9 +47,6 @@ struct iio_dmabuf_priv { u64 context; - /* Spinlock used for locking the dma_fence */ - spinlock_t lock; - struct dma_buf_attachment *attach; struct sg_table *sgt; enum dma_data_direction dir; @@ -57,7 +54,12 @@ struct iio_dmabuf_priv { }; struct iio_dma_fence { + /* + * Must remain the first member so the default release callback can pass + * the fence directly to dma_fence_free(). + */ struct dma_fence base; + spinlock_t lock; /* protects base */ struct iio_dmabuf_priv *priv; struct work_struct work; }; @@ -1619,12 +1621,16 @@ static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) wake_up(&buffer->pollq); - guard(mutex)(&buffer->dmabufs_mutex); - - /* Close all attached DMABUFs */ - list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { - list_del_init(&priv->entry); - iio_buffer_dmabuf_put(priv->attach); + /* + * The mutex must be unlocked before iio_device_put(), which might drop the + * last reference and free the buffer. + */ + scoped_guard(mutex, &buffer->dmabufs_mutex) { + /* Close all attached DMABUFs */ + list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { + list_del_init(&priv->entry); + iio_buffer_dmabuf_put(priv->attach); + } } kfree(ib); @@ -1702,7 +1708,6 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib, if (!priv) return -ENOMEM; - spin_lock_init(&priv->lock); priv->context = dma_fence_context_alloc(1); dmabuf = dma_buf_get(fd); @@ -1827,18 +1832,9 @@ iio_buffer_dma_fence_get_driver_name(struct dma_fence *fence) return "iio"; } -static void iio_buffer_dma_fence_release(struct dma_fence *fence) -{ - struct iio_dma_fence *iio_fence = - container_of(fence, struct iio_dma_fence, base); - - kfree(iio_fence); -} - static const struct dma_fence_ops iio_buffer_dma_fence_ops = { .get_driver_name = iio_buffer_dma_fence_get_driver_name, .get_timeline_name = iio_buffer_dma_fence_get_driver_name, - .release = iio_buffer_dma_fence_release, }; static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, @@ -1892,6 +1888,8 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, goto err_attachment_put; } + spin_lock_init(&fence->lock); + fence->priv = priv; seqno = atomic_add_return(1, &priv->seqno); @@ -1902,7 +1900,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, * the dma_fence. */ dma_fence_init(&fence->base, &iio_buffer_dma_fence_ops, - &priv->lock, priv->context, seqno); + &fence->lock, priv->context, seqno); ret = iio_dma_resv_lock(dmabuf, nonblock); if (ret) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7e3d3872e2e6..767a7794624a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -99,6 +99,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", + [IIO_VOLUMEFLOW] = "volumeflow", }; static const char * const iio_modifier_names[] = { @@ -656,6 +657,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, int size, const int *vals) { int tmp0, tmp1; + int l = 0; s64 tmp2; bool scale_db = false; @@ -699,7 +701,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_INT_MULTIPLE: { int i; - int l = 0; for (i = 0; i < size; ++i) l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]); @@ -708,8 +709,26 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); case IIO_VAL_INT_64: - tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); - return sysfs_emit_at(buf, offset, "%lld", tmp2); + return sysfs_emit_at(buf, offset, "%lld", + iio_val_s64_compose(vals[0], vals[1])); + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: + { + int scale = type - IIO_VAL_DECIMAL64_BASE; + s64 frac; + + tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]), + int_pow(10, scale), &frac); + if (tmp2 == 0 && frac < 0) + l += sysfs_emit_at(buf, offset, "-"); + + l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale, + abs(frac)); + return l; + } default: return 0; } @@ -979,6 +998,7 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; + int type, dec_scale = 0; int integer, fract = 0; long long integer64; bool is_char = false; @@ -989,9 +1009,11 @@ static ssize_t iio_write_channel_info(struct device *dev, if (!indio_dev->info->write_raw) return -EINVAL; - if (indio_dev->info->write_raw_get_fmt) - switch (indio_dev->info->write_raw_get_fmt(indio_dev, - this_attr->c, this_attr->address)) { + if (indio_dev->info->write_raw_get_fmt) { + type = indio_dev->info->write_raw_get_fmt(indio_dev, + this_attr->c, + this_attr->address); + switch (type) { case IIO_VAL_INT: fract_mult = 0; break; @@ -1007,12 +1029,20 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_CHAR: is_char = true; break; + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: + dec_scale = type - IIO_VAL_DECIMAL64_BASE; + fallthrough; case IIO_VAL_INT_64: is_64bit = true; break; default: return -EINVAL; } + } if (is_char) { char ch; @@ -1021,12 +1051,14 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; integer = ch; } else if (is_64bit) { - ret = kstrtoll(buf, 0, &integer64); + if (dec_scale) + ret = kstrtodec64(buf, dec_scale, &integer64); + else + ret = kstrtoll(buf, 0, &integer64); if (ret) return ret; - fract = upper_32_bits(integer64); - integer = lower_32_bits(integer64); + iio_val_s64_decompose(integer64, &integer, &fract); } else { ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, scale_db); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9ce20cb05a9b..fb08e4f02520 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -281,7 +281,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, return ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { @@ -386,7 +386,7 @@ struct iio_channel *iio_channel_get(struct device *dev, return iio_channel_get_sys(name, channel_name); } -EXPORT_SYMBOL_GPL(iio_channel_get); +EXPORT_SYMBOL_NS_GPL(iio_channel_get, "IIO_CONSUMER"); void iio_channel_release(struct iio_channel *channel) { @@ -395,7 +395,7 @@ void iio_channel_release(struct iio_channel *channel) iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_channel_release); +EXPORT_SYMBOL_NS_GPL(iio_channel_release, "IIO_CONSUMER"); static void devm_iio_channel_free(void *iio_channel) { @@ -418,7 +418,7 @@ struct iio_channel *devm_iio_channel_get(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get, "IIO_CONSUMER"); struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, struct fwnode_handle *fwnode, @@ -437,7 +437,7 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(devm_fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); struct iio_channel *iio_channel_get_all(struct device *dev) { @@ -506,7 +506,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) iio_device_put(chans[i].indio_dev); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_get_all, "IIO_CONSUMER"); void iio_channel_release_all(struct iio_channel *channels) { @@ -518,7 +518,7 @@ void iio_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_channel_release_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_release_all, "IIO_CONSUMER"); static void devm_iio_channel_free_all(void *iio_channels) { @@ -541,7 +541,7 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev) return channels; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get_all, "IIO_CONSUMER"); static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) @@ -585,7 +585,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_raw, "IIO_CONSUMER"); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { @@ -597,7 +597,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_average_raw, "IIO_CONSUMER"); int iio_multiply_value(int *result, s64 multiplier, unsigned int type, int val, int val2) @@ -701,7 +701,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, return iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); } -EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); +EXPORT_SYMBOL_NS_GPL(iio_convert_raw_to_processed, "IIO_CONSUMER"); int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) @@ -714,13 +714,13 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, return iio_channel_read(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_read_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_attribute, "IIO_CONSUMER"); int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); } -EXPORT_SYMBOL_GPL(iio_read_channel_offset); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_offset, "IIO_CONSUMER"); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) @@ -752,20 +752,20 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, scale); } } -EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed_scale, "IIO_CONSUMER"); int iio_read_channel_processed(struct iio_channel *chan, int *val) { /* This is just a special case with scale factor 1 */ return iio_read_channel_processed_scale(chan, val, 1); } -EXPORT_SYMBOL_GPL(iio_read_channel_processed); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed, "IIO_CONSUMER"); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); } -EXPORT_SYMBOL_GPL(iio_read_channel_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_scale, "IIO_CONSUMER"); static int iio_channel_read_avail(struct iio_channel *chan, const int **vals, int *type, int *length, @@ -794,7 +794,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, return iio_channel_read_avail(chan, vals, type, length, attribute); } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_attribute, "IIO_CONSUMER"); int iio_read_avail_channel_raw(struct iio_channel *chan, const int **vals, int *length) @@ -811,7 +811,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, return ret; } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_max(struct iio_channel *chan, int *val, int *val2, int *type, @@ -867,7 +867,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_max_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_min(struct iio_channel *chan, int *val, int *val2, int *type, @@ -923,7 +923,7 @@ int iio_read_min_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_min_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_min_channel_raw, "IIO_CONSUMER"); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { @@ -937,7 +937,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) return 0; } -EXPORT_SYMBOL_GPL(iio_get_channel_type); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_type, "IIO_CONSUMER"); static int iio_channel_write(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum info) @@ -961,13 +961,13 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, return iio_channel_write(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_write_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_attribute, "IIO_CONSUMER"); int iio_write_channel_raw(struct iio_channel *chan, int val) { return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_write_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_raw, "IIO_CONSUMER"); unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) { @@ -982,7 +982,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) return i; } -EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_ext_info_count, "IIO_CONSUMER"); static const struct iio_chan_spec_ext_info * iio_lookup_ext_info(const struct iio_channel *chan, const char *attr) @@ -1017,7 +1017,7 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, return ext_info->read(chan->indio_dev, ext_info->private, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, const char *buf, size_t len) @@ -1031,7 +1031,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, return ext_info->write(chan->indio_dev, ext_info->private, chan->channel, buf, len); } -EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { @@ -1042,4 +1042,4 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_label); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_label, "IIO_CONSUMER"); diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 633a105fd7f0..0ea282ecc99a 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -808,10 +808,8 @@ static int adux1020_probe(struct i2c_client *client) NULL, adux1020_interrupt_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ADUX1020_DRV_NAME, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 8dacb1730429..d7e0924b8106 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -1545,11 +1545,8 @@ static int apds9160_probe(struct i2c_client *client) apds9160_irq_handler, IRQF_ONESHOT, "apds9160_event", indio_dev); - if (ret) { - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", - client->irq); - } + if (ret) + return ret; } else { indio_dev->info = &apds9160_info_no_events; indio_dev->channels = apds9160_channels_without_events; diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index d60ade1209f3..f0abbc4a862a 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -432,10 +432,8 @@ static int apds9300_probe(struct i2c_client *client) NULL, apds9300_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9300_event", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 5ca4c87524fe..f681b02e4922 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -469,9 +469,9 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) int status = 0; u8 buff[3]; - ret = pm_runtime_resume_and_get(data->dev); - if (ret) - return ret; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(data->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return PM_RUNTIME_ACQUIRE_ERR(&pm); ret = regmap_field_read(rf->intg_time, &intg_time_idx); if (ret) @@ -535,8 +535,6 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) *val = get_unaligned_le24(&buff); - pm_runtime_put_autosuspend(data->dev); - return 0; } @@ -1286,8 +1284,7 @@ static int apds9306_probe(struct i2c_client *client) apds9306_irq_handler, IRQF_ONESHOT, "apds9306_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "failed to assign interrupt.\n"); + return ret; } else { indio_dev->info = &apds9306_info_no_events; indio_dev->channels = apds9306_channels_without_events; diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 2686c3b0c03b..afe520de60e2 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1098,10 +1098,8 @@ static int apds9960_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9960_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) goto error_power_down; - } ret = iio_device_register(indio_dev); if (ret) diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 0aa8e5cc6c56..2b8ff36071b8 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -784,8 +784,7 @@ static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev, IRQF_ONESHOT, "bh1745_interrupt", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "Request for IRQ failed\n"); + return ret; } return 0; diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 2590fc8fd154..b32a94028f09 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -368,7 +368,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: cm32181->calibscale = val; - return val; + return 0; case IIO_CHAN_INFO_INT_TIME: ret = cm32181_write_als_it(cm32181, val2); return ret; diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index fbb9e0a13733..506c91f44f43 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -80,22 +80,19 @@ static int cm3323_init(struct iio_dev *indio_dev) { int ret; struct cm3323_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading reg_conf\n"); /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); data->reg_conf = ret; ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); - if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error writing reg_conf\n"); return 0; } @@ -236,10 +233,8 @@ static int cm3323_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; ret = cm3323_init(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "cm3323 chip init failed\n"); + if (ret < 0) return ret; - } ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev); if (ret < 0) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 98c84f33db60..34c127593947 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -232,10 +232,8 @@ static int cm3605_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_disable_aset; - } /* Just name the trigger the same as the driver */ led_trigger_register_simple("cm3605", &cm3605->led); @@ -324,3 +322,4 @@ module_platform_driver(cm3605_driver); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a8db514cca5e..1fd40d60182e 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -342,6 +342,10 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev, bool state) { struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + if (state == gp2ap002->enabled) + return 0; if (state) { /* @@ -349,13 +353,16 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev, * already) and reintialize the sensor by using runtime_pm * callbacks. */ - pm_runtime_get_sync(gp2ap002->dev); - gp2ap002->enabled = true; + ret = pm_runtime_resume_and_get(gp2ap002->dev); + if (ret) + return ret; + } else { pm_runtime_put_autosuspend(gp2ap002->dev); - gp2ap002->enabled = false; } + gp2ap002->enabled = state; + return 0; } @@ -573,10 +580,8 @@ static int gp2ap002_probe(struct i2c_client *client) ret = devm_request_threaded_irq(dev, client->irq, NULL, gp2ap002_prox_irq, IRQF_ONESHOT, "gp2ap002", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_put_pm; - } gp2ap002->irq = client->irq; /* @@ -642,6 +647,7 @@ static int gp2ap002_runtime_suspend(struct device *dev) /* Disable chip and IRQ, everything off */ ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00); if (ret) { + enable_irq(gp2ap002->irq); dev_err(gp2ap002->dev, "error setting up operation mode\n"); return ret; } @@ -669,7 +675,7 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = regulator_enable(gp2ap002->vio); if (ret) { dev_err(dev, "failed to enable VIO regulator in resume path\n"); - return ret; + goto out_disable_vdd; } msleep(20); @@ -677,13 +683,19 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = gp2ap002_init(gp2ap002); if (ret) { dev_err(dev, "re-initialization failed\n"); - return ret; + goto out_disable_vio; } /* Re-activate the IRQ */ enable_irq(gp2ap002->irq); return 0; + +out_disable_vio: + regulator_disable(gp2ap002->vio); +out_disable_vdd: + regulator_disable(gp2ap002->vdd); + return ret; } static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend, @@ -717,3 +729,4 @@ module_i2c_driver(gp2ap002_driver); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 6bf1bc9a38fe..929343e42d83 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -33,9 +33,9 @@ struct als_state { u32 illum[CHANNEL_SCAN_INDEX_MAX]; aligned_s64 timestamp; } scan; - int scale_pre_decml; - int scale_post_decml; - int scale_precision; + int scale_pre_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_post_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_precision[CHANNEL_SCAN_INDEX_MAX]; int value_offset; int num_channels; s64 timestamp; @@ -119,9 +119,8 @@ static const struct iio_chan_spec als_channels[] = { /* Channel read_raw handler */ static int als_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) { struct als_state *als_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev; @@ -162,12 +161,12 @@ static int als_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&als_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( hsdev, hsdev->usage, address, report_id, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&als_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -175,9 +174,11 @@ static int als_read_raw(struct iio_dev *indio_dev, ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = als_state->scale_pre_decml; - *val2 = als_state->scale_post_decml; - ret_type = als_state->scale_precision; + if (chan->scan_index >= CHANNEL_SCAN_INDEX_MAX) + return -EINVAL; + *val = als_state->scale_pre_decml[chan->scan_index]; + *val2 = als_state->scale_post_decml[chan->scan_index]; + ret_type = als_state->scale_precision[chan->scan_index]; break; case IIO_CHAN_INFO_OFFSET: *val = als_state->value_offset; @@ -205,10 +206,8 @@ static int als_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int als_write_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) { struct als_state *als_state = iio_priv(indio_dev); int ret = 0; @@ -240,8 +239,7 @@ static const struct iio_info als_info = { /* Callback handler to send event after all samples are received and captured */ static int als_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -262,9 +260,9 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -303,9 +301,9 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int als_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - struct als_state *st) + struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct als_state *st) { struct iio_chan_spec *channels; int ret, index = 0; @@ -331,6 +329,10 @@ static int als_parse_report(struct platform_device *pdev, }; ++index; + st->scale_precision[i] = hid_sensor_format_scale(usage_id, + &st->als[i], &st->scale_pre_decml[i], + &st->scale_post_decml[i]); + dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, st->als[i].report_id); } @@ -340,10 +342,6 @@ static int als_parse_report(struct platform_device *pdev, if (index) ret = 0; - st->scale_precision = hid_sensor_format_scale(usage_id, - &st->als[CHANNEL_SCAN_INDEX_INTENSITY], - &st->scale_pre_decml, &st->scale_post_decml); - return ret; } @@ -399,31 +397,31 @@ static int hid_als_probe(struct platform_device *pdev) atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &als_state->common_attributes); + &als_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - als_state->callbacks.send_event = als_proc_event; als_state->callbacks.capture_sample = als_capture_sample; als_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); return ret; @@ -436,8 +434,8 @@ static void hid_als_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct als_state *als_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); } diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 95bf2bfd86ee..63fd3eff171c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -69,9 +69,8 @@ static const struct iio_chan_spec prox_channels[] = { /* Channel read_raw handler */ static int prox_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) { struct prox_state *prox_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -134,10 +133,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int prox_write_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) { struct prox_state *prox_state = iio_priv(indio_dev); int ret = 0; @@ -165,8 +162,7 @@ static const struct iio_info prox_info = { /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -182,9 +178,9 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -217,8 +213,8 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int prox_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct prox_state *st) + struct hid_sensor_hub_device *hsdev, + struct prox_state *st) { struct iio_chan_spec *channels = st->channels; int index = 0; @@ -270,8 +266,7 @@ static int hid_prox_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct prox_state *prox_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct prox_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -305,18 +300,12 @@ static int hid_prox_probe(struct platform_device *pdev) atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &prox_state->common_attributes); + &prox_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - prox_state->callbacks.send_event = prox_proc_event; prox_state->callbacks.capture_sample = prox_capture_sample; prox_state->callbacks.pdev = pdev; @@ -324,13 +313,19 @@ static int hid_prox_probe(struct platform_device *pdev) &prox_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); return ret; @@ -343,8 +338,8 @@ static void hid_prox_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct prox_state *prox_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); } diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index b88e7c4eae3e..33deb1726689 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -333,16 +333,6 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) return isl29028_read_als_ir(chip, ir_data); } -static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) -{ - struct device *dev = regmap_get_device(chip->regmap); - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - /* Channel IO */ static int isl29028_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -352,7 +342,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -405,11 +395,11 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - ret = isl29028_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(dev); if (ret < 0) return ret; - return ret; + return 0; } static int isl29028_read_raw(struct iio_dev *indio_dev, @@ -420,7 +410,7 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret, pm_ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -476,10 +466,10 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, /** * Preserve the ret variable if the call to - * isl29028_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = isl29028_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(dev); if (pm_ret < 0) return pm_ret; diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index bdc74b8226c8..bc031f2c3141 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -838,8 +838,7 @@ static int ltr390_probe(struct i2c_client *client) "ltr390_thresh_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", client->irq); + return ret; } ret = ltr390_pm_init(data); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 7d045be78c6d..fa0d16890c8b 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1537,11 +1537,8 @@ static int ltr501_probe(struct i2c_client *client) IRQF_ONESHOT, "ltr501_thresh_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", - client->irq); + if (ret) return ret; - } } else { indio_dev->info = data->chip_info->info_no_irq; } diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 3f34ddc911b4..0a8851342039 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -247,11 +247,10 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data) return ret; greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0); + ltrf216a_set_power_state(data, false); if (greendata < 0) return greendata; - ltrf216a_set_power_state(data, false); - lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC; return lux; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index c5bd7fa23fb7..a948353014ac 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -100,9 +101,14 @@ struct opt3001_chip_info { struct opt3001 { struct i2c_client *client; - struct device *dev; + /* + * Ensure data capture and read-modify-write sequences are + * not interrupted. + */ struct mutex lock; + + /* Allows for IRQs to bypass locking mechanism */ bool ok_to_ignore_lock; bool result_ready; wait_queue_head_t result_ready_queue; @@ -223,11 +229,10 @@ static const struct opt3001_scale opt3002_scales[] = { }, }; -static int opt3001_find_scale(const struct opt3001 *opt, int val, - int val2, u8 *exponent) +static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, + u8 *exponent) { - int i; - for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i]; /* * Compare the integer and micro parts to determine value scale. @@ -242,8 +247,8 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val, return -EINVAL; } -static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, - u16 mantissa, int *val, int *val2) +static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa, + int *val, int *val2) { int ret; int whole = opt->chip_info->factor_whole; @@ -310,123 +315,143 @@ static const struct iio_chan_spec opt3002_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), }; -static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +static int opt3001_start_conversion(struct opt3001 *opt) { - int ret; - u16 mantissa; + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; u16 reg; - u8 exponent; - u16 value; - long timeout; - - if (opt->use_irq) { - /* - * Enable the end-of-conversion interrupt mechanism. Note that - * doing so will overwrite the low-level limit value however we - * will restore this value later on. - */ - ret = i2c_smbus_write_word_swapped(opt->client, - OPT3001_LOW_LIMIT, - OPT3001_LOW_LIMIT_EOC_ENABLE); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } - - /* Allow IRQ to access the device despite lock being set */ - opt->ok_to_ignore_lock = true; - } + int ret; /* Reset data-ready indicator flag */ opt->result_ready = false; /* Configure for single-conversion mode and start a new conversion */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } reg = ret; opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); + if (ret) + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + + return ret; +} + +static int opt3001_get_processed_irq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 value; + int ret; + + /* + * Enable the end-of-conversion interrupt mechanism. Note that doing so + * will overwrite the low-level limit value however we will restore this + * value later on. + */ + ret = i2c_smbus_write_word_swapped(client, + OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + /* Allow IRQ to access the device despite lock being set */ + opt->ok_to_ignore_lock = true; + + ret = opt3001_start_conversion(opt); + if (ret) goto err; - } - if (opt->use_irq) { - /* Wait for the IRQ to indicate the conversion is complete */ - ret = wait_event_timeout(opt->result_ready_queue, - opt->result_ready, - msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) { - ret = -ETIMEDOUT; - goto err; - } - } else { - /* Sleep for result ready time */ - timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? - OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG; - msleep(timeout); - - /* Check result ready flag */ - ret = i2c_smbus_read_word_swapped(opt->client, - OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; - } - - if (!(ret & OPT3001_CONFIGURATION_CRF)) { - ret = -ETIMEDOUT; - goto err; - } - - /* Obtain value */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_RESULT); - goto err; - } - opt->result = ret; - opt->result_ready = true; - } + if (wait_event_timeout(opt->result_ready_queue, opt->result_ready, + msecs_to_jiffies(OPT3001_RESULT_READY_LONG))) + ret = 0; + else + ret = -ETIMEDOUT; err: - if (opt->use_irq) - /* Disallow IRQ to access the device while lock is active */ - opt->ok_to_ignore_lock = false; + opt->ok_to_ignore_lock = false; if (ret < 0) return ret; - if (opt->use_irq) { - /* - * Disable the end-of-conversion interrupt mechanism by - * restoring the low-level limit value (clearing - * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing - * those enable bits would affect the actual limit value due to - * bit-overlap and therefore can't be done. - */ - value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; - ret = i2c_smbus_write_word_swapped(opt->client, - OPT3001_LOW_LIMIT, - value); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + /* + * Disable the end-of-conversion interrupt mechanism by restoring the + * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note + * that selectively clearing those enable bits would affect the actual + * limit value due to bit-overlap and therefore can't be done. + */ + value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; + ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value); + if (ret) + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + + return ret; +} + +static int opt3001_get_processed_noirq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + int ret; + + ret = opt3001_start_conversion(opt); + if (ret) + return ret; + + if (opt->int_time == OPT3001_INT_TIME_SHORT) + msleep(OPT3001_RESULT_READY_SHORT); + else + msleep(OPT3001_RESULT_READY_LONG); + + /* Check result ready flag */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } + if (!(ret & OPT3001_CONFIGURATION_CRF)) + return -ETIMEDOUT; + + /* Obtain value */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); + return ret; + } + + opt->result = ret; + opt->result_ready = true; + + return 0; +} + +static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +{ + u16 mantissa; + u8 exponent; + int ret; + + if (opt->use_irq) + ret = opt3001_get_processed_irq(opt); + else + ret = opt3001_get_processed_noirq(opt); + if (ret) + return ret; + exponent = OPT3001_REG_EXPONENT(opt->result); mantissa = OPT3001_REG_MANTISSA(opt->result); @@ -445,13 +470,15 @@ static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2) static int opt3001_set_int_time(struct opt3001 *opt, int time) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); return ret; } @@ -470,16 +497,14 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time) return -EINVAL; } - return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + return i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); } static int opt3001_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -487,31 +512,24 @@ static int opt3001_read_raw(struct iio_dev *iio, if (chan->type != opt->chip_info->chan_type) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = opt3001_get_processed(opt, val, val2); - break; + return opt3001_get_processed(opt, val, val2); case IIO_CHAN_INFO_INT_TIME: - ret = opt3001_get_int_time(opt, val, val2); - break; + return opt3001_get_int_time(opt, val, val2); default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -525,47 +543,46 @@ static int opt3001_write_raw(struct iio_dev *iio, if (val != 0) return -EINVAL; - mutex_lock(&opt->lock); - ret = opt3001_set_int_time(opt, val2); - mutex_unlock(&opt->lock); + guard(mutex)(&opt->lock); - return ret; + return opt3001_set_int_time(opt, val2); } static int opt3001_read_event_value(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, enum iio_event_info info, - int *val, int *val2) + 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 opt3001 *opt = iio_priv(iio); - int ret = IIO_VAL_INT_PLUS_MICRO; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (dir) { case IIO_EV_DIR_RISING: opt3001_to_iio_ret(opt, opt->high_thresh_exp, - opt->high_thresh_mantissa, val, val2); - break; + opt->high_thresh_mantissa, val, val2); + return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_DIR_FALLING: opt3001_to_iio_ret(opt, opt->low_thresh_exp, - opt->low_thresh_mantissa, val, val2); - break; + opt->low_thresh_mantissa, val, val2); + return IIO_VAL_INT_PLUS_MICRO; default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_event_value(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, enum iio_event_info info, - int val, int val2) + 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 opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; int whole; int integer; @@ -580,12 +597,12 @@ static int opt3001_write_event_value(struct iio_dev *iio, if (val < 0) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); ret = opt3001_find_scale(opt, val, val2, &exponent); if (ret < 0) { - dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2); - goto err; + dev_err(dev, "can't find scale for %d.%06u\n", val, val2); + return ret; } whole = opt->chip_info->factor_whole; @@ -608,25 +625,22 @@ static int opt3001_write_event_value(struct iio_dev *iio, opt->low_thresh_exp = exponent; break; default: - ret = -EINVAL; - goto err; + return -EINVAL; } - ret = i2c_smbus_write_word_swapped(opt->client, reg, value); + ret = i2c_smbus_write_word_swapped(client, reg, value); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", reg); - goto err; + dev_err(dev, "failed to write register %02x\n", reg); + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static int opt3001_read_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) { struct opt3001 *opt = iio_priv(iio); @@ -634,10 +648,14 @@ static int opt3001_read_event_config(struct iio_dev *iio, } static int opt3001_write_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, bool state) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) { struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 mode; u16 reg; @@ -648,33 +666,29 @@ static int opt3001_write_event_config(struct iio_dev *iio, if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN) return 0; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS : OPT3001_CONFIGURATION_M_SHUTDOWN; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } reg = ret; opt3001_set_mode(opt, ®, mode); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - goto err; + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static const struct iio_info opt3001_info = { @@ -689,46 +703,68 @@ static const struct iio_info opt3001_info = { static int opt3001_read_id(struct opt3001 *opt) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; char manufacturer[2]; u16 device_id; int ret; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_MANUFACTURER_ID); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_MANUFACTURER_ID); manufacturer[0] = ret >> 8; manufacturer[1] = ret & 0xff; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_DEVICE_ID); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_DEVICE_ID); device_id = ret; - dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0], - manufacturer[1], device_id); + dev_info(dev, "Found %c%c OPT%04x\n", manufacturer[0], manufacturer[1], + device_id); return 0; } +static void opt3001_power_off(void *data) +{ + struct opt3001 *opt = data; + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 reg_val; + int ret; + + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return; + } + + reg_val = ret; + opt3001_set_mode(opt, ®_val, OPT3001_CONFIGURATION_M_SHUTDOWN); + + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg_val); + if (ret < 0) + dev_err(dev, "failed to write to register %02x\n", + OPT3001_CONFIGURATION); +} + static int opt3001_configure(struct opt3001 *opt) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_CONFIGURATION); reg = ret; @@ -751,30 +787,28 @@ static int opt3001_configure(struct opt3001 *opt) reg &= ~OPT3001_CONFIGURATION_ME; reg &= ~OPT3001_CONFIGURATION_FC_MASK; - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - return ret; - } + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to write register %02x\n", + OPT3001_CONFIGURATION); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + ret = devm_add_action_or_reset(dev, opt3001_power_off, opt); + if (ret) + return dev_err_probe(dev, ret, + "failed to register power off function\n"); + + ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_LOW_LIMIT); opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_HIGH_LIMIT); - return ret; - } + ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_HIGH_LIMIT); opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret); @@ -786,6 +820,8 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) { struct iio_dev *iio = _iio; struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; bool wake_result_ready_queue = false; enum iio_chan_type chan_type = opt->chip_info->chan_type; @@ -794,10 +830,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) if (!ok_to_ignore_lock) mutex_lock(&opt->lock); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); goto out; } @@ -805,21 +841,21 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) OPT3001_CONFIGURATION_M_CONTINUOUS) { if (ret & OPT3001_CONFIGURATION_FH) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_RESULT); + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); goto out; } opt->result = ret; @@ -852,12 +888,13 @@ static int opt3001_probe(struct i2c_client *client) opt = iio_priv(iio); opt->client = client; - opt->dev = dev; opt->chip_info = i2c_get_match_data(client); - mutex_init(&opt->lock); + ret = devm_mutex_init(dev, &opt->lock); + if (ret) + return ret; + init_waitqueue_head(&opt->result_ready_queue); - i2c_set_clientdata(client, iio); if (opt->chip_info->has_id) { ret = opt3001_read_id(opt); @@ -875,55 +912,20 @@ static int opt3001_probe(struct i2c_client *client) iio->modes = INDIO_DIRECT_MODE; iio->info = &opt3001_info; - ret = devm_iio_device_register(dev, iio); - if (ret) { - dev_err(dev, "failed to register IIO device\n"); - return ret; - } - /* Make use of INT pin only if valid IRQ no. is given */ if (irq > 0) { - ret = request_threaded_irq(irq, NULL, opt3001_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "opt3001", iio); - if (ret) { - dev_err(dev, "failed to request IRQ #%d\n", irq); + ret = devm_request_threaded_irq(dev, irq, NULL, opt3001_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); + if (ret) return ret; - } + opt->use_irq = true; } else { - dev_dbg(opt->dev, "enabling interrupt-less operation\n"); + dev_dbg(dev, "enabling interrupt-less operation\n"); } - return 0; -} - -static void opt3001_remove(struct i2c_client *client) -{ - struct iio_dev *iio = i2c_get_clientdata(client); - struct opt3001 *opt = iio_priv(iio); - int ret; - u16 reg; - - if (opt->use_irq) - free_irq(client->irq, iio); - - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return; - } - - reg = ret; - opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); - - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - } + return devm_iio_device_register(dev, iio); } static const struct opt3001_chip_info opt3001_chip_information = { @@ -964,7 +966,6 @@ MODULE_DEVICE_TABLE(of, opt3001_of_match); static struct i2c_driver opt3001_driver = { .probe = opt3001_probe, - .remove = opt3001_remove, .id_table = opt3001_id, .driver = { diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index dd152d921b48..aa3d87995b7c 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -39,7 +39,7 @@ #define OPT4001_CTRL_OPER_MODE_MASK GENMASK(5, 4) #define OPT4001_CTRL_LATCH_MASK GENMASK(3, 3) #define OPT4001_CTRL_INT_POL_MASK GENMASK(2, 2) -#define OPT4001_CTRL_FAULT_COUNT GENMASK(0, 1) +#define OPT4001_CTRL_FAULT_COUNT_MASK GENMASK(1, 0) /* OPT4001 constants */ #define OPT4001_DEVICE_ID_VAL 0x121 @@ -173,6 +173,7 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, u8 crc; u8 calc_crc; u64 lux_raw; + u32 rem; int ret; ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1); @@ -199,8 +200,8 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, lux_raw = lux_raw << exp; lux_raw = lux_raw * chip->chip_info->mul; - *val = div_u64_rem(lux_raw, chip->chip_info->div, val2); - *val2 = *val2 * 100; + *val = div_u64_rem(lux_raw, chip->chip_info->div, &rem); + *val2 = rem * 100; return IIO_VAL_INT_PLUS_NANO; } @@ -222,33 +223,14 @@ static int opt4001_set_conf(struct opt4001_chip *chip) return ret; } -static int opt4001_power_down(struct opt4001_chip *chip) -{ - struct device *dev = &chip->client->dev; - int ret; - unsigned int reg; - - ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, ®); - if (ret) { - dev_err(dev, "Failed to read configuration\n"); - return ret; - } - - /* MODE_OFF is 0x0 so just set bits to 0 */ - reg &= ~OPT4001_CTRL_OPER_MODE_MASK; - - ret = regmap_write(chip->regmap, OPT4001_CTRL, reg); - if (ret) - dev_err(dev, "Failed to set configuration to power down\n"); - - return ret; -} - static void opt4001_chip_off_action(void *data) { struct opt4001_chip *chip = data; + int ret; - opt4001_power_down(chip); + ret = regmap_clear_bits(chip->regmap, OPT4001_CTRL, OPT4001_CTRL_OPER_MODE_MASK); + if (ret) + dev_err(&chip->client->dev, "Failed to power down\n"); } static const struct iio_chan_spec opt4001_channels[] = { @@ -287,6 +269,9 @@ static int opt4001_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4001_als_time_to_index(val2); if (int_time < 0) return int_time; diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index c391ad3271c6..8253a6a8bee4 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -632,6 +632,9 @@ static int opt4060_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4060_als_time_to_index(val2); if (int_time < 0) return int_time; @@ -710,6 +713,7 @@ static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, { int ret, pers, fault_count, int_time; u64 uval; + u32 rem; int_time = opt4060_int_time_reg[chip->int_time][0]; @@ -735,7 +739,8 @@ static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, } uval = mul_u32_u32(int_time, pers); - *val = div_u64_rem(uval, MICRO, val2); + *val = div_u64_rem(uval, MICRO, &rem); + *val2 = rem; return IIO_VAL_INT_PLUS_MICRO; } @@ -805,7 +810,7 @@ static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, ®val); if (ret) { - dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + dev_err(chip->dev, "Failed to read THRESHOLD_HIGH.\n"); return ret; } *th_hi = opt4060_calc_val_from_th_reg(regval); @@ -1200,7 +1205,7 @@ static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(chip->dev, ret, "Could not request IRQ\n"); + return ret; init_completion(&chip->completion); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index f961973892f2..7d82efc31f3a 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -987,11 +987,8 @@ static int rpr0521_probe(struct i2c_client *client) rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rpr0521_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d for trigger0 failed\n", - client->irq); + if (ret) goto err_pm_disable; - } ret = devm_iio_trigger_register(indio_dev->dev.parent, data->drdy_trigger0); @@ -1071,7 +1068,10 @@ static int rpr0521_runtime_resume(struct device *dev) struct rpr0521_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + if (data->als_ps_need_en) { ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); if (ret < 0) diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 4601ae5d2009..bb7c99041819 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1251,10 +1251,8 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "si1145_irq", trig); - if (ret < 0) { - dev_err(&client->dev, "irq request failed\n"); + if (ret) return ret; - } ret = devm_iio_trigger_register(&client->dev, trig); if (ret) diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index bcd729a9924e..94d5261b2663 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -196,11 +196,8 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) st_uvis25_trigger_handler_thread, irq_type | IRQF_ONESHOT, iio_dev->name, hw); - if (err) { - dev_err(dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger", iio_dev->name); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index c4c15093e9e5..0f78a0abfafb 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -46,7 +46,7 @@ static const struct of_device_id st_uvis25_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match); static const struct spi_device_id st_uvis25_spi_id_table[] = { - { ST_UVIS25_DEV_NAME }, + { .name = ST_UVIS25_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e7ce6f32592b..7c8a1d2b2ed0 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -673,11 +673,8 @@ static int stk3310_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "stk3310_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_standby; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index b61442c5c1be..b392943e51c8 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -9,8 +9,6 @@ * TCS34727) * * Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf - * - * TODO: wait time */ #include @@ -19,6 +17,7 @@ #include #include #include +#include #include #include @@ -53,19 +52,30 @@ #define TCS3472_STATUS_AINT BIT(4) #define TCS3472_STATUS_AVALID BIT(0) #define TCS3472_ENABLE_AIEN BIT(4) +#define TCS3472_ENABLE_WEN BIT(3) #define TCS3472_ENABLE_AEN BIT(1) #define TCS3472_ENABLE_PON BIT(0) +#define TCS3472_ENABLE_RUN \ + (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON | TCS3472_ENABLE_WEN) #define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1)) +#define TCS3472_CONFIG_WLONG BIT(1) + +#define TCS3472_ATIME_TO_US(atime) (((256) - (atime)) * 2400) struct tcs3472_data { struct i2c_client *client; struct mutex lock; + int target_freq_hz; + int target_freq_uhz; u16 low_thresh; u16 high_thresh; u8 enable; + u8 enable_pre_suspend; u8 control; u8 atime; u8 apers; + u8 wtime; + bool wlong; }; static const struct iio_event_spec tcs3472_events[] = { @@ -91,6 +101,7 @@ static const struct iio_event_spec tcs3472_events[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .channel2 = IIO_MOD_LIGHT_##_color, \ .address = _addr, \ .scan_index = _si, \ @@ -114,9 +125,65 @@ static const struct iio_chan_spec tcs3472_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +/* + * The chip's cycle time is the sum of three components: + * - ATIME: the programmable RGBC integration time. + * - The fixed RGBC initialization time (2400 us). + * - WTIME: the wait time, used only if WEN is set. If WLONG is active, + * the wait step is multiplied by 12 (2400 us -> 28800 us). + */ +static unsigned int tcs3472_cycle_time_us(struct tcs3472_data *data) +{ + unsigned int atime_us = TCS3472_ATIME_TO_US(data->atime); + unsigned int init_us = 2400; + unsigned int wtime_us; + + if (!(data->enable & TCS3472_ENABLE_WEN)) + wtime_us = 0; + else if (data->wlong) + wtime_us = (256 - data->wtime) * 28800; + else + wtime_us = (256 - data->wtime) * 2400; + + return atime_us + init_us + wtime_us; +} + +/* + * Convert a cycle time in microseconds to a frequency in Hz and microhertz. + * + * Given cycle_us = T (the cycle period in microseconds), the corresponding + * frequency is: + * f = 1e6 / T [Hz] + * + * The result is split into the IIO_VAL_INT_PLUS_MICRO format: + * val = floor(1e6 / T) [Hz] + * val2 = (1e6 mod T) * 1e6 / T [microhertz] + */ +static void tcs3472_cycle_to_freq(unsigned int cycle_us, int *val, int *val2) +{ + *val = USEC_PER_SEC / cycle_us; + *val2 = div_u64((u64)(USEC_PER_SEC % cycle_us) * USEC_PER_SEC, + cycle_us); +} + static int tcs3472_req_data(struct tcs3472_data *data) { - int tries = 50; + /* + * The worst-case cycle time is reached with ATIME=0x00, WTIME=0x00 + * and WLONG=1. So: + * 614 ms (Max Integration Time) + * + 2.4 ms (RGBC Init) + * + 7.37 s (Max Wait Time) + * = ~ 8 s (Total Max cycle time). + * Use that as a polling upper bound; in normal operation the loop + * exits as soon as AVALID is set. So the total number of tries in 8 + * seconds considering a polling period of 20 ms is 400. + * Considering a 20% margin due to oscillator tolerance, the total + * duration becomes approximately 9.8 seconds, which corresponds to + * about 480 steps. Therefore, setting it to 500 appears to be a + * reasonable and safe trade-off. + */ + int tries = 500; int ret; while (tries--) { @@ -164,18 +231,165 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: *val = 0; - *val2 = (256 - data->atime) * 2400; + *val2 = TCS3472_ATIME_TO_US(data->atime); return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: { + unsigned int cycle_us; + + guard(mutex)(&data->lock); + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, val, val2); + return IIO_VAL_INT_PLUS_MICRO; + } default: return -EINVAL; } } +/* + * __tcs3472_set_sampling_freq() - implementation of sampling frequency + * configuration. The caller must hold data->lock. + */ +static int __tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + unsigned int atime_us; + unsigned int init_us = 2400; + u64 cycle_us; + s64 wait_us; + int wtime; + bool wlong = false; + u8 config; + int ret; + + if (val < 0 || val2 < 0 || (val == 0 && val2 == 0)) + return -EINVAL; + + atime_us = TCS3472_ATIME_TO_US(data->atime); + + /* + * cycle_us = 1 / freq, expressed in microseconds. + * Numerator: 1 [s] = PSEC_PER_SEC [ps] + * Denominator: freq [Hz] * MICROHZ_PER_HZ + val2 [uHz] = freq in [uHz] + * Result: ps / uHz = us + */ + cycle_us = div64_u64(PSEC_PER_SEC, (u64)val * MICROHZ_PER_HZ + val2); + + /* + * wait_us can be negative when the requested frequency is too high + * to be reached, or very large when the requested frequency is + * close to zero. Use s64 to cover the full range: + * + * cycle_us = PSEC_PER_SEC / (val * MICROHZ_PER_HZ + val2) + * + * The divisor of the formula above reaches its maximum when + * val = val2 = INT_MAX: + * INT_MAX * MICROHZ_PER_HZ + INT_MAX = ~2.15e18 + * so cycle_us_min = floor(1e12 / 2.15e18) = 0. + * + * The divisor reaches its minimum (1) when val = 0 and val2 = 1, + * so cycle_us_max = 1e12 / 1 = 1e12. + * + * Therefore: + * wait_us_min = 0 - 2400 - 612000 = -616800 + * wait_us_max = 1e12 - 2400 - 2400 = 999999995200 + * + * Both fit comfortably in s64. + */ + wait_us = (s64)cycle_us - init_us - atime_us; + if (wait_us < 2400) { + if (data->enable & TCS3472_ENABLE_WEN) { + u8 enable = data->enable & ~TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ENABLE, enable); + if (ret) + return ret; + + data->enable = enable; + } + + data->target_freq_hz = val; + data->target_freq_uhz = val2; + return 0; + } + + /* + * Wait state is needed: make sure WEN is active before programming + * WTIME (and possibly WLONG). + */ + if (!(data->enable & TCS3472_ENABLE_WEN)) { + u8 enable = data->enable | TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + enable); + if (ret) + return ret; + + data->enable = enable; + } + + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 2400); + if (wtime < 0) { + /* + * If wait_us is too high (so the requested frequency is too + * low), the resulting wait exceeds what WTIME can represent + * (max 614 ms without WLONG). Enable WLONG, whose step is 12x + * longer (28.8 ms instead of 2.4 ms), and recompute. + */ + wlong = true; + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 28800); + } + + if (wlong != data->wlong) { + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + + config = ret; + if (wlong) + config |= TCS3472_CONFIG_WLONG; + else + config &= ~TCS3472_CONFIG_WLONG; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_CONFIG, + config); + if (ret) + return ret; + + data->wlong = wlong; + } + + /* + * If the requested wait is so long that even WLONG cannot + * cover it, wtime may still be negative. Saturate to 0, + * which is the largest possible wait (256 * 28.8 ms = 7.37 s). + */ + wtime = clamp(wtime, 0, 255); + ret = i2c_smbus_write_byte_data(data->client, TCS3472_WTIME, wtime); + if (ret) + return ret; + + data->wtime = wtime; + data->target_freq_hz = val; + data->target_freq_uhz = val2; + + return 0; +} + +static int tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + guard(mutex)(&data->lock); + return __tcs3472_set_sampling_freq(data, val, val2); +} + static int tcs3472_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct tcs3472_data *data = iio_priv(indio_dev); + int ret; int i; switch (mask) { @@ -196,15 +410,31 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev, if (val != 0) return -EINVAL; for (i = 0; i < 256; i++) { - if (val2 == (256 - i) * 2400) { - data->atime = i; - return i2c_smbus_write_byte_data( - data->client, TCS3472_ATIME, - data->atime); - } + if (val2 != (256 - i) * 2400) + continue; + guard(mutex)(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ATIME, i); + if (ret) + return ret; + + data->atime = i; + + /* + * ATIME just changed, so the cycle time changed too. + * Re-run the sampling frequency logic to recompute + * WTIME and preserve the user's last requested + * frequency. Lock is already held. + */ + return __tcs3472_set_sampling_freq(data, + data->target_freq_hz, + data->target_freq_uhz); } return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + return tcs3472_set_sampling_freq(data, val, val2); default: return -EINVAL; } @@ -234,7 +464,7 @@ static int tcs3472_read_event(struct iio_dev *indio_dev, data->high_thresh : data->low_thresh; return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: - period = (256 - data->atime) * 2400 * + period = tcs3472_cycle_time_us(data) * tcs3472_intr_pers[data->apers]; *val = period / USEC_PER_SEC; *val2 = period % USEC_PER_SEC; @@ -279,11 +509,13 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->low_thresh = val; return 0; - case IIO_EV_INFO_PERIOD: + case IIO_EV_INFO_PERIOD:{ + unsigned int cycle_us; + period = val * USEC_PER_SEC + val2; + cycle_us = tcs3472_cycle_time_us(data); for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { - if (period <= (256 - data->atime) * 2400 * - tcs3472_intr_pers[i]) + if (period <= cycle_us * tcs3472_intr_pers[i]) break; } ret = i2c_smbus_write_byte_data(data->client, TCS3472_PERS, i); @@ -293,6 +525,7 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->apers = i; return 0; + } default: return -EINVAL; } @@ -434,17 +667,16 @@ static const struct iio_info tcs3472_info = { static int tcs3472_powerdown(struct tcs3472_data *data) { int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + data->enable_pre_suspend = data->enable; + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); + data->enable & ~TCS3472_ENABLE_RUN); if (ret) return ret; - data->enable &= ~enable_mask; - return 0; } @@ -458,7 +690,9 @@ static int tcs3472_probe(struct i2c_client *client) struct device *dev = &client->dev; struct tcs3472_data *data; struct iio_dev *indio_dev; + unsigned int cycle_us; int ret; + u8 enable; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -498,6 +732,16 @@ static int tcs3472_probe(struct i2c_client *client) return ret; data->atime = ret; + ret = i2c_smbus_read_byte_data(data->client, TCS3472_WTIME); + if (ret < 0) + return ret; + data->wtime = ret; + + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + data->wlong = (ret & TCS3472_CONFIG_WLONG) ? 1 : 0; + ret = i2c_smbus_read_word_data(data->client, TCS3472_AILT); if (ret < 0) return ret; @@ -518,14 +762,30 @@ static int tcs3472_probe(struct i2c_client *client) if (ret < 0) return ret; - /* enable device */ - data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; - data->enable &= ~TCS3472_ENABLE_AIEN; - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable); + /* + * Enable the chip in its full running state, including WEN. The + * actual wait time is controlled by the WTIME and WLONG registers, + * which retain their power-on defaults until userspace writes to + * sampling_frequency. + */ + enable = (ret | TCS3472_ENABLE_RUN) & ~TCS3472_ENABLE_AIEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, enable); if (ret < 0) return ret; + data->enable = enable; + + /* + * Initialize target frequency from the chip's current state so that + * subsequent integration_time changes via IIO_CHAN_INFO_INT_TIME can + * preserve a meaningful sampling rate, even before userspace writes + * sampling_frequency for the first time. + */ + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, &data->target_freq_hz, + &data->target_freq_uhz); + ret = devm_add_action_or_reset(dev, tcs3472_powerdown_action, data); if (ret) return ret; @@ -561,16 +821,21 @@ static int tcs3472_resume(struct device *dev) struct tcs3472_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + /* + * Restore the full ENABLE register from the snapshot taken in + * tcs3472_powerdown(). This preserves the user's last + * sampling_frequency configuration (in particular the WEN bit) + * across suspend/resume. + */ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable | enable_mask); + data->enable_pre_suspend); if (ret) return ret; - data->enable |= enable_mask; + data->enable = data->enable_pre_suspend; return 0; } diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 45f3513d931e..7bef0c3118c2 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -754,7 +754,7 @@ static int tsl2563_probe(struct i2c_client *client) "tsl2563_event", indio_dev); if (err) - return dev_err_probe(dev, err, "irq request error\n"); + return err; } err = tsl2563_configure(chip); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index a0dd122af2cf..53fd423aa7ae 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -456,12 +456,8 @@ static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev) usleep_range(3000, 3500); - ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON | - TSL2583_CNTL_ADC_ENBL); - if (ret < 0) - return ret; - - return ret; + return tsl2583_set_power_state(chip, + TSL2583_CNTL_PWR_ON | TSL2583_CNTL_ADC_ENBL); } /* Sysfs Interface Functions */ @@ -475,7 +471,7 @@ static ssize_t in_illuminance_input_target_show(struct device *dev, int ret; mutex_lock(&chip->als_mutex); - ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target); + ret = sysfs_emit(buf, "%d\n", chip->als_settings.als_cal_target); mutex_unlock(&chip->als_mutex); return ret; @@ -533,10 +529,10 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, int offset = 0; for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) { - offset += sprintf(buf + offset, "%u,%u,%u,", - chip->als_settings.als_device_lux[i].ratio, - chip->als_settings.als_device_lux[i].ch0, - chip->als_settings.als_device_lux[i].ch1); + offset += sysfs_emit_at(buf, offset, "%u,%u,%u,", + chip->als_settings.als_device_lux[i].ratio, + chip->als_settings.als_device_lux[i].ch0, + chip->als_settings.als_device_lux[i].ch1); if (chip->als_settings.als_device_lux[i].ratio == 0) { /* * We just printed the first "0" entry. @@ -547,7 +543,7 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, } } - offset += sprintf(buf + offset, "\n"); + offset += sysfs_emit_at(buf, offset, "\n"); return offset; } @@ -639,14 +635,6 @@ static const struct iio_chan_spec tsl2583_channels[] = { }, }; -static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) -{ - if (on) - return pm_runtime_resume_and_get(&chip->client->dev); - - return pm_runtime_put_autosuspend(&chip->client->dev); -} - static int tsl2583_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -654,7 +642,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret, pm_ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -722,16 +710,16 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } /* * Preserve the ret variable if the call to - * tsl2583_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = tsl2583_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(&chip->client->dev); if (pm_ret < 0) return pm_ret; @@ -745,7 +733,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -786,15 +774,15 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } - ret = tsl2583_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(&chip->client->dev); if (ret < 0) return ret; - return ret; + return 0; } static const struct iio_info tsl2583_info = { diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 244f44379c36..4486a1d9d84d 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1274,7 +1274,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev, } break; case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_LIGHT) + if (chan->type == IIO_INTENSITY) *val = tsl2772_als_gain[chip->settings.als_gain]; else *val = tsl2772_prox_gain[chip->settings.prox_gain]; @@ -1849,11 +1849,8 @@ static int tsl2772_probe(struct i2c_client *clientp) IRQF_ONESHOT, "TSL2772_event", indio_dev); - if (ret) { - dev_err(&clientp->dev, - "%s: irq request failed\n", __func__); + if (ret) return ret; - } } else { indio_dev->channels = chip->chip_info->channel_without_events; } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 128ae3f94074..336468d59ea7 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -537,16 +537,6 @@ static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) return !!(ret & VCNL4000_SELF_TIMED_EN); } -static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) { int ret; @@ -850,7 +840,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4000_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -869,7 +859,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } - vcnl4000_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) @@ -1922,7 +1912,7 @@ static void vcnl4000_cleanup(void *data) static int vcnl4000_probe(struct i2c_client *client) { - const char * const regulator_names[] = { "vdd", "vio", "vled" }; + static const char * const regulator_names[] = { "vdd", "vio", "vled" }; struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index bf3a49b4351d..ce1bd42b0c7c 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -145,16 +145,6 @@ static const struct iio_trigger_ops vcnl4035_trigger_ops = { .set_trigger_state = vcnl4035_als_drdy_set_state, }; -static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4035_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -202,11 +192,11 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; ret = vcnl4035_read_info_raw(indio_dev, chan, val); - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_INT_TIME: *val = 50; @@ -237,7 +227,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (val <= 0 || val > 800) return -EINVAL; - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -247,7 +237,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (!ret) data->als_it_val = val / 100; - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; default: return -EINVAL; @@ -647,7 +637,10 @@ static int vcnl4035_runtime_resume(struct device *dev) struct vcnl4035_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); if (ret < 0) return ret; diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 7c837d60dad8..ec9c127e7d8a 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -921,9 +921,7 @@ static int veml6030_set_info(struct iio_dev *indio_dev) IRQF_TRIGGER_LOW | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret < 0) - return dev_err_probe(&client->dev, ret, - "irq %d request failed\n", - client->irq); + return ret; indio_dev->info = &veml6030_info; } else { diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 4f270f405b21..aef8a5333442 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -721,7 +721,7 @@ static int vl6180_probe(struct i2c_client *client) IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, "devm_request_irq error\n"); + return ret; init_completion(&data->completion); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index ea2697fb5ab6..d359003b248e 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -209,6 +209,28 @@ config INFINEON_TLV493D To compile this driver as a module, choose M here: the module will be called tlv493d. +config QMC5883L + tristate "QST QMC5883L 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support driver for QST QMC5883L 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc5883l. + +config QMC6308 + tristate "QST QMC6308 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the QST QMC6308 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc6308. + config SENSORS_HMC5843 tristate select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 7fd9b3fd914e..8f2205975c1b 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -27,6 +27,9 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o +obj-$(CONFIG_QMC5883L) += qmc5883l.o +obj-$(CONFIG_QMC6308) += qmc6308.o + obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index c7fdb7c2f543..375023db2758 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -379,11 +379,7 @@ static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result) return -ERANGE; } - ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); - if (ret) - return ret; - - return ret; + return regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); } static irqreturn_t ak8974_drdy_irq(int irq, void *d) @@ -926,11 +922,8 @@ static int ak8974_probe(struct i2c_client *i2c) irq_trig, ak8974->name, ak8974); - if (ret) { - dev_err(&i2c->dev, "unable to request DRDY IRQ " - "- proceeding without IRQ\n"); + if (ret) goto no_irq; - } ak8974->drdy_irq = true; } diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index bf2551988008..32dff9cdd0f7 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -9,23 +9,25 @@ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved */ -#include -#include -#include +#include #include #include -#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include + #include #include +#include +#include #include #include #include -#include -#include #include "bmc150_magn.h" @@ -245,14 +247,14 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_SLEEP << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_SLEEP)); case BMC150_MAGN_POWER_MODE_NORMAL: return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_NORMAL << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_NORMAL)); } return -EINVAL; @@ -290,7 +292,7 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, ®_val); if (ret < 0) return ret; - odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR; + odr_val = FIELD_GET(BMC150_MAGN_MASK_ODR, reg_val); for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) { @@ -303,21 +305,17 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val) { - int ret; u8 i; for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) { - if (bmc150_magn_samp_freq_table[i].freq == val) { - ret = regmap_update_bits(data->regmap, - BMC150_MAGN_REG_OPMODE_ODR, - BMC150_MAGN_MASK_ODR, - bmc150_magn_samp_freq_table[i]. - reg_val << - BMC150_MAGN_SHIFT_ODR); - if (ret < 0) - return ret; - return 0; - } + if (bmc150_magn_samp_freq_table[i].freq != val) + continue; + + return regmap_update_bits(data->regmap, + BMC150_MAGN_REG_OPMODE_ODR, + BMC150_MAGN_MASK_ODR, + FIELD_PREP(BMC150_MAGN_MASK_ODR, + bmc150_magn_samp_freq_table[i].reg_val)); } return -EINVAL; @@ -800,7 +798,7 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, BMC150_MAGN_MASK_DRDY_EN, - state << BMC150_MAGN_SHIFT_DRDY_EN); + FIELD_PREP(BMC150_MAGN_MASK_DRDY_EN, state)); if (ret < 0) return ret; diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index 8af8d3528c43..27ab845fb257 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -33,9 +33,9 @@ static void bmc150_magn_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmc150_magn_spi_id[] = { - {"bmc150_magn", 0}, - {"bmc156_magn", 0}, - {"bmm150_magn", 0}, + { .name = "bmc150_magn" }, + { .name = "bmc156_magn" }, + { .name = "bmm150_magn" }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index d8b8bcc865c3..ad18f233ee16 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -134,9 +134,8 @@ static const struct iio_chan_spec magn_3d_channels[] = { /* Channel read_raw handler */ static int magn_3d_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) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int report_id = -1; @@ -166,8 +165,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, false); return -EINVAL; } - hid_sensor_power_state(&magn_state->magn_flux_attributes, - false); + hid_sensor_power_state(&magn_state->magn_flux_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: @@ -228,10 +226,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int magn_3d_write_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) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int ret = 0; @@ -269,8 +265,7 @@ static const struct iio_info magn_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -291,9 +286,9 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -326,7 +321,7 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, iio_val = magn_state->magn_val_addr[offset]; - if (iio_val != NULL) + if (iio_val) *iio_val = *((u32 *)raw_data); else ret = -EINVAL; @@ -367,18 +362,17 @@ static int magn_3d_parse_report(struct platform_device *pdev, return -EINVAL; } - dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", - attr_count); + dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", attr_count); dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n", - st->magn[0].index, - st->magn[0].report_id, - st->magn[1].index, st->magn[1].report_id, - st->magn[2].index, st->magn[2].report_id); + st->magn[0].index, + st->magn[0].report_id, + st->magn[1].index, st->magn[1].report_id, + st->magn[2].index, st->magn[2].report_id); /* Setup IIO channel array */ _channels = devm_kcalloc(&pdev->dev, attr_count, - sizeof(struct iio_chan_spec), - GFP_KERNEL); + sizeof(struct iio_chan_spec), + GFP_KERNEL); if (!_channels) { dev_err(&pdev->dev, "failed to allocate space for iio channels\n"); @@ -425,8 +419,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, *channels = _channels; - dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", - *chan_count); + dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", *chan_count); st->magn_flux_attr.scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_COMPASS_3D, @@ -467,7 +460,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct magn_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -490,8 +483,8 @@ static int hid_magn_3d_probe(struct platform_device *pdev) magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, - &channels, &chan_count, - HID_USAGE_SENSOR_COMPASS_3D, magn_state); + &channels, &chan_count, + HID_USAGE_SENSOR_COMPASS_3D, magn_state); if (ret) { dev_err(&pdev->dev, "failed to parse report\n"); return ret; @@ -506,32 +499,32 @@ static int hid_magn_3d_probe(struct platform_device *pdev) atomic_set(&magn_state->magn_flux_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - magn_state->callbacks.send_event = magn_3d_proc_event; magn_state->callbacks.capture_sample = magn_3d_capture_sample; magn_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->callbacks); + &magn_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); return ret; @@ -544,8 +537,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct magn_3d_state *magn_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); } diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index 6a55c1559b0d..29dee5dce2ae 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -56,7 +56,6 @@ static int hmc5843_spi_probe(struct spi_device *spi) { int ret; struct regmap *regmap; - const struct spi_device_id *id = spi_get_device_id(spi); spi->mode = SPI_MODE_3; spi->max_speed_hz = 8000000; @@ -69,8 +68,7 @@ static int hmc5843_spi_probe(struct spi_device *spi) return PTR_ERR(regmap); return hmc5843_common_probe(&spi->dev, - regmap, - id->driver_data, id->name); + regmap, HMC5983_ID, "hmc5983"); } static void hmc5843_spi_remove(struct spi_device *spi) @@ -79,7 +77,7 @@ static void hmc5843_spi_remove(struct spi_device *spi) } static const struct spi_device_id hmc5843_id[] = { - { "hmc5983", HMC5983_ID }, + { .name = "hmc5983" }, { } }; MODULE_DEVICE_TABLE(spi, hmc5843_id); diff --git a/drivers/iio/magnetometer/mmc5983.c b/drivers/iio/magnetometer/mmc5983.c index 03f46e8c1611..d10a6288e147 100644 --- a/drivers/iio/magnetometer/mmc5983.c +++ b/drivers/iio/magnetometer/mmc5983.c @@ -327,7 +327,7 @@ static const struct of_device_id mmc5983_of_match[] = { MODULE_DEVICE_TABLE(of, mmc5983_of_match); static const struct i2c_device_id mmc5983_id[] = { - { "mmc5983" }, + { .name = "mmc5983" }, { } }; MODULE_DEVICE_TABLE(i2c, mmc5983_id); diff --git a/drivers/iio/magnetometer/qmc5883l.c b/drivers/iio/magnetometer/qmc5883l.c new file mode 100644 index 000000000000..c3469715dc63 --- /dev/null +++ b/drivers/iio/magnetometer/qmc5883l.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for QST QMC5883L 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Siratul Islam + * + * Datasheet available at + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define QMC5883L_REG_X_LSB 0x00 +#define QMC5883L_REG_STATUS1 0x06 +#define QMC5883L_REG_CTRL1 0x09 +#define QMC5883L_REG_CTRL2 0x0A +#define QMC5883L_REG_SET_RESET 0x0B +#define QMC5883L_REG_ID 0x0D + +#define QMC5883L_CHIP_ID 0xFF + +#define QMC5883L_MODE_MASK GENMASK(1, 0) +#define QMC5883L_ODR_MASK GENMASK(3, 2) +#define QMC5883L_RNG_MASK GENMASK(5, 4) +#define QMC5883L_OSR_MASK GENMASK(7, 6) + +#define QMC5883L_MODE_STANDBY 0x00 +#define QMC5883L_MODE_CONT 0x01 + +#define QMC5883L_ODR_10HZ 0x00 +#define QMC5883L_ODR_50HZ 0x01 +#define QMC5883L_ODR_100HZ 0x02 +#define QMC5883L_ODR_200HZ 0x03 + +#define QMC5883L_RNG_2G 0x00 +#define QMC5883L_RNG_8G 0x01 + +#define QMC5883L_OSR_512 0x00 +#define QMC5883L_OSR_256 0x01 +#define QMC5883L_OSR_128 0x02 +#define QMC5883L_OSR_64 0x03 + +#define QMC5883L_STATUS_DRDY BIT(0) +#define QMC5883L_STATUS_OVL BIT(1) + +#define QMC5883L_SET_RESET_VAL BIT(0) +#define QMC5883L_INT_DISABLE BIT(0) +#define QMC5883L_SOFT_RESET BIT(7) + +#define QMC5883L_PORT_US 350 + +struct qmc5883l_data { + struct regmap *regmap; + /* + * Protect data->range/odr/osr. + * Protect poll and read during measurement. + */ + struct mutex mutex; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc5883l_chan { + QMC5883L_AXIS_X, + QMC5883L_AXIS_Y, + QMC5883L_AXIS_Z, +}; + +static const int qmc5883l_odr_avail[] = { 10, 50, 100, 200 }; + +static const int qmc5883l_osr_avail[] = { 512, 256, 128, 64 }; + +static const int qmc5883l_scales[][2] = { + [QMC5883L_RNG_2G] = { 0, 83333 }, + [QMC5883L_RNG_8G] = { 0, 333333 }, +}; + +static int qmc5883l_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + struct regmap *map = data->regmap; + unsigned int status; + __le16 buf[3]; + int ret; + + guard(mutex) (&data->mutex); + + /* 50ms headroom over the slowest ODR (10Hz) */ + ret = regmap_read_poll_timeout(map, QMC5883L_REG_STATUS1, + status, (status & QMC5883L_STATUS_DRDY), + 2 * USEC_PER_MSEC, 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(map, QMC5883L_REG_X_LSB, buf, sizeof(buf)); + if (ret) + return ret; + + if (status & QMC5883L_STATUS_OVL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc5883l_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc5883l_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc5883l_scales[data->range][0]; + *val2 = qmc5883l_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + switch (data->odr) { + case QMC5883L_ODR_200HZ: + *val = 200; + break; + case QMC5883L_ODR_100HZ: + *val = 100; + break; + case QMC5883L_ODR_50HZ: + *val = 50; + break; + case QMC5883L_ODR_10HZ: + *val = 10; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + switch (data->osr) { + case QMC5883L_OSR_64: + *val = 64; + break; + case QMC5883L_OSR_128: + *val = 128; + break; + case QMC5883L_OSR_256: + *val = 256; + break; + case QMC5883L_OSR_512: + *val = 512; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + u8 rng, osr, odr; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + if (val2 == qmc5883l_scales[QMC5883L_RNG_2G][1]) + rng = QMC5883L_RNG_2G; + else if (val2 == qmc5883l_scales[QMC5883L_RNG_8G][1]) + rng = QMC5883L_RNG_8G; + else + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_RNG_MASK, + FIELD_PREP(QMC5883L_RNG_MASK, rng)); + if (ret) + return ret; + + data->range = rng; + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + switch (val) { + case 200: + odr = QMC5883L_ODR_200HZ; + break; + case 100: + odr = QMC5883L_ODR_100HZ; + break; + case 50: + odr = QMC5883L_ODR_50HZ; + break; + case 10: + odr = QMC5883L_ODR_10HZ; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_ODR_MASK, + FIELD_PREP(QMC5883L_ODR_MASK, odr)); + if (ret) + return ret; + + data->odr = odr; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + switch (val) { + case 64: + osr = QMC5883L_OSR_64; + break; + case 128: + osr = QMC5883L_OSR_128; + break; + case 256: + osr = QMC5883L_OSR_256; + break; + case 512: + osr = QMC5883L_OSR_512; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_OSR_MASK, + FIELD_PREP(QMC5883L_OSR_MASK, osr)); + if (ret) + return ret; + + data->osr = osr; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = qmc5883l_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc5883l_osr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_osr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc5883l_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc5883l_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc5883l_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT; + } +} + +static const struct iio_info qmc5883l_info = { + .read_raw = qmc5883l_read_raw, + .write_raw = qmc5883l_write_raw, + .read_avail = qmc5883l_read_avail, + .write_raw_get_fmt = qmc5883l_write_raw_get_fmt, +}; + +static int qmc5883l_init(struct qmc5883l_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC5883L_REG_ID, ®); + if (ret) + return ret; + + /* Not failing because rev 1.0 had this register reserved */ + if (reg != QMC5883L_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_SOFT_RESET); + if (ret) + return ret; + + /* Use POR completion time as a conservative bound */ + fsleep(QMC5883L_PORT_US); + + /* DRDY pin not used in this version of the driver */ + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_INT_DISABLE); + if (ret) + return ret; + + ret = regmap_write(map, QMC5883L_REG_SET_RESET, QMC5883L_SET_RESET_VAL); + if (ret) + return ret; + + data->odr = QMC5883L_ODR_50HZ; + data->range = QMC5883L_RNG_2G; + data->osr = QMC5883L_OSR_64; + + return regmap_write(map, QMC5883L_REG_CTRL1, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_CONT) | + FIELD_PREP(QMC5883L_ODR_MASK, data->odr) | + FIELD_PREP(QMC5883L_RNG_MASK, data->range) | + FIELD_PREP(QMC5883L_OSR_MASK, data->osr)); +} + +static void qmc5883l_power_down_action(void *priv) +{ + struct qmc5883l_data *data = priv; + + regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_MODE_MASK, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_STANDBY)); +} + +static bool qmc5883l_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg <= QMC5883L_REG_STATUS1; +} + +static bool qmc5883l_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC5883L_REG_CTRL1: + case QMC5883L_REG_CTRL2: + case QMC5883L_REG_SET_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc5883l_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC5883L_REG_ID, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc5883l_volatile_reg, + .writeable_reg = qmc5883l_writable_reg, +}; + +#define QMC5883L_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC5883L_AXIS_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + } + +static const struct iio_chan_spec qmc5883l_channels[] = { + QMC5883L_CHANNEL(X), + QMC5883L_CHANNEL(Y), + QMC5883L_CHANNEL(Z), +}; + +static int qmc5883l_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc5883l_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &qmc5883l_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDD regulator\n"); + + ret = devm_regulator_get_enable(dev, "vddio"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDDIO regulator\n"); + + /* POR completion time max per datasheet Table 7 */ + fsleep(QMC5883L_PORT_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + indio_dev->name = "qmc5883l"; + indio_dev->info = &qmc5883l_info; + indio_dev->channels = qmc5883l_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc5883l_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc5883l_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc5883l init failed\n"); + + ret = devm_add_action_or_reset(dev, qmc5883l_power_down_action, data); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id qmc5883l_match[] = { + { .compatible = "qstcorp,qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc5883l_match); + +static const struct i2c_device_id qmc5883l_id[] = { + { .name = "qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc5883l_id); + +static struct i2c_driver qmc5883l_driver = { + .driver = { + .name = "qmc5883l", + .of_match_table = qmc5883l_match, + }, + .id_table = qmc5883l_id, + .probe = qmc5883l_probe, +}; +module_i2c_driver(qmc5883l_driver); + +MODULE_DESCRIPTION("QST QMC5883L 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Siratul Islam "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c new file mode 100644 index 000000000000..72348cd9ef01 --- /dev/null +++ b/drivers/iio/magnetometer/qmc6308.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support for QST QMC6308 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Jorijn van der Graaf + * + * Datasheet available at + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define QMC6308_REG_ID 0x00 +#define QMC6308_REG_X_LSB 0x01 +#define QMC6308_REG_STATUS 0x09 +#define QMC6308_REG_CTRL1 0x0A +#define QMC6308_REG_CTRL2 0x0B +#define QMC6308_REG_CTRL3 0x0D +#define QMC6308_REG_CTRL4 0x29 + +#define QMC6308_CHIP_ID 0x80 + +/* Control register 1 */ +#define QMC6308_MODE_MASK GENMASK(1, 0) +#define QMC6308_ODR_MASK GENMASK(3, 2) +#define QMC6308_OSR1_MASK GENMASK(5, 4) +#define QMC6308_OSR2_MASK GENMASK(7, 6) + +#define QMC6308_MODE_SUSPEND 0x00 +#define QMC6308_MODE_NORMAL 0x01 + +#define QMC6308_ODR_10HZ 0x00 +#define QMC6308_ODR_50HZ 0x01 +#define QMC6308_ODR_100HZ 0x02 +#define QMC6308_ODR_200HZ 0x03 + +#define QMC6308_OSR1_8 0x00 +#define QMC6308_OSR1_4 0x01 +#define QMC6308_OSR1_2 0x02 +#define QMC6308_OSR1_1 0x03 + +/* Control register 2 */ +#define QMC6308_SET_RESET_MASK GENMASK(1, 0) +#define QMC6308_RNG_MASK GENMASK(3, 2) +#define QMC6308_SELF_TEST BIT(6) +#define QMC6308_SOFT_RST BIT(7) + +#define QMC6308_SET_RESET_ON 0x00 + +#define QMC6308_RNG_30G 0x00 +#define QMC6308_RNG_12G 0x01 +#define QMC6308_RNG_8G 0x02 +#define QMC6308_RNG_2G 0x03 + +/* Status register */ +#define QMC6308_STATUS_DRDY BIT(0) +#define QMC6308_STATUS_OVFL BIT(1) + +/* Power-on completion time (datasheet Table 7) */ +#define QMC6308_POR_US 250 + +#define QMC6308_AUTOSUSPEND_DELAY_MS 500 + +struct qmc6308_data { + struct regmap *regmap; + /* Protect data->range/odr/osr and serialize measurements */ + struct mutex mutex; + struct iio_mount_matrix orientation; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc6308_axis { + QMC6308_AXIS_X, + QMC6308_AXIS_Y, + QMC6308_AXIS_Z, +}; + +static const int qmc6308_odr_avail[] = { + [QMC6308_ODR_10HZ] = 10, + [QMC6308_ODR_50HZ] = 50, + [QMC6308_ODR_100HZ] = 100, + [QMC6308_ODR_200HZ] = 200, +}; + +static const int qmc6308_osr1_avail[] = { + [QMC6308_OSR1_8] = 8, + [QMC6308_OSR1_4] = 4, + [QMC6308_OSR1_2] = 2, + [QMC6308_OSR1_1] = 1, +}; + +/* + * Sensitivity is 1000/2500/3750/15000 LSB/Gauss for the + * +-30/12/8/2 Gauss ranges respectively. + */ +static const int qmc6308_scales[][2] = { + [QMC6308_RNG_30G] = { 0, 1000000 }, + [QMC6308_RNG_12G] = { 0, 400000 }, + [QMC6308_RNG_8G] = { 0, 266667 }, + [QMC6308_RNG_2G] = { 0, 66667 }, +}; + +static int qmc6308_set_mode(struct qmc6308_data *data, unsigned int mode) +{ + return regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_MODE_MASK, + FIELD_PREP(QMC6308_MODE_MASK, mode)); +} + +static int qmc6308_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int status; + __le16 buf[3]; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(dev, "Failed to power on (%d)\n", ret); + return ret; + } + + guard(mutex)(&data->mutex); + + /* + * Reading the status register clears DRDY, which is why the poll + * and the data read stay under one mutex hold. A runtime resume + * clears DRDY too, so a sample converted before the last suspend + * is never returned here. + * + * The timeout is 50ms of headroom over the slowest ODR (10Hz). + */ + ret = regmap_read_poll_timeout(data->regmap, QMC6308_REG_STATUS, + status, (status & QMC6308_STATUS_DRDY), + 2 * USEC_PER_MSEC, + 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, QMC6308_REG_X_LSB, buf, + sizeof(buf)); + if (ret) + return ret; + + if (status & QMC6308_STATUS_OVFL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc6308_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc6308_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc6308_scales[data->range][0]; + *val2 = qmc6308_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + *val = qmc6308_odr_avail[data->odr]; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + *val = qmc6308_osr1_avail[data->osr]; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc6308_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + unsigned int i; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(qmc6308_scales); i++) { + if (val2 == qmc6308_scales[i][1]) + break; + } + if (i == ARRAY_SIZE(qmc6308_scales)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL2, + QMC6308_RNG_MASK, + FIELD_PREP(QMC6308_RNG_MASK, i)); + if (ret) + return ret; + + data->range = i; + + /* + * The data registers still hold (and DRDY still + * advertises) a sample converted at the previous range; + * discard it so that a read does not pair old-range data + * with the new scale. A conversion already in flight may + * still complete at the old range, so this narrows the + * window rather than closing it. The range change itself + * took effect, so only log a failure here: an error + * would mislead userspace about an effective write. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, + &status); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to discard stale sample (%d)\n", ret); + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + for (i = 0; i < ARRAY_SIZE(qmc6308_odr_avail); i++) { + if (val == qmc6308_odr_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_odr_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_ODR_MASK, + FIELD_PREP(QMC6308_ODR_MASK, i)); + if (ret) + return ret; + + data->odr = i; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + for (i = 0; i < ARRAY_SIZE(qmc6308_osr1_avail); i++) { + if (val == qmc6308_osr1_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_osr1_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_OSR1_MASK, + FIELD_PREP(QMC6308_OSR1_MASK, i)); + if (ret) + return ret; + + data->osr = i; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc6308_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = qmc6308_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc6308_osr1_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_osr1_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc6308_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc6308_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc6308_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT; + } +} + +static const struct iio_mount_matrix * +qmc6308_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info qmc6308_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qmc6308_get_mount_matrix), + { } +}; + +static const struct iio_info qmc6308_info = { + .read_raw = qmc6308_read_raw, + .write_raw = qmc6308_write_raw, + .read_avail = qmc6308_read_avail, + .write_raw_get_fmt = qmc6308_write_raw_get_fmt, +}; + +static int qmc6308_init(struct qmc6308_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC6308_REG_ID, ®); + if (ret) + return ret; + + /* Allow unknown IDs so that fallback compatibles work */ + if (reg != QMC6308_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + /* The SOFT_RST bit is not auto-cleared and must be written back 0 */ + ret = regmap_write(map, QMC6308_REG_CTRL2, QMC6308_SOFT_RST); + if (ret) + return ret; + + /* + * The datasheet gives no soft-reset completion figure; reuse the + * power-on time as a conservative bound. + */ + fsleep(QMC6308_POR_US); + + data->range = QMC6308_RNG_30G; + data->odr = QMC6308_ODR_50HZ; + data->osr = QMC6308_OSR1_8; + + ret = regmap_write(map, QMC6308_REG_CTRL2, + FIELD_PREP(QMC6308_SET_RESET_MASK, + QMC6308_SET_RESET_ON) | + FIELD_PREP(QMC6308_RNG_MASK, data->range)); + if (ret) + return ret; + + /* OSR2 (second-stage filter) set to its power-on default of 0 */ + return regmap_write(map, QMC6308_REG_CTRL1, + FIELD_PREP(QMC6308_MODE_MASK, + QMC6308_MODE_NORMAL) | + FIELD_PREP(QMC6308_ODR_MASK, data->odr) | + FIELD_PREP(QMC6308_OSR1_MASK, data->osr) | + FIELD_PREP(QMC6308_OSR2_MASK, 0)); +} + +static void qmc6308_power_down_action(void *priv) +{ + struct qmc6308_data *data = priv; + + if (!pm_runtime_status_suspended(regmap_get_device(data->regmap))) + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static bool qmc6308_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg >= QMC6308_REG_X_LSB && reg <= QMC6308_REG_STATUS; +} + +static bool qmc6308_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC6308_REG_CTRL1: + case QMC6308_REG_CTRL2: + case QMC6308_REG_CTRL3: + case QMC6308_REG_CTRL4: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc6308_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC6308_REG_CTRL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc6308_volatile_reg, + .writeable_reg = qmc6308_writable_reg, +}; + +#define QMC6308_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC6308_AXIS_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = qmc6308_ext_info, \ + } + +static const struct iio_chan_spec qmc6308_channels[] = { + QMC6308_CHANNEL(X), + QMC6308_CHANNEL(Y), + QMC6308_CHANNEL(Z), +}; + +static int qmc6308_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc6308_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + map = devm_regmap_init_i2c(client, &qmc6308_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDD regulator\n"); + + fsleep(QMC6308_POR_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read mount matrix\n"); + + indio_dev->name = "qmc6308"; + indio_dev->info = &qmc6308_info; + indio_dev->channels = qmc6308_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc6308_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc6308_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc6308 init failed\n"); + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, qmc6308_power_down_action, data); + if (ret) + return ret; + + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, QMC6308_AUTOSUSPEND_DELAY_MS); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int qmc6308_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + + return qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static int qmc6308_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + int ret; + + ret = qmc6308_set_mode(data, QMC6308_MODE_NORMAL); + if (ret) + return ret; + + /* + * DRDY may still be set for a sample converted before the last + * suspend; reading the status register clears it so the next + * measurement waits for fresh data. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, &status); + if (ret) { + /* Best effort to leave the chip in a consistent state */ + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); + } + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(qmc6308_pm_ops, qmc6308_runtime_suspend, + qmc6308_runtime_resume, NULL); + +static const struct of_device_id qmc6308_match[] = { + { .compatible = "qstcorp,qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc6308_match); + +static const struct i2c_device_id qmc6308_id[] = { + { .name = "qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc6308_id); + +static struct i2c_driver qmc6308_driver = { + .driver = { + .name = "qmc6308", + .of_match_table = qmc6308_match, + .pm = pm_ptr(&qmc6308_pm_ops), + }, + .id_table = qmc6308_id, + .probe = qmc6308_probe, +}; +module_i2c_driver(qmc6308_driver); + +MODULE_DESCRIPTION("QST QMC6308 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Jorijn van der Graaf "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index ac3f9f7fc808..46fce2978d76 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -568,10 +568,8 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request irq line failed.\n"); + if (ret) return ret; - } data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", indio_dev->name, diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 69693e836ad7..af4642b6c3c0 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -87,12 +87,12 @@ static int st_magn_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_magn_id_table[] = { - { LIS3MDL_MAGN_DEV_NAME }, - { LSM303AGR_MAGN_DEV_NAME }, - { LIS2MDL_MAGN_DEV_NAME }, - { LSM9DS1_MAGN_DEV_NAME }, - { IIS2MDC_MAGN_DEV_NAME }, - { LSM303C_MAGN_DEV_NAME }, + { .name = LIS3MDL_MAGN_DEV_NAME }, + { .name = LSM303AGR_MAGN_DEV_NAME }, + { .name = LIS2MDL_MAGN_DEV_NAME }, + { .name = LSM9DS1_MAGN_DEV_NAME }, + { .name = IIS2MDC_MAGN_DEV_NAME }, + { .name = LSM303C_MAGN_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 4421dafcf94e..562457a67382 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -463,3 +463,4 @@ module_platform_driver(mux_driver); MODULE_DESCRIPTION("IIO multiplexer driver"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index ea60611192f3..aea28321db95 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -85,8 +85,7 @@ static const struct iio_chan_spec incl_3d_channels[] = { }; /* Adjust channel real bits based on report descriptor */ -static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, - int size) +static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, int size) { chan->scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -97,9 +96,8 @@ static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, /* Channel read_raw handler */ static int incl_3d_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) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int report_id = -1; @@ -124,7 +122,7 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { hid_sensor_power_state(&incl_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&incl_state->common_attributes, false); @@ -157,10 +155,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int incl_3d_write_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) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int ret; @@ -188,8 +184,7 @@ static const struct iio_info incl_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -211,9 +206,9 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -246,7 +241,7 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int incl_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct incl_3d_state *st) { int ret; @@ -306,7 +301,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct incl_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -349,33 +344,33 @@ static int hid_incl_3d_probe(struct platform_device *pdev) atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &incl_state->common_attributes); + &incl_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - incl_state->callbacks.send_event = incl_3d_proc_event; incl_state->callbacks.capture_sample = incl_3d_capture_sample; incl_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, - HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->callbacks); + HID_USAGE_SENSOR_INCLINOMETER_3D, + &incl_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); return ret; @@ -388,8 +383,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct incl_3d_state *incl_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); } diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index cc3e66dbb90f..f8c98f866cb0 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -80,9 +80,8 @@ static const struct iio_chan_spec dev_rot_channels[] = { /* Channel read_raw handler */ static int dev_rot_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int size, int *vals, int *val_len, - long mask) + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; @@ -164,10 +163,8 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int dev_rot_write_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) { struct dev_rot_state *rot_state = iio_priv(indio_dev); int ret; @@ -211,8 +208,7 @@ static const struct iio_info dev_rot_info = { /* Callback handler to send event after all samples are received and captured */ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -244,9 +240,9 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -275,7 +271,7 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int dev_rot_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, struct dev_rot_state *st) { int ret; @@ -313,7 +309,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct dev_rot_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -361,32 +357,32 @@ static int hid_dev_rot_probe(struct platform_device *pdev) atomic_set(&rot_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &rot_state->common_attributes); + &rot_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - rot_state->callbacks.send_event = dev_rot_proc_event; rot_state->callbacks.capture_sample = dev_rot_capture_sample; rot_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &rot_state->callbacks); + &rot_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); return ret; @@ -399,8 +395,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dev_rot_state *rot_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); } diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index bffa8fe4fe44..d275bc1413fe 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -106,8 +106,8 @@ static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels, /* Channel read_raw handler */ static int hinge_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) { struct hinge_state *st = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -153,8 +153,8 @@ static int hinge_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int hinge_write_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) { struct hinge_state *st = iio_priv(indio_dev); @@ -189,7 +189,7 @@ static const struct iio_info hinge_info = { * and captured. */ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -208,8 +208,9 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -235,7 +236,7 @@ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, static int hinge_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned int usage_id, struct hinge_state *st) + u32 usage_id, struct hinge_state *st) { int ret; int i; @@ -291,7 +292,7 @@ static int hid_hinge_probe(struct platform_device *pdev) } indio_dev->num_channels = ARRAY_SIZE(hinge_channels); - indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels, + indio_dev->channels = devm_kmemdup(&pdev->dev, hinge_channels, sizeof(hinge_channels), GFP_KERNEL); if (!indio_dev->channels) return -ENOMEM; diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index ddf54a1df976..ec1b7bdc706b 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -169,10 +169,10 @@ static int max5481_probe(struct spi_device *spi) } static const struct spi_device_id max5481_id_table[] = { - { "max5481", max5481 }, - { "max5482", max5482 }, - { "max5483", max5483 }, - { "max5484", max5484 }, + { .name = "max5481", .driver_data = max5481 }, + { .name = "max5482", .driver_data = max5482 }, + { .name = "max5483", .driver_data = max5483 }, + { .name = "max5484", .driver_data = max5484 }, { } }; MODULE_DEVICE_TABLE(spi, max5481_id_table); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 9541695af474..1f0d0056f412 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -125,9 +125,9 @@ static void max5487_spi_remove(struct spi_device *spi) } static const struct spi_device_id max5487_id[] = { - { "MAX5487", 10 }, - { "MAX5488", 50 }, - { "MAX5489", 100 }, + { .name = "MAX5487", .driver_data = 10 }, + { .name = "MAX5488", .driver_data = 50 }, + { .name = "MAX5489", .driver_data = 100 }, { } }; MODULE_DEVICE_TABLE(spi, max5487_id); diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index ed0764231224..cfd5d0f6d368 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -175,12 +175,12 @@ static const struct of_device_id mcp41010_match[] = { MODULE_DEVICE_TABLE(of, mcp41010_match); static const struct spi_device_id mcp41010_id[] = { - { "mcp41010", MCP41010 }, - { "mcp41050", MCP41050 }, - { "mcp41100", MCP41100 }, - { "mcp42010", MCP42010 }, - { "mcp42050", MCP42050 }, - { "mcp42100", MCP42100 }, + { .name = "mcp41010", .driver_data = MCP41010 }, + { .name = "mcp41050", .driver_data = MCP41050 }, + { .name = "mcp41100", .driver_data = MCP41100 }, + { .name = "mcp42010", .driver_data = MCP42010 }, + { .name = "mcp42050", .driver_data = MCP42050 }, + { .name = "mcp42100", .driver_data = MCP42100 }, { } }; MODULE_DEVICE_TABLE(spi, mcp41010_id); diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index dad09ce6a75d..fadf9fcc1817 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -406,70 +406,70 @@ static const struct of_device_id mcp4131_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); static const struct spi_device_id mcp4131_id[] = { - { "mcp4131-502", MCP413x_502 }, - { "mcp4131-103", MCP413x_103 }, - { "mcp4131-503", MCP413x_503 }, - { "mcp4131-104", MCP413x_104 }, - { "mcp4132-502", MCP413x_502 }, - { "mcp4132-103", MCP413x_103 }, - { "mcp4132-503", MCP413x_503 }, - { "mcp4132-104", MCP413x_104 }, - { "mcp4141-502", MCP414x_502 }, - { "mcp4141-103", MCP414x_103 }, - { "mcp4141-503", MCP414x_503 }, - { "mcp4141-104", MCP414x_104 }, - { "mcp4142-502", MCP414x_502 }, - { "mcp4142-103", MCP414x_103 }, - { "mcp4142-503", MCP414x_503 }, - { "mcp4142-104", MCP414x_104 }, - { "mcp4151-502", MCP415x_502 }, - { "mcp4151-103", MCP415x_103 }, - { "mcp4151-503", MCP415x_503 }, - { "mcp4151-104", MCP415x_104 }, - { "mcp4152-502", MCP415x_502 }, - { "mcp4152-103", MCP415x_103 }, - { "mcp4152-503", MCP415x_503 }, - { "mcp4152-104", MCP415x_104 }, - { "mcp4161-502", MCP416x_502 }, - { "mcp4161-103", MCP416x_103 }, - { "mcp4161-503", MCP416x_503 }, - { "mcp4161-104", MCP416x_104 }, - { "mcp4162-502", MCP416x_502 }, - { "mcp4162-103", MCP416x_103 }, - { "mcp4162-503", MCP416x_503 }, - { "mcp4162-104", MCP416x_104 }, - { "mcp4231-502", MCP423x_502 }, - { "mcp4231-103", MCP423x_103 }, - { "mcp4231-503", MCP423x_503 }, - { "mcp4231-104", MCP423x_104 }, - { "mcp4232-502", MCP423x_502 }, - { "mcp4232-103", MCP423x_103 }, - { "mcp4232-503", MCP423x_503 }, - { "mcp4232-104", MCP423x_104 }, - { "mcp4241-502", MCP424x_502 }, - { "mcp4241-103", MCP424x_103 }, - { "mcp4241-503", MCP424x_503 }, - { "mcp4241-104", MCP424x_104 }, - { "mcp4242-502", MCP424x_502 }, - { "mcp4242-103", MCP424x_103 }, - { "mcp4242-503", MCP424x_503 }, - { "mcp4242-104", MCP424x_104 }, - { "mcp4251-502", MCP425x_502 }, - { "mcp4251-103", MCP425x_103 }, - { "mcp4251-503", MCP425x_503 }, - { "mcp4251-104", MCP425x_104 }, - { "mcp4252-502", MCP425x_502 }, - { "mcp4252-103", MCP425x_103 }, - { "mcp4252-503", MCP425x_503 }, - { "mcp4252-104", MCP425x_104 }, - { "mcp4261-502", MCP426x_502 }, - { "mcp4261-103", MCP426x_103 }, - { "mcp4261-503", MCP426x_503 }, - { "mcp4261-104", MCP426x_104 }, - { "mcp4262-502", MCP426x_502 }, - { "mcp4262-103", MCP426x_103 }, - { "mcp4262-503", MCP426x_503 }, - { "mcp4262-104", MCP426x_104 }, + { .name = "mcp4131-502", .driver_data = MCP413x_502 }, + { .name = "mcp4131-103", .driver_data = MCP413x_103 }, + { .name = "mcp4131-503", .driver_data = MCP413x_503 }, + { .name = "mcp4131-104", .driver_data = MCP413x_104 }, + { .name = "mcp4132-502", .driver_data = MCP413x_502 }, + { .name = "mcp4132-103", .driver_data = MCP413x_103 }, + { .name = "mcp4132-503", .driver_data = MCP413x_503 }, + { .name = "mcp4132-104", .driver_data = MCP413x_104 }, + { .name = "mcp4141-502", .driver_data = MCP414x_502 }, + { .name = "mcp4141-103", .driver_data = MCP414x_103 }, + { .name = "mcp4141-503", .driver_data = MCP414x_503 }, + { .name = "mcp4141-104", .driver_data = MCP414x_104 }, + { .name = "mcp4142-502", .driver_data = MCP414x_502 }, + { .name = "mcp4142-103", .driver_data = MCP414x_103 }, + { .name = "mcp4142-503", .driver_data = MCP414x_503 }, + { .name = "mcp4142-104", .driver_data = MCP414x_104 }, + { .name = "mcp4151-502", .driver_data = MCP415x_502 }, + { .name = "mcp4151-103", .driver_data = MCP415x_103 }, + { .name = "mcp4151-503", .driver_data = MCP415x_503 }, + { .name = "mcp4151-104", .driver_data = MCP415x_104 }, + { .name = "mcp4152-502", .driver_data = MCP415x_502 }, + { .name = "mcp4152-103", .driver_data = MCP415x_103 }, + { .name = "mcp4152-503", .driver_data = MCP415x_503 }, + { .name = "mcp4152-104", .driver_data = MCP415x_104 }, + { .name = "mcp4161-502", .driver_data = MCP416x_502 }, + { .name = "mcp4161-103", .driver_data = MCP416x_103 }, + { .name = "mcp4161-503", .driver_data = MCP416x_503 }, + { .name = "mcp4161-104", .driver_data = MCP416x_104 }, + { .name = "mcp4162-502", .driver_data = MCP416x_502 }, + { .name = "mcp4162-103", .driver_data = MCP416x_103 }, + { .name = "mcp4162-503", .driver_data = MCP416x_503 }, + { .name = "mcp4162-104", .driver_data = MCP416x_104 }, + { .name = "mcp4231-502", .driver_data = MCP423x_502 }, + { .name = "mcp4231-103", .driver_data = MCP423x_103 }, + { .name = "mcp4231-503", .driver_data = MCP423x_503 }, + { .name = "mcp4231-104", .driver_data = MCP423x_104 }, + { .name = "mcp4232-502", .driver_data = MCP423x_502 }, + { .name = "mcp4232-103", .driver_data = MCP423x_103 }, + { .name = "mcp4232-503", .driver_data = MCP423x_503 }, + { .name = "mcp4232-104", .driver_data = MCP423x_104 }, + { .name = "mcp4241-502", .driver_data = MCP424x_502 }, + { .name = "mcp4241-103", .driver_data = MCP424x_103 }, + { .name = "mcp4241-503", .driver_data = MCP424x_503 }, + { .name = "mcp4241-104", .driver_data = MCP424x_104 }, + { .name = "mcp4242-502", .driver_data = MCP424x_502 }, + { .name = "mcp4242-103", .driver_data = MCP424x_103 }, + { .name = "mcp4242-503", .driver_data = MCP424x_503 }, + { .name = "mcp4242-104", .driver_data = MCP424x_104 }, + { .name = "mcp4251-502", .driver_data = MCP425x_502 }, + { .name = "mcp4251-103", .driver_data = MCP425x_103 }, + { .name = "mcp4251-503", .driver_data = MCP425x_503 }, + { .name = "mcp4251-104", .driver_data = MCP425x_104 }, + { .name = "mcp4252-502", .driver_data = MCP425x_502 }, + { .name = "mcp4252-103", .driver_data = MCP425x_103 }, + { .name = "mcp4252-503", .driver_data = MCP425x_503 }, + { .name = "mcp4252-104", .driver_data = MCP425x_104 }, + { .name = "mcp4261-502", .driver_data = MCP426x_502 }, + { .name = "mcp4261-103", .driver_data = MCP426x_103 }, + { .name = "mcp4261-503", .driver_data = MCP426x_503 }, + { .name = "mcp4261-104", .driver_data = MCP426x_104 }, + { .name = "mcp4262-502", .driver_data = MCP426x_502 }, + { .name = "mcp4262-103", .driver_data = MCP426x_103 }, + { .name = "mcp4262-503", .driver_data = MCP426x_503 }, + { .name = "mcp4262-104", .driver_data = MCP426x_104 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4131_id); diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c index 735348492699..974e4447fc2c 100644 --- a/drivers/iio/potentiometer/x9250.c +++ b/drivers/iio/potentiometer/x9250.c @@ -198,8 +198,8 @@ static const struct of_device_id x9250_of_match[] = { MODULE_DEVICE_TABLE(of, x9250_of_match); static const struct spi_device_id x9250_id_table[] = { - { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] }, - { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] }, + { .name = "x9250t", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250T] }, + { .name = "x9250u", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250U] }, { } }; MODULE_DEVICE_TABLE(spi, x9250_id_table); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 1984d990438c..00cb3fea97ca 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -422,3 +422,4 @@ module_i2c_driver(lmp91000_driver); MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("LMP91000 digital potentiostat"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c index b44f1bf4c633..41395036e9ad 100644 --- a/drivers/iio/pressure/abp2030pa.c +++ b/drivers/iio/pressure/abp2030pa.c @@ -379,7 +379,6 @@ static int abp2_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (channel->type) { diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c index 8bc59eb499aa..60d078574827 100644 --- a/drivers/iio/pressure/abp2030pa_spi.c +++ b/drivers/iio/pressure/abp2030pa_spi.c @@ -45,7 +45,7 @@ static const struct of_device_id abp2_spi_match[] = { MODULE_DEVICE_TABLE(of, abp2_spi_match); static const struct spi_device_id abp2_spi_id[] = { - { "abp2030pa" }, + { .name = "abp2030pa" }, { } }; MODULE_DEVICE_TABLE(spi, abp2_spi_id); diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 990340a9b10c..1066792b9087 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -750,7 +750,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_read_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -924,7 +927,10 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_write_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -1333,7 +1339,7 @@ static int __bmp280_trigger_probe(struct iio_dev *indio_dev, irq_thread_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "request IRQ failed.\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) @@ -2254,7 +2260,10 @@ static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_read_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -2328,7 +2337,10 @@ static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_write_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -3108,11 +3120,17 @@ EXPORT_SYMBOL_NS(bmp085_chip_info, "IIO_BMP280"); static int bmp280_buffer_preenable(struct iio_dev *indio_dev) { struct bmp280_data *data = iio_priv(indio_dev); + int ret; - pm_runtime_get_sync(data->dev); - data->chip_info->set_mode(data, BMP280_NORMAL); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; - return 0; + ret = data->chip_info->set_mode(data, BMP280_NORMAL); + if (ret) + pm_runtime_put_autosuspend(data->dev); + + return ret; } static int bmp280_buffer_postdisable(struct iio_dev *indio_dev) diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 04bf2f5be5b1..a02a621b8015 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -118,13 +118,13 @@ static const struct of_device_id bmp280_of_spi_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp085", (kernel_ulong_t)&bmp085_chip_info }, - { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, - { "bme280", (kernel_ulong_t)&bme280_chip_info }, - { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, - { "bmp580", (kernel_ulong_t)&bmp580_chip_info }, + { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info }, + { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp181", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info }, + { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info }, + { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info }, + { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 01a873165923..961888fd03a7 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -309,10 +309,8 @@ static int dlh_probe(struct i2c_client *client) ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, st->info->name, indio_dev); - if (ret) { - dev_err(&client->dev, "failed to allocate threaded irq"); + if (ret) return ret; - } st->use_interrupt = true; init_completion(&st->completion); diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index f45af72a0554..45bdb8c7670f 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -845,7 +845,6 @@ static const struct iio_info dps310_info = { static int dps310_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct dps310_data *data; struct iio_dev *iio; int rc; @@ -858,7 +857,7 @@ static int dps310_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); - iio->name = id->name; + iio->name = DPS310_DEV_NAME; iio->channels = dps310_channels; iio->num_channels = ARRAY_SIZE(dps310_channels); iio->info = &dps310_info; diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index ae499d197555..e688b0776547 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -55,9 +55,8 @@ static const struct iio_chan_spec press_channels[] = { /* Channel read_raw handler */ static int press_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) { struct press_state *press_state = iio_priv(indio_dev); int report_id = -1; @@ -81,7 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&press_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, @@ -89,7 +88,7 @@ static int press_read_raw(struct iio_dev *indio_dev, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&press_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -123,10 +122,8 @@ static int press_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int press_write_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) { struct press_state *press_state = iio_priv(indio_dev); int ret = 0; @@ -154,8 +151,7 @@ static const struct iio_info press_info = { /* Callback handler to send event after all samples are received and captured */ static int press_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); @@ -175,7 +171,7 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -201,10 +197,10 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int press_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned usage_id, - struct press_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct press_state *st) { int ret; @@ -221,7 +217,7 @@ static int press_parse_report(struct platform_device *pdev, }; dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, - st->press_attr.report_id); + st->press_attr.report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_PRESSURE, @@ -240,8 +236,7 @@ static int hid_press_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct press_state *press_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct press_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -284,32 +279,32 @@ static int hid_press_probe(struct platform_device *pdev) atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &press_state->common_attributes); + &press_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - press_state->callbacks.send_event = press_proc_event; press_state->callbacks.capture_sample = press_capture_sample; press_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->callbacks); + &press_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); return ret; @@ -322,8 +317,8 @@ static void hid_press_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct press_state *press_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); } diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c index be58e550cc12..02021b6f799a 100644 --- a/drivers/iio/pressure/hsc030pa_spi.c +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -39,7 +39,7 @@ static const struct of_device_id hsc_spi_match[] = { MODULE_DEVICE_TABLE(of, hsc_spi_match); static const struct spi_device_id hsc_spi_id[] = { - { "hsc030pa" }, + { .name = "hsc030pa" }, { } }; MODULE_DEVICE_TABLE(spi, hsc_spi_id); diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 16e112b796ba..2929ea8e4e01 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -203,9 +203,9 @@ int mpl115_probe(struct device *dev, const char *name, if (data->shutdown) { /* Enable runtime PM */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = pm_runtime_set_active(dev); + if (ret) + return ret; /* * As the device takes 3 ms to come up with a fresh @@ -215,7 +215,10 @@ int mpl115_probe(struct device *dev, const char *name, */ pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); - pm_runtime_put(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; dev_dbg(dev, "low-power mode enabled"); } else diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c index 4e1d24beff94..3cff616e8d2f 100644 --- a/drivers/iio/pressure/mpl115_spi.c +++ b/drivers/iio/pressure/mpl115_spi.c @@ -84,7 +84,7 @@ static int mpl115_spi_probe(struct spi_device *spi) } static const struct spi_device_id mpl115_spi_ids[] = { - { "mpl115", 0 }, + { .name = "mpl115" }, { } }; MODULE_DEVICE_TABLE(spi, mpl115_spi_ids); diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c index 23f47c04ed45..6270370383c3 100644 --- a/drivers/iio/pressure/mprls0025pa_spi.c +++ b/drivers/iio/pressure/mprls0025pa_spi.c @@ -59,7 +59,7 @@ static const struct of_device_id mpr_spi_match[] = { MODULE_DEVICE_TABLE(of, mpr_spi_match); static const struct spi_device_id mpr_spi_id[] = { - { "mprls0025pa" }, + { .name = "mprls0025pa" }, { } }; MODULE_DEVICE_TABLE(spi, mpr_spi_id); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index c41aaa6244cd..7133e6744e3f 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -113,8 +113,8 @@ static const struct of_device_id ms5611_spi_matches[] = { MODULE_DEVICE_TABLE(of, ms5611_spi_matches); static const struct spi_device_id ms5611_id[] = { - { "ms5611", MS5611 }, - { "ms5607", MS5607 }, + { .name = "ms5611", .driver_data = MS5611 }, + { .name = "ms5607", .driver_data = MS5607 }, { } }; MODULE_DEVICE_TABLE(spi, ms5611_id); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index b3be9de03678..57941fb4a535 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -621,17 +621,15 @@ static const struct iio_buffer_setup_ops bm1390_buffer_ops = { .predisable = bm1390_buffer_predisable, }; -static irqreturn_t bm1390_trigger_handler(int irq, void *p) +static bool bm1390_handle_trigger(struct iio_dev *idev) { - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; struct bm1390_data *data = iio_priv(idev); int ret, status; /* DRDY is acked by reading status reg */ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status); if (ret || !status) - return IRQ_NONE; + return false; dev_dbg(data->dev, "DRDY trig status 0x%x\n", status); @@ -639,7 +637,7 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) ret = bm1390_pressure_read(data, &data->buf.pressure); if (ret) { dev_warn(data->dev, "sample read failed %d\n", ret); - return IRQ_NONE; + return false; } } @@ -648,15 +646,26 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) &data->buf.temp, sizeof(data->buf.temp)); if (ret) { dev_warn(data->dev, "temp read failed %d\n", ret); - return IRQ_HANDLED; + return true; } } iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf), data->timestamp); + + return true; +} + +static irqreturn_t bm1390_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + bool result; + + result = bm1390_handle_trigger(idev); iio_trigger_notify_done(idev->trig); - return IRQ_HANDLED; + return IRQ_RETVAL(result); } /* Get timestamps and wake the thread if we need to read data */ @@ -796,7 +805,7 @@ static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev, &bm1390_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, itrig); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index d843186b44af..8add0c392c6a 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -93,18 +93,18 @@ static int st_press_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_press_id_table[] = { - { LPS001WP_PRESS_DEV_NAME }, - { LPS25H_PRESS_DEV_NAME }, - { LPS331AP_PRESS_DEV_NAME }, - { LPS22HB_PRESS_DEV_NAME }, - { LPS33HW_PRESS_DEV_NAME }, - { LPS35HW_PRESS_DEV_NAME }, - { LPS22HH_PRESS_DEV_NAME }, - { LPS22DF_PRESS_DEV_NAME }, - { "lps001wp-press" }, - { "lps25h-press", }, - { "lps331ap-press" }, - { "lps22hb-press" }, + { .name = LPS001WP_PRESS_DEV_NAME }, + { .name = LPS25H_PRESS_DEV_NAME }, + { .name = LPS331AP_PRESS_DEV_NAME }, + { .name = LPS22HB_PRESS_DEV_NAME }, + { .name = LPS33HW_PRESS_DEV_NAME }, + { .name = LPS35HW_PRESS_DEV_NAME }, + { .name = LPS22HH_PRESS_DEV_NAME }, + { .name = LPS22DF_PRESS_DEV_NAME }, + { .name = "lps001wp-press" }, + { .name = "lps25h-press" }, + { .name = "lps331ap-press" }, + { .name = "lps22hb-press" }, { } }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 2c68fdf2744e..b38493ff3b8b 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -911,11 +911,8 @@ static int zpa2326_init_managed_irq(struct device *parent, zpa2326_handle_threaded_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(parent), indio_dev); - if (err) { - dev_err(parent, "failed to request interrupt %d (%d)", irq, - err); + if (err) return err; - } dev_info(parent, "using interrupt %d", irq); diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c index 73e37a77c933..dfbb74a58dc0 100644 --- a/drivers/iio/pressure/zpa2326_spi.c +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -60,7 +60,7 @@ static void zpa2326_remove_spi(struct spi_device *spi) } static const struct spi_device_id zpa2326_spi_ids[] = { - { "zpa2326", 0 }, + { .name = "zpa2326" }, { } }; MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 3406232822cb..d7f43c3af165 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -428,10 +428,8 @@ static int as3935_probe(struct spi_device *spi) dev_name(dev), indio_dev); - if (ret) { - dev_err(dev, "unable to request irq\n"); + if (ret) return ret; - } ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { @@ -448,7 +446,7 @@ static const struct of_device_id as3935_of_match[] = { MODULE_DEVICE_TABLE(of, as3935_of_match); static const struct spi_device_id as3935_id[] = { - {"as3935", 0}, + { .name = "as3935" }, { } }; MODULE_DEVICE_TABLE(spi, as3935_id); diff --git a/drivers/iio/proximity/d3323aa.c b/drivers/iio/proximity/d3323aa.c index d40e3dff9eb1..1f43c1fed342 100644 --- a/drivers/iio/proximity/d3323aa.c +++ b/drivers/iio/proximity/d3323aa.c @@ -771,7 +771,7 @@ static int d3323aa_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(dev), indio_dev); if (ret) - return dev_err_probe(dev, ret, "Could not request IRQ\n"); + return ret; ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX, D3323AA_FILTER_GAIN_DEFAULT_IDX, diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index a6ff7cbe9e65..0199a6351a46 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1139,7 +1139,7 @@ static int hx9023s_probe(struct i2c_client *client) IRQF_ONESHOT, "hx9023s_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "irq request failed\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 65af31d43453..b3db7f3a49f6 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -910,7 +910,7 @@ static int irsd200_probe(struct i2c_client *client) IRQF_TRIGGER_RISING | IRQF_ONESHOT, NULL, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request irq\n"); + return ret; trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index eab881b0cdc7..accfc296e1f0 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -213,10 +213,8 @@ static int mb1232_probe(struct i2c_client *client) if (data->irqnr > 0) { ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq, IRQF_TRIGGER_FALLING, id->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request_irq: %d\n", ret); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 7be50bdebfcb..4e042214df29 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -303,10 +303,8 @@ static int srf04_probe(struct platform_device *pdev) ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, pdev->name, indio_dev); - if (ret < 0) { - dev_err(data->dev, "request_irq: %d\n", ret); + if (ret) return ret; - } platform_set_drvdata(pdev, indio_dev); @@ -330,6 +328,7 @@ static int srf04_probe(struct platform_device *pdev) if (ret) { dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); iio_device_unregister(indio_dev); + return ret; } pm_runtime_enable(data->dev); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index 59b35e40739b..4d4937395d9a 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -517,7 +517,7 @@ int sx_common_probe(struct i2c_client *client, IRQF_ONESHOT, "sx_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "No IRQ\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 7f417372566a..6b0d7a9038d9 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -638,12 +638,8 @@ static int vcnl3020_probe(struct i2c_client *client) NULL, vcnl3020_handle_irq_thread, IRQF_ONESHOT, indio_dev->name, indio_dev); - if (rc) { - dev_err(&client->dev, - "Error (%d) irq request failed (%u)\n", rc, - client->irq); + if (rc) return rc; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index b7127c3c52d2..9fe14ceb8be7 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -127,10 +127,8 @@ static int vl53l0x_configure_irq(struct i2c_client *client, ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, vl53l0x_threaded_irq, irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&client->dev, "devm_request_irq error: %d\n", ret); + if (ret) return ret; - } ret = i2c_smbus_write_byte_data(data->client, VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index dc4ffbc95d1f..bce238d1b6de 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -2,7 +2,7 @@ /* * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. * - * Copyright (C) 2026 Siratul Islam + * Copyright (C) 2026 Siratul Islam * * Datasheet available at * @@ -42,6 +42,7 @@ #define VL53L1X_REG_SOFT_RESET 0x0000 #define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 #define VL53L1X_REG_VHV_CONFIG__INIT 0x000B +#define VL53L1X_REG_DEFAULT_CONFIG 0x002D #define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030 #define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031 #define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 @@ -63,7 +64,6 @@ #define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL 0x00DE #define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS 0x00E5 #define VL53L1X_REG_IDENTIFICATION__MODEL_ID 0x010F -#define VL53L1X_REG_DEFAULT_CONFIG 0x002D #define VL53L1X_MODEL_ID_VAL 0xEACC @@ -81,6 +81,9 @@ #define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) +#define VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED BIT(0) +#define VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY BIT(0) + /* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ static const struct u32_fract vl53l1x_osc_correction = { .numerator = 1075, @@ -190,6 +193,17 @@ static int vl53l1x_stop_ranging(struct vl53l1x_data *data) VL53L1X_MODE_START_STOP); } +static int vl53l1x_wait_data_ready(struct vl53l1x_data *data) +{ + unsigned int val; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + return regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, val, + (val & VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY) != data->gpio_polarity, + 1 * USEC_PER_MSEC, 1 * USEC_PER_SEC); +} + /* * Default configuration blob from ST's VL53L1X Ultra Lite Driver * (STSW-IMG009). @@ -229,10 +243,9 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data) } ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val, - val & BIT(0), - 1 * USEC_PER_MSEC, - 100 * USEC_PER_MSEC); + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, + val, val & VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED, + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); if (ret) return dev_err_probe(dev, ret, "firmware boot timeout\n"); @@ -260,12 +273,7 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data) if (ret) return ret; - /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ - ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_GPIO__TIO_HV_STATUS, val, - (val & 1) != data->gpio_polarity, - 1 * USEC_PER_MSEC, - 1000 * USEC_PER_MSEC); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; @@ -460,14 +468,7 @@ static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) if (!wait_for_completion_timeout(&data->completion, HZ)) return -ETIMEDOUT; } else { - unsigned int rdy; - - /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ - ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy, - (rdy & 1) != data->gpio_polarity, - 1 * USEC_PER_MSEC, - 1000 * USEC_PER_MSEC); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; } @@ -750,6 +751,6 @@ static struct i2c_driver vl53l1x_driver = { }; module_i2c_driver(vl53l1x_driver); -MODULE_AUTHOR("Siratul Islam "); +MODULE_AUTHOR("Siratul Islam "); MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index 55bcbbd4021a..27ecc434eee9 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -143,18 +143,14 @@ static int ad2s1200_probe(struct spi_device *spi) st->sdev = spi; st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW); - if (IS_ERR(st->sample)) { - dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n", - PTR_ERR(st->sample)); - return PTR_ERR(st->sample); - } + if (IS_ERR(st->sample)) + return dev_err_probe(&spi->dev, PTR_ERR(st->sample), + "Failed to claim SAMPLE gpio\n"); st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW); - if (IS_ERR(st->rdvel)) { - dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n", - PTR_ERR(st->rdvel)); - return PTR_ERR(st->rdvel); - } + if (IS_ERR(st->rdvel)) + return dev_err_probe(&spi->dev, PTR_ERR(st->rdvel), + "Failed to claim RDVEL gpio\n"); indio_dev->info = &ad2s1200_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -182,8 +178,8 @@ static const struct of_device_id ad2s1200_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1200_of_match); static const struct spi_device_id ad2s1200_id[] = { - { "ad2s1200" }, - { "ad2s1205" }, + { .name = "ad2s1200" }, + { .name = "ad2s1205" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1200_id); diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 1be19fe8aa3f..3b5ec21e3446 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1276,10 +1276,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, return regmap_write(st->regmap, reg, writeval); } -static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +static void ad2s1210_scan_to_buffers(struct iio_dev *indio_dev, s64 timestamp) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; struct ad2s1210_state *st = iio_priv(indio_dev); size_t chan = 0; int ret; @@ -1295,15 +1293,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_POSITION_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_POS); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1315,15 +1313,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_VELOCITY_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_VEL); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1334,16 +1332,22 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - goto error_ret; + return; st->sample.fault = reg_val; } - ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), - pf->timestamp); + timestamp); +} -error_ret: +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ad2s1210_scan_to_buffers(indio_dev, pf->timestamp); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -1597,7 +1601,7 @@ static const struct of_device_id ad2s1210_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1210_of_match); static const struct spi_device_id ad2s1210_id[] = { - { "ad2s1210" }, + { .name = "ad2s1210" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1210_id); diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c index 18f1c905eeac..b7a2dcc42871 100644 --- a/drivers/iio/resolver/ad2s90.c +++ b/drivers/iio/resolver/ad2s90.c @@ -110,7 +110,7 @@ static const struct of_device_id ad2s90_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s90_of_match); static const struct spi_device_id ad2s90_id[] = { - { "ad2s90" }, + { .name = "ad2s90" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s90_id); diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 54a00253de11..5e3262e461f4 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec temperature_channels[] = { /* Adjust channel real bits based on report descriptor */ static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -100,8 +100,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev, } static int temperature_write_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) { struct temperature_state *temp_st = iio_priv(indio_dev); @@ -124,7 +124,7 @@ static const struct iio_info temperature_info = { /* Callback handler to send event after all samples are received and captured */ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -139,8 +139,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -156,10 +157,10 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int temperature_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned int usage_id, - struct temperature_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct temperature_state *st) { int ret; @@ -170,8 +171,7 @@ static int temperature_parse_report(struct platform_device *pdev, if (ret < 0) return ret; - temperature_adjust_channel_bit_mask(channels, 0, - st->temperature_attr.size); + temperature_adjust_channel_bit_mask(channels, 0, st->temperature_attr.size); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_TEMPERATURE, @@ -212,13 +212,13 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) return ret; - temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels, - sizeof(temperature_channels), GFP_KERNEL); + temp_chans = devm_kmemdup(&pdev->dev, temperature_channels, + sizeof(temperature_channels), GFP_KERNEL); if (!temp_chans) return -ENOMEM; ret = temperature_parse_report(pdev, hsdev, temp_chans, - HID_USAGE_SENSOR_TEMPERATURE, temp_st); + HID_USAGE_SENSOR_TEMPERATURE, temp_st); if (ret) return ret; @@ -231,7 +231,7 @@ static int hid_temperature_probe(struct platform_device *pdev) atomic_set(&temp_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &temp_st->common_attributes); + &temp_st->common_attributes); if (ret) return ret; @@ -239,11 +239,11 @@ static int hid_temperature_probe(struct platform_device *pdev) temperature_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temperature_callbacks); + &temperature_callbacks); if (ret) goto error_remove_trigger; - ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); + ret = iio_device_register(indio_dev); if (ret) goto error_remove_callback; @@ -263,6 +263,7 @@ static void hid_temperature_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); + iio_device_unregister(indio_dev); sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes); } diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index f8c4917bb118..6efb5252a773 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1994,7 +1994,7 @@ static int ltc2983_probe(struct spi_device *spi) ret = devm_request_irq(dev, spi->irq, ltc2983_irq_handler, IRQF_TRIGGER_RISING, st->info->name, st); if (ret) - return dev_err_probe(dev, ret, "failed to request an irq\n"); + return ret; if (st->info->has_eeprom) { ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD, @@ -2070,11 +2070,11 @@ static const struct ltc2983_chip_info ltm2985_chip_info_data = { }; static const struct spi_device_id ltc2983_id_table[] = { - { "adt7604", (kernel_ulong_t)&adt7604_chip_info_data }, - { "ltc2983", (kernel_ulong_t)<c2983_chip_info_data }, - { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, - { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, - { "ltm2985", (kernel_ulong_t)<m2985_chip_info_data }, + { .name = "adt7604", .driver_data = (kernel_ulong_t)&adt7604_chip_info_data }, + { .name = "ltc2983", .driver_data = (kernel_ulong_t)<c2983_chip_info_data }, + { .name = "ltc2984", .driver_data = (kernel_ulong_t)<c2984_chip_info_data }, + { .name = "ltc2986", .driver_data = (kernel_ulong_t)<c2986_chip_info_data }, + { .name = "ltm2985", .driver_data = (kernel_ulong_t)<m2985_chip_info_data }, { } }; MODULE_DEVICE_TABLE(spi, ltc2983_id_table); diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 13405f3ba829..061f6fe6de5e 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -461,7 +461,7 @@ static int max31856_probe(struct spi_device *spi) } static const struct spi_device_id max31856_id[] = { - { "max31856", 0 }, + { .name = "max31856" }, { } }; MODULE_DEVICE_TABLE(spi, max31856_id); diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c index 82cf06c4d272..f9c93f95d80b 100644 --- a/drivers/iio/temperature/max31865.c +++ b/drivers/iio/temperature/max31865.c @@ -324,7 +324,7 @@ static int max31865_probe(struct spi_device *spi) } static const struct spi_device_id max31865_id[] = { - { "max31865", 0 }, + { .name = "max31865" }, { } }; MODULE_DEVICE_TABLE(spi, max31865_id); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 015afeab1914..5f216e238d55 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -262,15 +262,15 @@ static int maxim_thermocouple_probe(struct spi_device *spi) } static const struct spi_device_id maxim_thermocouple_id[] = { - {"max6675", MAX6675}, - {"max31855", MAX31855}, - {"max31855k", MAX31855K}, - {"max31855j", MAX31855J}, - {"max31855n", MAX31855N}, - {"max31855s", MAX31855S}, - {"max31855t", MAX31855T}, - {"max31855e", MAX31855E}, - {"max31855r", MAX31855R}, + { .name = "max6675", .driver_data = MAX6675 }, + { .name = "max31855", .driver_data = MAX31855 }, + { .name = "max31855k", .driver_data = MAX31855K }, + { .name = "max31855j", .driver_data = MAX31855J }, + { .name = "max31855n", .driver_data = MAX31855N }, + { .name = "max31855s", .driver_data = MAX31855S }, + { .name = "max31855t", .driver_data = MAX31855T }, + { .name = "max31855e", .driver_data = MAX31855E }, + { .name = "max31855r", .driver_data = MAX31855R }, { } }; MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index 2f6ff87d2a37..4e743424f88e 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -527,10 +527,8 @@ static int tmp007_probe(struct i2c_client *client) NULL, tmp007_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, tmp007_id->name, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 6bc18616ad15..74cb8d62bef3 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -9,6 +9,7 @@ * Note: This driver assumes that the sensor has been calibrated beforehand. */ +#include #include #include #include @@ -38,6 +39,7 @@ #define TMP116_DEVICE_ID 0x1116 #define TMP117_DEVICE_ID 0x0117 +#define TMP119_DEVICE_ID 0x2117 struct tmp117_data { struct i2c_client *client; @@ -139,6 +141,12 @@ static const struct tmp11x_info tmp117_channels_info = { .num_channels = ARRAY_SIZE(tmp117_channels) }; +static const struct tmp11x_info tmp119_channels_info = { + .name = "tmp119", + .channels = tmp117_channels, + .num_channels = ARRAY_SIZE(tmp117_channels) +}; + static const struct iio_info tmp117_info = { .read_raw = tmp117_read_raw, .write_raw = tmp117_write_raw, @@ -172,6 +180,9 @@ static int tmp117_probe(struct i2c_client *client) case TMP117_DEVICE_ID: match_data = &tmp117_channels_info; break; + case TMP119_DEVICE_ID: + match_data = &tmp119_channels_info; + break; default: dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", @@ -204,6 +215,7 @@ static int tmp117_probe(struct i2c_client *client) static const struct of_device_id tmp117_of_match[] = { { .compatible = "ti,tmp116", .data = &tmp116_channels_info }, { .compatible = "ti,tmp117", .data = &tmp117_channels_info }, + { .compatible = "ti,tmp119", .data = &tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); @@ -211,6 +223,7 @@ MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { { .name = "tmp116", .driver_data = (kernel_ulong_t)&tmp116_channels_info }, { .name = "tmp117", .driver_data = (kernel_ulong_t)&tmp117_channels_info }, + { .name = "tmp119", .driver_data = (kernel_ulong_t)&tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index 872dd8582003..e5d0a2ac4670 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -200,56 +200,108 @@ static void iio_test_iio_format_value_multiple(struct kunit *test) static void iio_test_iio_format_value_integer_64(struct kunit *test) { int values[2]; - s64 value; char *buf; int ret; buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); - value = 24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n"); - value = -24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n"); - value = 0; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(0, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); - value = UINT_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(UINT_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n"); - value = -((s64)UINT_MAX); - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-((s64)UINT_MAX), &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n"); - value = LLONG_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n"); - value = LLONG_MIN; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n"); } +static void iio_test_iio_format_value_decimal_64(struct kunit *test) +{ + int values[2]; + char *buf; + int ret; + + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + /* DECIMAL64_MILLI: positive >= 1, value 1.234 */ + iio_val_s64_decompose(1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.234\n"); + + /* DECIMAL64_MICRO: positive >= 1, value 3.141592 */ + iio_val_s64_decompose(3141592, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "3.141592\n"); + + /* DECIMAL64_MILLI: positive < 1, value 0.042 */ + iio_val_s64_decompose(42, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.042\n"); + + /* DECIMAL64_MILLI: negative <= -1, value -1.234 */ + iio_val_s64_decompose(-1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.234\n"); + + /* DECIMAL64_MILLI: negative > -1, value -0.123 */ + iio_val_s64_decompose(-123, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.123\n"); + + /* DECIMAL64_MILLI: zero */ + iio_val_s64_decompose(0, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000\n"); + + /* DECIMAL64_NANO: value 1.000000001 */ + iio_val_s64_decompose(1000000001, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000001\n"); + + /* DECIMAL64_MICRO: large value using upper 32 bits */ + iio_val_s64_decompose(5000000000000042LL, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "5000000000.000042\n"); + + /* limits */ + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372.036854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036.854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854.775807\n"); + + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372.036854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036.854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854.775808\n"); +} + static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_integer), KUNIT_CASE(iio_test_iio_format_value_fixedpoint), @@ -257,6 +309,7 @@ static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), KUNIT_CASE(iio_test_iio_format_value_integer_64), + KUNIT_CASE(iio_test_iio_format_value_decimal_64), { } }; diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index ac6942cf1e44..bcd4a607be19 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -575,7 +575,7 @@ static const struct rescale_tc_data offset_cases[] = { static void case_to_desc(const struct rescale_tc_data *t, char *desc) { - strcpy(desc, t->name); + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); } KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc); diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index ff44f9978b71..4fa42f88bcfa 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -329,3 +329,4 @@ module_platform_driver(adc_joystick_driver); MODULE_DESCRIPTION("Input driver for joysticks connected over ADC"); MODULE_AUTHOR("Artur Rojek "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index f1753207429d..d687459a0c80 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -202,3 +202,4 @@ module_platform_driver(adc_keys_driver); MODULE_AUTHOR("Alexandre Belloni "); MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 98d5b2ba63fb..89c4d7b2b89e 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -372,3 +372,4 @@ module_platform_driver(vf50_touch_driver); MODULE_AUTHOR("Sanchayan Maity"); MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c index 68f7e2e28f37..c89edc1280fe 100644 --- a/drivers/input/touchscreen/resistive-adc-touch.c +++ b/drivers/input/touchscreen/resistive-adc-touch.c @@ -300,3 +300,4 @@ module_platform_driver(grts_driver); MODULE_AUTHOR("Eugen Hristev "); MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 73a9db2c7537..4aa991a54101 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -548,7 +548,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_data->node->name, dst_data->node->name); if (!path->name) { - kfree(path); + icc_put(path); path = ERR_PTR(-ENOMEM); } @@ -646,8 +646,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); if (!path->name) { - kfree(path); - path = ERR_PTR(-ENOMEM); + mutex_unlock(&icc_lock); + icc_put(path); + return ERR_PTR(-ENOMEM); } out: mutex_unlock(&icc_lock); diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c index 08df9188ef94..91f86d9237a6 100644 --- a/drivers/interconnect/debugfs-client.c +++ b/drivers/interconnect/debugfs-client.c @@ -142,6 +142,8 @@ int icc_debugfs_client_init(struct dentry *icc_dir) int ret; pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE); + if (!pdev) + return -ENOMEM; ret = platform_device_add(pdev); if (ret) { diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 5b8a094ca4ed..b3187acf90b8 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config INTERCONNECT_QCOM tristate "Qualcomm Network-on-Chip interconnect drivers" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST help Support for Qualcomm's Network-on-Chip interconnect hardware. @@ -47,6 +47,16 @@ config INTERCONNECT_QCOM_KAANAPALI This is a driver for the Qualcomm Network-on-Chip on kaanapali-based platforms. +config INTERCONNECT_QCOM_MAILI + tristate "Qualcomm MAILI interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + default ARCH_QCOM + help + This is a driver for the Qualcomm Network-on-Chip on maili-based + platforms. + config INTERCONNECT_QCOM_MSM8909 tristate "Qualcomm MSM8909 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 1c7d410b40cc..5f139a370bbe 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -8,6 +8,7 @@ qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o qnoc-hawi-objs := hawi.o qnoc-kaanapali-objs := kaanapali.o +qnoc-maili-objs := maili.o qnoc-milos-objs := milos.o qnoc-msm8909-objs := msm8909.o qnoc-msm8916-objs := msm8916.o @@ -56,6 +57,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o +obj-$(CONFIG_INTERCONNECT_QCOM_MAILI) += qnoc-maili.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 23a1d116e79a..ca4c8a944755 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -479,13 +479,11 @@ int qnoc_probe(struct platform_device *pdev) cd_num = 0; } - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + qp = devm_kzalloc(dev, struct_size(qp, intf_clks, cd_num), GFP_KERNEL); if (!qp) return -ENOMEM; - qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL); - if (!qp->intf_clks) - return -ENOMEM; + qp->num_intf_clks = cd_num; if (desc->bus_clk_desc) { qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc), @@ -507,7 +505,6 @@ int qnoc_probe(struct platform_device *pdev) return -ENOMEM; data->num_nodes = num_nodes; - qp->num_intf_clks = cd_num; for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 7d1cb2efa9ee..cdd5f444b152 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -40,7 +40,6 @@ struct rpm_clk_resource { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @num_intf_clks: the total number of intf_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access * @qos_offset: offset to QoS registers @@ -49,13 +48,13 @@ struct rpm_clk_resource { * @bus_clk_rate: bus clock rate in Hz * @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks * @bus_clk: a pointer to a HLOS-owned bus clock - * @intf_clks: a clk_bulk_data array of interface clocks * @keep_alive: whether to always keep a minimum vote on the bus clocks * @ignore_enxio: whether to ignore ENXIO errors (for MSM8974) + * @num_intf_clks: the total number of intf_clks clk_bulk_data entries + * @intf_clks: a clk_bulk_data array of interface clocks */ struct qcom_icc_provider { struct icc_provider provider; - int num_intf_clks; enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; @@ -64,9 +63,10 @@ struct qcom_icc_provider { u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM]; const struct rpm_clk_resource *bus_clk_desc; struct clk *bus_clk; - struct clk_bulk_data *intf_clks; bool keep_alive; bool ignore_enxio; + int num_intf_clks; + struct clk_bulk_data intf_clks[] __counted_by(num_intf_clks); }; /** diff --git a/drivers/interconnect/qcom/maili.c b/drivers/interconnect/qcom/maili.c new file mode 100644 index 000000000000..e5eed7f998f9 --- /dev/null +++ b/drivers/interconnect/qcom/maili.c @@ -0,0 +1,2091 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node ddr_eff_veto_slave = { + .name = "ddr_eff_veto_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup3_core_slave = { + .name = "qup3_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup4_core_slave = { + .name = "qup4_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup5_core_slave = { + .name = "qup5_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router_fence = { + .name = "qhs_ipc_router_fence", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_g4x1 = { + .name = "xs_pcie_g4x1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_lpi_cc = { + .name = "qhs_lpi_cc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qns_lb = { + .name = "qns_lb", + .channels = 4, + .buswidth = 16, +}; + +static struct qcom_icc_node srvc_llclpi_noc = { + .name = "srvc_llclpi_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_llclpi_noc_chipcx = { + .name = "srvc_llclpi_noc_chipcx", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node ddr_rt_slave = { + .name = "ddr_rt_slave", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto_cfg = { + .name = "qhs_crypto_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_eva_cfg = { + .name = "qhs_eva_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_iris_cfg = { + .name = "qhs_iris_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg = { + .name = "qhs_pcie_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_g4x1_cfg = { + .name = "qhs_pcie_g4x1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup3 = { + .name = "qhs_qup3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup4 = { + .name = "qhs_qup4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup5 = { + .name = "qhs_qup5", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3 = { + .name = "qhs_usb3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_cfg = { + .name = "qss_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_stm = { + .name = "qss_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_tcsr = { + .name = "qss_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ddr_eff_veto_master = { + .name = "ddr_eff_veto_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qup3_core_master = { + .name = "qup3_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_node qup4_core_master = { + .name = "qup4_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_node qup5_core_master = { + .name = "qup5_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup5_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 2, + .link_nodes = { &xs_pcie, &xs_pcie_g4x1 }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc_llclpi_noc = { + .name = "qnm_lpiaon_noc_llclpi_noc", + .channels = 1, + .buswidth = 16, + .num_links = 4, + .link_nodes = { &qhs_lpi_cc, &qns_lb, + &srvc_llclpi_noc, &srvc_llclpi_noc_chipcx }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node ddr_rt_mc = { + .name = "ddr_rt_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_node qsm_cfg_center = { + .name = "qsm_cfg_center", + .channels = 1, + .buswidth = 4, + .num_links = 14, + .link_nodes = { &qhs_boot_rom, &qhs_clk_ctl, + &qhs_crypto_cfg, &qhs_gpuss_cfg, + &qhs_imem_cfg, &qhs_ipc_router, + &qhs_mss_cfg, &qhs_prng, + &qhs_tlmm, &qhs_vsense_ctrl_cfg, + &qss_qdss_cfg, &qss_qdss_stm, + &qss_tcsr, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_east = { + .name = "qsm_cfg_east", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup2, &qhs_qup4 }, +}; + +static struct qcom_icc_node qsm_cfg_mm_hf = { + .name = "qsm_cfg_mm_hf", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qhs_display_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_mm_sf = { + .name = "qsm_cfg_mm_sf", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_camera_cfg, &qhs_eva_cfg, + &qhs_iris_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_north = { + .name = "qsm_cfg_north", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_ahb2phy0, &qhs_ufs_mem_cfg, + &qhs_usb3 }, +}; + +static struct qcom_icc_node qsm_cfg_west = { + .name = "qsm_cfg_west", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup1, &qhs_sdc2 }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 4, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qns_llc_lpinoc = { + .name = "qns_llc_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc_llclpi_noc }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_center_cfg = { + .name = "qss_stdst_center_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_center }, +}; + +static struct qcom_icc_node qss_stdst_east_cfg = { + .name = "qss_stdst_east_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_east }, +}; + +static struct qcom_icc_node qss_stdst_mm_hf_cfg = { + .name = "qss_stdst_mm_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_hf }, +}; + +static struct qcom_icc_node qss_stdst_mm_sf_cfg = { + .name = "qss_stdst_mm_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_sf }, +}; + +static struct qcom_icc_node qss_stdst_north_cfg = { + .name = "qss_stdst_north_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_north }, +}; + +static struct qcom_icc_node qss_stdst_west_cfg = { + .name = "qss_stdst_west_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_west }, +}; + +static struct qcom_icc_node alm_gic = { + .name = "alm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qnm_qpace = { + .name = "qnm_qpace", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x153000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qsm_cfg_south = { + .name = "qsm_cfg_south", + .channels = 1, + .buswidth = 4, + .num_links = 8, + .link_nodes = { &qhs_i2c, &qhs_pcie_cfg, + &qhs_pcie_g4x1_cfg, &qhs_qspi, + &qhs_qup3, &qhs_qup5, + &qhs_sdc4, &qss_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_south_cfg = { + .name = "qss_stdst_south_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_south }, +}; + +static struct qcom_icc_node qsm_cnoc_main = { + .name = "qsm_cnoc_main", + .channels = 1, + .buswidth = 4, + .num_links = 7, + .link_nodes = { &qss_stdst_center_cfg, &qss_stdst_east_cfg, + &qss_stdst_mm_hf_cfg, &qss_stdst_mm_sf_cfg, + &qss_stdst_north_cfg, &qss_stdst_south_cfg, + &qss_stdst_west_cfg }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cnoc_main }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 8, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x145000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x147000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 2, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x51000, 0xd1000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x149000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x59000, 0xd9000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14b000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_wlan_q6 = { + .name = "qnm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_gemnoc = { + .name = "qns_pcie_gemnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x50000, 0x51000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x52000, 0x53000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x21000, 0x22000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_mdss_dcp = { + .name = "qnm_mdss_dcp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x24000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x25000, 0x26000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x23000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie = { + .name = "xm_pcie", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie_g4x1 = { + .name = "xm_pcie_g4x1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node qnm_aggre_noc = { + .name = "qnm_aggre_noc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_apss_noc = { + .name = "qnm_apss_noc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x49000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x48000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup3 = { + .name = "qhm_qup3", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x46000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup4 = { + .name = "qhm_qup4", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x47000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup5 = { + .name = "qhm_qup5", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4e000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x40000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x41000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_qup1 = { + .name = "qxm_qup1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4d000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x45000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x42000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x43000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x44000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4b000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3 = { + .name = "xm_usb3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4c000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 32, + .num_links = 2, + .link_nodes = { &qns_llc_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = { + .name = "qnm_lpinoc_dsp_qns4m", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 25, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem, + &xs_pcie, &xs_pcie_g4x1, + &qsm_cfg_center, &qsm_cfg_east, + &qsm_cfg_mm_hf, &qsm_cfg_mm_sf, + &qsm_cfg_north, &qsm_cfg_south, + &qsm_cfg_west, &qhs_ahb2phy0, + &qhs_boot_rom, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto_cfg, + &qhs_eva_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 6, + .nodes = { &qhs_display_cfg, &qhs_qup1, + &qhs_qup2, &qhs_qup3, + &qhs_qup4, &qhs_qup5 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_de0 = { + .name = "DE0", + .enable_mask = BIT(0), + .num_nodes = 1, + .nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 5, + .nodes = { &qnm_lpiaon_noc_llclpi_noc, &qns_lb, + &qnm_lpass_lpinoc, &qns_llc_lpinoc, + &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mc5 = { + .name = "MC5", + .num_nodes = 1, + .nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 9, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_vapss_hcp, &qnm_video_cv_cpu, + &qnm_video_mvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qpc0 = { + .name = "QPC0", + .num_nodes = 1, + .nodes = { &qnm_qpace }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup3 = { + .name = "QUP3", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup4 = { + .name = "QUP4", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup5 = { + .name = "QUP5", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup5_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 15, + .nodes = { &alm_gic, &alm_gpu_tcu, + &alm_sys_tcu, &chm_apps, + &qnm_gpu, &qnm_lpass_gemnoc, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qnm_wlan_q6, &qns_gem_noc_cnoc, + &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_QUP_3] = &qhm_qup3, + [MASTER_QUP_4] = &qhm_qup4, + [MASTER_QUP_5] = &qhm_qup5, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QUP_1] = &qxm_qup1, + [MASTER_SOCCP_PROC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3] = &xm_usb3, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config maili_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x56200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_aggre1_noc = { + .config = &maili_aggre1_noc_regmap_config, + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_de0, + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, + &bcm_qup3, + &bcm_qup4, + &bcm_qup5, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_DDR_EFF_VETO] = &ddr_eff_veto_master, + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [MASTER_QUP_CORE_4] = &qup4_core_master, + [MASTER_QUP_CORE_5] = &qup5_core_master, + [SLAVE_DDR_EFF_VETO] = &ddr_eff_veto_slave, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, + [SLAVE_QUP_CORE_4] = &qup4_core_slave, + [SLAVE_QUP_CORE_5] = &qup5_core_slave, +}; + +static const struct qcom_icc_desc maili_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_FENCE] = &qhs_ipc_router_fence, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &xs_pcie, + [SLAVE_PCIE_1] = &xs_pcie_g4x1, +}; + +static const struct regmap_config maili_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_cnoc_main = { + .config = &maili_cnoc_main_regmap_config, + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_qpc0, + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GIC] = &alm_gic, + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_QPACE] = &qnm_qpace, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qnm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config maili_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x160200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_gem_noc = { + .config = &maili_gem_noc_regmap_config, + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const llclpi_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const llclpi_noc_nodes[] = { + [MASTER_LPIAON_NOC_LLCLPI_NOC] = &qnm_lpiaon_noc_llclpi_noc, + [SLAVE_LPASS_LPI_CC] = &qhs_lpi_cc, + [SLAVE_LLCC_ISLAND] = &qns_lb, + [SLAVE_SERVICE_LLCLPI_NOC] = &srvc_llclpi_noc, + [SLAVE_SERVICE_LLCLPI_NOC_CHIPCX] = &srvc_llclpi_noc_chipcx, +}; + +static const struct regmap_config maili_llclpi_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_llclpi_noc = { + .config = &maili_llclpi_noc_regmap_config, + .nodes = llclpi_noc_nodes, + .num_nodes = ARRAY_SIZE(llclpi_noc_nodes), + .bcms = llclpi_noc_bcms, + .num_bcms = ARRAY_SIZE(llclpi_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct regmap_config maili_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_ag_noc = { + .config = &maili_lpass_ag_noc_regmap_config, + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LLCLPI_NOC] = &qns_llc_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct regmap_config maili_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpiaon_noc = { + .config = &maili_lpass_lpiaon_noc_regmap_config, + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qnm_lpinoc_dsp_qns4m, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct regmap_config maili_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x46080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpicx_noc = { + .config = &maili_lpass_lpicx_noc_regmap_config, + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_mc5, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [MASTER_DDR_RT] = &ddr_rt_mc, + [SLAVE_EBI1] = &ebi, + [SLAVE_DDR_RT] = &ddr_rt_slave, +}; + +static const struct qcom_icc_desc maili_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_MDSS_DCP] = &qnm_mdss_dcp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, +}; + +static const struct regmap_config maili_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5f800, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_mmss_noc = { + .config = &maili_mmss_noc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qnm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct regmap_config maili_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x21280, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_nsp_noc = { + .config = &maili_nsp_noc_regmap_config, + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn3, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie, + [MASTER_PCIE_1] = &xm_pcie_g4x1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_gemnoc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct regmap_config maili_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x12400, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_pcie_anoc = { + .config = &maili_pcie_anoc_regmap_config, + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const stdst_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const stdst_cfg_nodes[] = { + [MASTER_CFG_CENTER] = &qsm_cfg_center, + [MASTER_CFG_EAST] = &qsm_cfg_east, + [MASTER_CFG_MM_HF] = &qsm_cfg_mm_hf, + [MASTER_CFG_MM_SF] = &qsm_cfg_mm_sf, + [MASTER_CFG_NORTH] = &qsm_cfg_north, + [MASTER_CFG_SOUTH] = &qsm_cfg_south, + [MASTER_CFG_WEST] = &qsm_cfg_west, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_CFG] = &qhs_crypto_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_IRIS_CFG] = &qhs_iris_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie_g4x1_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_QUP_4] = &qhs_qup4, + [SLAVE_QUP_5] = &qhs_qup5, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_CFG] = &qss_qdss_cfg, + [SLAVE_QDSS_STM] = &qss_qdss_stm, + [SLAVE_TCSR] = &qss_tcsr, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct regmap_config maili_stdst_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_cfg = { + .config = &maili_stdst_cfg_regmap_config, + .nodes = stdst_cfg_nodes, + .num_nodes = ARRAY_SIZE(stdst_cfg_nodes), + .bcms = stdst_cfg_bcms, + .num_bcms = ARRAY_SIZE(stdst_cfg_bcms), +}; + +static struct qcom_icc_node * const stdst_main_nodes[] = { + [MASTER_CNOC_STARDUST] = &qsm_cnoc_main, + [SLAVE_STARDUST_CENTER_CFG] = &qss_stdst_center_cfg, + [SLAVE_STARDUST_EAST_CFG] = &qss_stdst_east_cfg, + [SLAVE_STARDUST_MM_HF_CFG] = &qss_stdst_mm_hf_cfg, + [SLAVE_STARDUST_MM_SF_CFG] = &qss_stdst_mm_sf_cfg, + [SLAVE_STARDUST_NORTH_CFG] = &qss_stdst_north_cfg, + [SLAVE_STARDUST_SOUTH_CFG] = &qss_stdst_south_cfg, + [SLAVE_STARDUST_WEST_CFG] = &qss_stdst_west_cfg, +}; + +static const struct regmap_config maili_stdst_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_main = { + .config = &maili_stdst_main_regmap_config, + .nodes = stdst_main_nodes, + .num_nodes = ARRAY_SIZE(stdst_main_nodes), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre_noc, + [MASTER_APSS_NOC] = &qnm_apss_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct regmap_config maili_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_system_noc = { + .config = &maili_system_noc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,maili-aggre-noc", .data = &maili_aggre1_noc }, + { .compatible = "qcom,maili-clk-virt", .data = &maili_clk_virt }, + { .compatible = "qcom,maili-cnoc-main", .data = &maili_cnoc_main }, + { .compatible = "qcom,maili-gem-noc", .data = &maili_gem_noc }, + { .compatible = "qcom,maili-llclpi-noc", .data = &maili_llclpi_noc }, + { .compatible = "qcom,maili-lpass-ag-noc", .data = &maili_lpass_ag_noc }, + { .compatible = "qcom,maili-lpass-lpiaon-noc", .data = &maili_lpass_lpiaon_noc }, + { .compatible = "qcom,maili-lpass-lpicx-noc", .data = &maili_lpass_lpicx_noc }, + { .compatible = "qcom,maili-mc-virt", .data = &maili_mc_virt }, + { .compatible = "qcom,maili-mmss-noc", .data = &maili_mmss_noc }, + { .compatible = "qcom,maili-nsp-noc", .data = &maili_nsp_noc }, + { .compatible = "qcom,maili-pcie-anoc", .data = &maili_pcie_anoc }, + { .compatible = "qcom,maili-stdst-cfg", .data = &maili_stdst_cfg }, + { .compatible = "qcom,maili-stdst-main", .data = &maili_stdst_main }, + { .compatible = "qcom,maili-system-noc", .data = &maili_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-maili", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Maili NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index b33f00da1880..ecad636b53e0 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -60,6 +60,7 @@ struct qcom_osm_l3_desc { unsigned int lut_row_size; unsigned int reg_freq_lut; unsigned int reg_perf_state; + unsigned int lut_max_entries; }; #define DEFINE_QNODE(_name, _buswidth) \ @@ -90,6 +91,7 @@ static const struct qcom_osm_l3_desc osm_l3 = { .lut_row_size = OSM_LUT_ROW_SIZE, .reg_freq_lut = OSM_REG_FREQ_LUT, .reg_perf_state = OSM_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static const struct qcom_osm_l3_desc epss_l3_perf_state = { @@ -98,6 +100,16 @@ static const struct qcom_osm_l3_desc epss_l3_perf_state = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, +}; + +static const struct qcom_osm_l3_desc shikra_epss_l3_perf_state = { + .nodes = epss_l3_nodes, + .num_nodes = ARRAY_SIZE(epss_l3_nodes), + .lut_row_size = EPSS_LUT_ROW_SIZE, + .reg_freq_lut = EPSS_REG_FREQ_LUT, + .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = 12, }; static const struct qcom_osm_l3_desc epss_l3_l3_vote = { @@ -106,6 +118,7 @@ static const struct qcom_osm_l3_desc epss_l3_l3_vote = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_L3_VOTE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst) @@ -189,7 +202,7 @@ static int qcom_osm_l3_probe(struct platform_device *pdev) qp->reg_perf_state = desc->reg_perf_state; - for (i = 0; i < LUT_MAX_ENTRIES; i++) { + for (i = 0; i < desc->lut_max_entries; i++) { info = readl_relaxed(qp->base + desc->reg_freq_lut + i * desc->lut_row_size); src = FIELD_GET(LUT_SRC, info); @@ -272,6 +285,7 @@ static const struct of_device_id osm_l3_of_match[] = { { .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state }, { .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 }, + { .compatible = "qcom,shikra-epss-l3", .data = &shikra_epss_l3_perf_state }, { .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state }, diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c index 4110536664d0..15e284fcd89a 100644 --- a/drivers/interconnect/qcom/sc8280xp.c +++ b/drivers/interconnect/qcom/sc8280xp.c @@ -215,6 +215,11 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -223,6 +228,11 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -231,6 +241,11 @@ static struct qcom_icc_node qhm_qup2 = { .name = "qhm_qup2", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -247,6 +262,12 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -255,6 +276,11 @@ static struct qcom_icc_node xm_emac_1 = { .name = "xm_emac_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -263,6 +289,11 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -271,6 +302,11 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -279,6 +315,11 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -287,6 +328,11 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -295,6 +341,11 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -303,6 +354,11 @@ static struct qcom_icc_node xm_usb4_host0 = { .name = "xm_usb4_host0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -311,6 +367,11 @@ static struct qcom_icc_node xm_usb4_host1 = { .name = "xm_usb4_host1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -319,6 +380,11 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -327,6 +393,11 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -343,6 +414,12 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -367,6 +444,11 @@ static struct qcom_icc_node xm_emac_0 = { .name = "xm_emac_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -375,6 +457,11 @@ static struct qcom_icc_node xm_pcie3_0 = { .name = "xm_pcie3_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -383,6 +470,11 @@ static struct qcom_icc_node xm_pcie3_1 = { .name = "xm_pcie3_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -391,6 +483,11 @@ static struct qcom_icc_node xm_pcie3_2a = { .name = "xm_pcie3_2a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -399,6 +496,11 @@ static struct qcom_icc_node xm_pcie3_2b = { .name = "xm_pcie3_2b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -407,6 +509,11 @@ static struct qcom_icc_node xm_pcie3_3a = { .name = "xm_pcie3_3a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -415,6 +522,11 @@ static struct qcom_icc_node xm_pcie3_3b = { .name = "xm_pcie3_3b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -423,6 +535,11 @@ static struct qcom_icc_node xm_pcie3_4 = { .name = "xm_pcie3_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -431,6 +548,11 @@ static struct qcom_icc_node xm_qdss_etr = { .name = "xm_qdss_etr", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -439,6 +561,11 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -447,6 +574,11 @@ static struct qcom_icc_node xm_ufs_card = { .name = "xm_ufs_card", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -586,6 +718,11 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa0000 }, + .prio = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -595,6 +732,11 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa1000 }, + .prio = 3, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -604,6 +746,11 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa2000 }, + .prio = 6, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -623,6 +770,11 @@ static struct qcom_icc_node qnm_cmpnoc0 = { .name = "qnm_cmpnoc0", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1b000, 0x66000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -632,6 +784,11 @@ static struct qcom_icc_node qnm_cmpnoc1 = { .name = "qnm_cmpnoc1", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x24000, 0x6f000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -651,6 +808,11 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x1c000, 0x1d000, 0x67000, 0x68000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -660,6 +822,11 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1e000, 0x69000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_llcc, &qns_pcie }, @@ -669,6 +836,11 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1f000, 0x6a000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -678,6 +850,12 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa3000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -687,6 +865,11 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa4000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -695,6 +878,11 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa5000 }, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, @@ -737,6 +925,11 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x10000, 0x10180 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -745,6 +938,11 @@ static struct qcom_icc_node qnm_mdp0_0 = { .name = "qnm_mdp0_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -753,6 +951,11 @@ static struct qcom_icc_node qnm_mdp0_1 = { .name = "qnm_mdp0_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -761,6 +964,11 @@ static struct qcom_icc_node qnm_mdp1_0 = { .name = "qnm_mdp1_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -769,6 +977,11 @@ static struct qcom_icc_node qnm_mdp1_1 = { .name = "qnm_mdp1_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf800 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -785,6 +998,11 @@ static struct qcom_icc_node qnm_rot_0 = { .name = "qnm_rot_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -793,6 +1011,11 @@ static struct qcom_icc_node qnm_rot_1 = { .name = "qnm_rot_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -801,6 +1024,11 @@ static struct qcom_icc_node qnm_video0 = { .name = "qnm_video0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -809,6 +1037,11 @@ static struct qcom_icc_node qnm_video1 = { .name = "qnm_video1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -817,6 +1050,11 @@ static struct qcom_icc_node qnm_video_cvp = { .name = "qnm_video_cvp", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -825,6 +1063,11 @@ static struct qcom_icc_node qxm_camnoc_icp = { .name = "qxm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -833,6 +1076,11 @@ static struct qcom_icc_node qxm_camnoc_sf = { .name = "qxm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -899,6 +1147,11 @@ static struct qcom_icc_node qnm_lpass_noc = { .name = "qnm_lpass_noc", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -915,6 +1168,12 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -923,6 +1182,11 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -1996,11 +2260,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, }; +static const struct regmap_config sc8280xp_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre1_noc = { + .config = &sc8280xp_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -2032,11 +2306,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, }; +static const struct regmap_config sc8280xp_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre2_noc = { + .config = &sc8280xp_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const clk_virt_bcms[] = { @@ -2158,7 +2442,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config sc8280xp_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2c000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_config_noc = { + .config = &sc8280xp_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -2174,7 +2467,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, }; +static const struct regmap_config sc8280xp_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_dc_noc = { + .config = &sc8280xp_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), .bcms = dc_noc_bcms, @@ -2208,7 +2510,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, }; +static const struct regmap_config sc8280xp_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb8400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_gem_noc = { + .config = &sc8280xp_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -2231,7 +2542,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, }; +static const struct regmap_config sc8280xp_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_lpass_ag_noc = { + .config = &sc8280xp_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -2279,7 +2599,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config sc8280xp_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1fa80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_mmss_noc = { + .config = &sc8280xp_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -2299,7 +2628,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = { [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, }; +static const struct regmap_config sc8280xp_nspa_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspa_noc = { + .config = &sc8280xp_nspa_noc_regmap_config, .nodes = nspa_noc_nodes, .num_nodes = ARRAY_SIZE(nspa_noc_nodes), .bcms = nspa_noc_bcms, @@ -2319,7 +2657,16 @@ static struct qcom_icc_node * const nspb_noc_nodes[] = { [SLAVE_SERVICE_NSPB_NOC] = &service_nspb_noc, }; +static const struct regmap_config sc8280xp_nspb_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspb_noc = { + .config = &sc8280xp_nspb_noc_regmap_config, .nodes = nspb_noc_nodes, .num_nodes = ARRAY_SIZE(nspb_noc_nodes), .bcms = nspb_noc_bcms, @@ -2348,7 +2695,16 @@ static struct qcom_icc_node * const system_noc_main_nodes[] = { [SLAVE_SERVICE_SNOC] = &srvc_snoc, }; +static const struct regmap_config sc8280xp_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1a400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_system_noc_main = { + .config = &sc8280xp_system_noc_regmap_config, .nodes = system_noc_main_nodes, .num_nodes = ARRAY_SIZE(system_noc_main_nodes), .bcms = system_noc_main_bcms, diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c index 2ba2823c7860..8075e0ff2059 100644 --- a/drivers/interconnect/qcom/x1e80100.c +++ b/drivers/interconnect/qcom/x1e80100.c @@ -173,6 +173,13 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -181,6 +188,13 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -189,6 +203,13 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -197,6 +218,13 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -205,6 +233,13 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -213,6 +248,13 @@ static struct qcom_icc_node qhm_qup2 = { .name = "qhm_qup2", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -221,6 +263,13 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -237,6 +286,13 @@ static struct qcom_icc_node xm_qdss_etr_0 = { .name = "xm_qdss_etr_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -245,6 +301,13 @@ static struct qcom_icc_node xm_qdss_etr_1 = { .name = "xm_qdss_etr_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -253,6 +316,13 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -337,6 +407,13 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x271000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -345,6 +422,13 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27d000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -353,6 +437,13 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x273000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -370,6 +461,13 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x51000, 0x58000, 0xd1000, 0xd8000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -378,6 +476,13 @@ static struct qcom_icc_node qnm_lpass = { .name = "qnm_lpass", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x275000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -387,6 +492,13 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x53000, 0xd3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -395,6 +507,13 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -403,6 +522,13 @@ static struct qcom_icc_node qnm_nsp_noc = { .name = "qnm_nsp_noc", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -412,6 +538,13 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x277000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -420,6 +553,13 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -429,6 +569,13 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -469,6 +616,13 @@ static struct qcom_icc_node qnm_av1_enc = { .name = "qnm_av1_enc", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2f000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -477,6 +631,13 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x28000, 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -485,6 +646,13 @@ static struct qcom_icc_node qnm_camnoc_icp = { .name = "qnm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -493,6 +661,13 @@ static struct qcom_icc_node qnm_camnoc_sf = { .name = "qnm_camnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2b000, 0x2c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -501,6 +676,13 @@ static struct qcom_icc_node qnm_eva = { .name = "qnm_eva", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x33000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -509,6 +691,13 @@ static struct qcom_icc_node qnm_mdp = { .name = "qnm_mdp", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2d000, 0x2e000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -517,6 +706,13 @@ static struct qcom_icc_node qnm_video = { .name = "qnm_video", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x30000, 0x31000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -525,6 +721,13 @@ static struct qcom_icc_node qnm_video_cv_cpu = { .name = "qnm_video_cv_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x32000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -533,6 +736,13 @@ static struct qcom_icc_node qnm_video_v_cpu = { .name = "qnm_video_v_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x34000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -573,6 +783,13 @@ static struct qcom_icc_node xm_pcie_3 = { .name = "xm_pcie_3", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -581,6 +798,13 @@ static struct qcom_icc_node xm_pcie_4 = { .name = "xm_pcie_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -589,6 +813,13 @@ static struct qcom_icc_node xm_pcie_5 = { .name = "xm_pcie_5", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -597,6 +828,13 @@ static struct qcom_icc_node xm_pcie_0 = { .name = "xm_pcie_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -605,6 +843,13 @@ static struct qcom_icc_node xm_pcie_1 = { .name = "xm_pcie_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -613,6 +858,13 @@ static struct qcom_icc_node xm_pcie_2 = { .name = "xm_pcie_2", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -621,6 +873,13 @@ static struct qcom_icc_node xm_pcie_6a = { .name = "xm_pcie_6a", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -629,6 +888,13 @@ static struct qcom_icc_node xm_pcie_6b = { .name = "xm_pcie_6b", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -653,6 +919,13 @@ static struct qcom_icc_node qnm_gic = { .name = "qnm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -685,6 +958,13 @@ static struct qcom_icc_node xm_usb2_0 = { .name = "xm_usb2_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -693,6 +973,13 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -701,6 +988,13 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -709,6 +1003,13 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -717,6 +1018,13 @@ static struct qcom_icc_node xm_usb3_2 = { .name = "xm_usb3_2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -725,6 +1033,13 @@ static struct qcom_icc_node xm_usb4_0 = { .name = "xm_usb4_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -733,6 +1048,13 @@ static struct qcom_icc_node xm_usb4_1 = { .name = "xm_usb4_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -741,6 +1063,13 @@ static struct qcom_icc_node xm_usb4_2 = { .name = "xm_usb4_2", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -1466,11 +1795,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, }; +static const struct regmap_config x1e80100_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre1_noc = { + .config = &x1e80100_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -1488,7 +1827,16 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, }; +static const struct regmap_config x1e80100_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre2_noc = { + .config = &x1e80100_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, @@ -1573,7 +1921,16 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config x1e80100_cnoc_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6600, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_cfg = { + .config = &x1e80100_cnoc_cfg_regmap_config, .nodes = cnoc_cfg_nodes, .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), .bcms = cnoc_cfg_bcms, @@ -1603,7 +1960,16 @@ static struct qcom_icc_node * const cnoc_main_nodes[] = { [SLAVE_PCIE_6B] = &xs_pcie_6b, }; +static const struct regmap_config x1e80100_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_main = { + .config = &x1e80100_cnoc_main_regmap_config, .nodes = cnoc_main_nodes, .num_nodes = ARRAY_SIZE(cnoc_main_nodes), .bcms = cnoc_main_bcms, @@ -1633,7 +1999,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, }; +static const struct regmap_config x1e80100_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x311200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_gem_noc = { + .config = &x1e80100_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -1648,7 +2023,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, }; +static const struct regmap_config x1e80100_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_ag_noc = { + .config = &x1e80100_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -1664,7 +2048,16 @@ static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, }; +static const struct regmap_config x1e80100_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpiaon_noc = { + .config = &x1e80100_lpass_lpiaon_noc_regmap_config, .nodes = lpass_lpiaon_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), .bcms = lpass_lpiaon_noc_bcms, @@ -1679,7 +2072,16 @@ static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, }; +static const struct regmap_config x1e80100_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3a200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = { + .config = &x1e80100_lpass_lpicx_noc_regmap_config, .nodes = lpass_lpicx_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), .bcms = lpass_lpicx_noc_bcms, @@ -1724,7 +2126,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config x1e80100_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5b800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_mmss_noc = { + .config = &x1e80100_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1740,7 +2151,16 @@ static struct qcom_icc_node * const nsp_noc_nodes[] = { [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, }; +static const struct regmap_config x1e80100_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_nsp_noc = { + .config = &x1e80100_nsp_noc_regmap_config, .nodes = nsp_noc_nodes, .num_nodes = ARRAY_SIZE(nsp_noc_nodes), .bcms = nsp_noc_bcms, @@ -1757,7 +2177,16 @@ static struct qcom_icc_node * const pcie_center_anoc_nodes[] = { [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, }; +static const struct regmap_config x1e80100_pcie_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7000, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_center_anoc = { + .config = &x1e80100_pcie_center_anoc_regmap_config, .nodes = pcie_center_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes), .bcms = pcie_center_anoc_bcms, @@ -1774,7 +2203,16 @@ static struct qcom_icc_node * const pcie_north_anoc_nodes[] = { [SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc, }; +static const struct regmap_config x1e80100_pcie_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_north_anoc = { + .config = &x1e80100_pcie_north_anoc_regmap_config, .nodes = pcie_north_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes), .bcms = pcie_north_anoc_bcms, @@ -1793,7 +2231,16 @@ static struct qcom_icc_node * const pcie_south_anoc_nodes[] = { [SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc, }; +static const struct regmap_config x1e80100_pcie_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_south_anoc = { + .config = &x1e80100_pcie_south_anoc_regmap_config, .nodes = pcie_south_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes), .bcms = pcie_south_anoc_bcms, @@ -1815,7 +2262,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, }; +static const struct regmap_config x1e80100_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_system_noc = { + .config = &x1e80100_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, @@ -1831,7 +2287,16 @@ static struct qcom_icc_node * const usb_center_anoc_nodes[] = { [SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc, }; +static const struct regmap_config x1e80100_usb_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_center_anoc = { + .config = &x1e80100_usb_center_anoc_regmap_config, .nodes = usb_center_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_center_anoc_nodes), .bcms = usb_center_anoc_bcms, @@ -1847,11 +2312,21 @@ static struct qcom_icc_node * const usb_north_anoc_nodes[] = { [SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc, }; +static const struct regmap_config x1e80100_usb_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_north_anoc = { + .config = &x1e80100_usb_north_anoc_regmap_config, .nodes = usb_north_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_north_anoc_nodes), .bcms = usb_north_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_north_anoc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const usb_south_anoc_bcms[] = { @@ -1867,11 +2342,21 @@ static struct qcom_icc_node * const usb_south_anoc_nodes[] = { [SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc, }; +static const struct regmap_config x1e80100_usb_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_south_anoc = { + .config = &x1e80100_usb_south_anoc_regmap_config, .nodes = usb_south_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_south_anoc_nodes), .bcms = usb_south_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_south_anoc_bcms), + .qos_requires_clocks = true, }; static const struct of_device_id qnoc_of_match[] = { diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 1bbefc6de708..2169e4b75f98 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,6 +27,8 @@ static const struct tty_operations ipoctal_fops; +static void ipoctal_release(struct kref *kref); + struct ipoctal_channel { struct ipoctal_stats stats; unsigned int nb_bytes; @@ -49,6 +53,10 @@ struct ipoctal { struct tty_driver *tty_drv; u8 __iomem *mem8_space; u8 __iomem *int_space; + struct kref kref; + struct module *carrier_owner; + struct rw_semaphore remove_sem; + bool removed; }; static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan, @@ -70,8 +78,14 @@ static void ipoctal_reset_channel(struct ipoctal_channel *channel) static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) { struct ipoctal_channel *channel; + struct ipoctal *ipoctal; channel = dev_get_drvdata(tty->dev); + ipoctal = chan_to_ipoctal(channel, tty->index); + + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return -ENODEV; /* * Enable RX. TX will be enabled when @@ -95,6 +109,7 @@ static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty) if (res) goto err_put_carrier; + kref_get(&ipoctal->kref); tty->driver_data = channel; return 0; @@ -460,8 +475,13 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, size_t count) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); size_t char_copied; + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed || !channel->tty_port.xmit_buf) + return 0; + char_copied = ipoctal_copy_write_buffer(channel, buf, count); /* As the IP-OCTAL 485 only supports half duplex, do it manually */ @@ -501,8 +521,13 @@ static void ipoctal_set_termios(struct tty_struct *tty, unsigned char mr2 = 0; unsigned char csr = 0; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); speed_t baud; + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + cflag = tty->termios.c_cflag; /* Disable and reset everything before change the setup */ @@ -631,10 +656,16 @@ static void ipoctal_hangup(struct tty_struct *tty) { unsigned long flags; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + spin_lock_irqsave(&channel->lock, flags); channel->nb_bytes = 0; channel->pointer_read = 0; @@ -651,10 +682,16 @@ static void ipoctal_hangup(struct tty_struct *tty) static void ipoctal_shutdown(struct tty_struct *tty) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + guard(rwsem_read)(&ipoctal->remove_sem); + if (ipoctal->removed) + return; + ipoctal_reset_channel(channel); tty_port_set_initialized(&channel->tty_port, false); } @@ -664,8 +701,9 @@ static void ipoctal_cleanup(struct tty_struct *tty) struct ipoctal_channel *channel = tty->driver_data; struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); - /* release the carrier driver */ - ipack_put_carrier(ipoctal->dev); + /* release the carrier driver via cached owner */ + module_put(ipoctal->carrier_owner); + kref_put(&ipoctal->kref, ipoctal_release); } static const struct tty_operations ipoctal_fops = { @@ -683,6 +721,13 @@ static const struct tty_operations ipoctal_fops = { .cleanup = ipoctal_cleanup, }; +static void ipoctal_release(struct kref *kref) +{ + struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref); + + kfree(ipoctal); +} + static int ipoctal_probe(struct ipack_device *dev) { int res; @@ -692,7 +737,11 @@ static int ipoctal_probe(struct ipack_device *dev) if (ipoctal == NULL) return -ENOMEM; + kref_init(&ipoctal->kref); + init_rwsem(&ipoctal->remove_sem); + ipoctal->dev = dev; + ipoctal->carrier_owner = dev->bus->owner; res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); if (res) goto out_uninst; @@ -701,7 +750,7 @@ static int ipoctal_probe(struct ipack_device *dev) return 0; out_uninst: - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); return res; } @@ -709,6 +758,9 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) { int i; + scoped_guard(rwsem_write, &ipoctal->remove_sem) + ipoctal->removed = true; + ipoctal->dev->bus->ops->free_irq(ipoctal->dev); for (i = 0; i < NR_CHANNELS; i++) { @@ -725,7 +777,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) tty_unregister_driver(ipoctal->tty_drv); kfree(ipoctal->tty_drv->name); tty_driver_kref_put(ipoctal->tty_drv); - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); } static void ipoctal_remove(struct ipack_device *idev) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 390256ed91f4..7364931dad3a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -57,28 +57,6 @@ config DUMMY_IRQ The sole purpose of this module is to help with debugging of systems on which spurious IRQs would happen on disabled IRQ vector. -config IBM_ASM - tristate "Device driver for IBM RSA service processor" - depends on X86 && PCI && INPUT - depends on SERIAL_8250 || SERIAL_8250=n - help - This option enables device driver support for in-band access to the - IBM RSA (Condor) service processor in eServer xSeries systems. - The ibmasm device driver allows user space application to access - ASM (Advanced Systems Management) functions on the service - processor. The driver is meant to be used in conjunction with - a user space API. - The ibmasm driver also enables the OS to use the UART on the - service processor board as a regular serial port. To make use of - this feature serial driver support (CONFIG_SERIAL_8250) must be - enabled. - - WARNING: This software may not be supported or function - correctly on your IBM server. Please consult the IBM ServerProven - website - for information on the specific driver level and support statement - for your IBM server. - config IBMVMC tristate "IBM Virtual Management Channel support" depends on PPC_PSERIES @@ -185,19 +163,6 @@ config ENCLOSURE_SERVICES driver (SCSI/ATA) which supports enclosures or a SCSI enclosure device (SES) to use these services. -config SGI_XP - tristate "Support communication between SGI SSIs" - depends on NET - depends on X86_UV && SMP - depends on X86_64 || BROKEN - select SGI_GRU if X86_64 && SMP - help - An SGI machine can be divided into multiple Single System - Images which act independently of each other and have - hardware based memory protection from the others. Enabling - this feature will allow for direct communication between SSIs - based on a network adapter and DMA messaging. - config SMPRO_ERRMON tristate "Ampere Computing SMPro error monitor driver" depends on MFD_SMPRO || COMPILE_TEST @@ -310,27 +275,6 @@ config QCOM_FASTRPC applications DSP processor. Say M if you want to enable this module. -config SGI_GRU - tristate "SGI GRU driver" - depends on X86_UV && SMP - select MMU_NOTIFIER - help - The GRU is a hardware resource located in the system chipset. The GRU - contains memory that can be mmapped into the user address space. - This memory is used to communicate with the GRU to perform functions - such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is - directly accessed by user instructions using user virtual addresses. - GRU instructions (ex., bcopy) use user virtual addresses for operands. - - If you are not running on a SGI UV system, say N. - -config SGI_GRU_DEBUG - bool "SGI GRU driver debug" - depends on SGI_GRU - help - This option enables additional debugging code for the SGI GRU driver. - If you are unsure, say N. - config APDS9802ALS tristate "Medfield Avago APDS9802 ALS Sensor module" depends on I2C @@ -494,16 +438,6 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB -config HISI_HIKEY_USB - tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform" - depends on (OF && GPIOLIB) || COMPILE_TEST - depends on USB_ROLE_SWITCH - help - If you say yes here this adds support for the on-board USB GPIO hub - found on HiKey 960/970 boards, which is necessary to support - switching between the dual-role USB-C port and the USB-A host ports - using only one USB controller. - config OPEN_DICE tristate "Open Profile for DICE driver" depends on OF_RESERVED_MEM @@ -651,4 +585,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" source "drivers/misc/rp1/Kconfig" +source "drivers/misc/issei/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fed47c7672b9..e8d8d5d88c0d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -3,7 +3,6 @@ # Makefile for misc devices that really don't fit anywhere else. # -obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_IBMVMC) += ibmvmc.o obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o @@ -22,8 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o -obj-$(CONFIG_SGI_XP) += sgi-xp/ -obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o @@ -56,7 +53,6 @@ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o -obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_NTSYNC) += ntsync.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o @@ -74,3 +70,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 469478f7a1d3..a74b0266ea16 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -73,38 +73,39 @@ static void ad_dpot_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id ad_dpot_id[] = { - {"ad5258", AD5258_ID}, - {"ad5259", AD5259_ID}, - {"ad5251", AD5251_ID}, - {"ad5252", AD5252_ID}, - {"ad5253", AD5253_ID}, - {"ad5254", AD5254_ID}, - {"ad5255", AD5255_ID}, - {"ad5241", AD5241_ID}, - {"ad5242", AD5242_ID}, - {"ad5243", AD5243_ID}, - {"ad5245", AD5245_ID}, - {"ad5246", AD5246_ID}, - {"ad5247", AD5247_ID}, - {"ad5248", AD5248_ID}, - {"ad5280", AD5280_ID}, - {"ad5282", AD5282_ID}, - {"adn2860", ADN2860_ID}, - {"ad5273", AD5273_ID}, - {"ad5161", AD5161_ID}, - {"ad5171", AD5171_ID}, - {"ad5170", AD5170_ID}, - {"ad5172", AD5172_ID}, - {"ad5173", AD5173_ID}, - {"ad5272", AD5272_ID}, - {"ad5274", AD5274_ID}, - {} + { .name = "ad5258", .driver_data = AD5258_ID }, + { .name = "ad5259", .driver_data = AD5259_ID }, + { .name = "ad5251", .driver_data = AD5251_ID }, + { .name = "ad5252", .driver_data = AD5252_ID }, + { .name = "ad5253", .driver_data = AD5253_ID }, + { .name = "ad5254", .driver_data = AD5254_ID }, + { .name = "ad5255", .driver_data = AD5255_ID }, + { .name = "ad5241", .driver_data = AD5241_ID }, + { .name = "ad5242", .driver_data = AD5242_ID }, + { .name = "ad5243", .driver_data = AD5243_ID }, + { .name = "ad5245", .driver_data = AD5245_ID }, + { .name = "ad5246", .driver_data = AD5246_ID }, + { .name = "ad5247", .driver_data = AD5247_ID }, + { .name = "ad5248", .driver_data = AD5248_ID }, + { .name = "ad5280", .driver_data = AD5280_ID }, + { .name = "ad5282", .driver_data = AD5282_ID }, + { .name = "adn2860", .driver_data = ADN2860_ID }, + { .name = "ad5273", .driver_data = AD5273_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5171", .driver_data = AD5171_ID }, + { .name = "ad5170", .driver_data = AD5170_ID }, + { .name = "ad5172", .driver_data = AD5172_ID }, + { .name = "ad5173", .driver_data = AD5173_ID }, + { .name = "ad5272", .driver_data = AD5272_ID }, + { .name = "ad5274", .driver_data = AD5274_ID }, + { } }; MODULE_DEVICE_TABLE(i2c, ad_dpot_id); static struct i2c_driver ad_dpot_i2c_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_i2c_probe, .remove = ad_dpot_i2c_remove, diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index 263055bda48b..c4c2ec52a83d 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -96,41 +96,42 @@ static void ad_dpot_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad_dpot_spi_id[] = { - {"ad5160", AD5160_ID}, - {"ad5161", AD5161_ID}, - {"ad5162", AD5162_ID}, - {"ad5165", AD5165_ID}, - {"ad5200", AD5200_ID}, - {"ad5201", AD5201_ID}, - {"ad5203", AD5203_ID}, - {"ad5204", AD5204_ID}, - {"ad5206", AD5206_ID}, - {"ad5207", AD5207_ID}, - {"ad5231", AD5231_ID}, - {"ad5232", AD5232_ID}, - {"ad5233", AD5233_ID}, - {"ad5235", AD5235_ID}, - {"ad5260", AD5260_ID}, - {"ad5262", AD5262_ID}, - {"ad5263", AD5263_ID}, - {"ad5290", AD5290_ID}, - {"ad5291", AD5291_ID}, - {"ad5292", AD5292_ID}, - {"ad5293", AD5293_ID}, - {"ad7376", AD7376_ID}, - {"ad8400", AD8400_ID}, - {"ad8402", AD8402_ID}, - {"ad8403", AD8403_ID}, - {"adn2850", ADN2850_ID}, - {"ad5270", AD5270_ID}, - {"ad5271", AD5271_ID}, - {} + { .name = "ad5160", .driver_data = AD5160_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5162", .driver_data = AD5162_ID }, + { .name = "ad5165", .driver_data = AD5165_ID }, + { .name = "ad5200", .driver_data = AD5200_ID }, + { .name = "ad5201", .driver_data = AD5201_ID }, + { .name = "ad5203", .driver_data = AD5203_ID }, + { .name = "ad5204", .driver_data = AD5204_ID }, + { .name = "ad5206", .driver_data = AD5206_ID }, + { .name = "ad5207", .driver_data = AD5207_ID }, + { .name = "ad5231", .driver_data = AD5231_ID }, + { .name = "ad5232", .driver_data = AD5232_ID }, + { .name = "ad5233", .driver_data = AD5233_ID }, + { .name = "ad5235", .driver_data = AD5235_ID }, + { .name = "ad5260", .driver_data = AD5260_ID }, + { .name = "ad5262", .driver_data = AD5262_ID }, + { .name = "ad5263", .driver_data = AD5263_ID }, + { .name = "ad5290", .driver_data = AD5290_ID }, + { .name = "ad5291", .driver_data = AD5291_ID }, + { .name = "ad5292", .driver_data = AD5292_ID }, + { .name = "ad5293", .driver_data = AD5293_ID }, + { .name = "ad7376", .driver_data = AD7376_ID }, + { .name = "ad8400", .driver_data = AD8400_ID }, + { .name = "ad8402", .driver_data = AD8402_ID }, + { .name = "ad8403", .driver_data = AD8403_ID }, + { .name = "adn2850", .driver_data = ADN2850_ID }, + { .name = "ad5270", .driver_data = AD5270_ID }, + { .name = "ad5271", .driver_data = AD5271_ID }, + { } }; MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id); static struct spi_driver ad_dpot_spi_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_spi_probe, .remove = ad_dpot_spi_remove, diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 57bead9fba1b..a4e22fd4a107 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -630,66 +630,132 @@ static struct attribute *ad525x_attributes_commands[] = { NULL }; -static const struct attribute_group ad525x_group_commands = { - .attrs = ad525x_attributes_commands, +static struct attribute *ad525x_attributes[] = { + &dev_attr_rdac0.attr, + &dev_attr_rdac1.attr, + &dev_attr_rdac2.attr, + &dev_attr_rdac3.attr, + &dev_attr_rdac4.attr, + &dev_attr_rdac5.attr, + &dev_attr_eeprom0.attr, + &dev_attr_eeprom1.attr, + &dev_attr_eeprom2.attr, + &dev_attr_eeprom3.attr, + &dev_attr_eeprom4.attr, + &dev_attr_eeprom5.attr, + &dev_attr_tolerance0.attr, + &dev_attr_tolerance1.attr, + &dev_attr_tolerance2.attr, + &dev_attr_tolerance3.attr, + &dev_attr_tolerance4.attr, + &dev_attr_tolerance5.attr, + &dev_attr_otp0.attr, + &dev_attr_otp1.attr, + &dev_attr_otp2.attr, + &dev_attr_otp3.attr, + &dev_attr_otp4.attr, + &dev_attr_otp5.attr, + &dev_attr_otp0en.attr, + &dev_attr_otp1en.attr, + &dev_attr_otp2en.attr, + &dev_attr_otp3en.attr, + &dev_attr_otp4en.attr, + &dev_attr_otp5en.attr, + &dev_attr_inc_all.attr, + &dev_attr_dec_all.attr, + &dev_attr_inc_all_6db.attr, + &dev_attr_dec_all_6db.attr, + NULL }; -static int ad_dpot_add_files(struct device *dev, - unsigned int features, unsigned int rdac) +static int ad525x_attr_index(struct attribute *attr, + const struct attribute * const *attrs) { - int err = sysfs_create_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp[rdac]); - } + int i; - if (err) - dev_err(dev, "failed to register sysfs hooks for RDAC%d\n", - rdac); + for (i = 0; attrs[i]; i++) + if (attr == attrs[i]) + return i; - return err; + return -ENOENT; } -static inline void ad_dpot_remove_files(struct device *dev, - unsigned int features, unsigned int rdac) +static bool ad525x_is_command_attr(struct attribute *attr) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - sysfs_remove_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - sysfs_remove_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp[rdac]); + int i; + + for (i = 0; ad525x_attributes_commands[i]; i++) { + if (attr == ad525x_attributes_commands[i]) + return true; } + + return false; } +static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct dpot_data *data = dev_get_drvdata(dev); + int rdac; + + if (!data) + return 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_wipers); + if (rdac >= 0) + return data->wipers & BIT(rdac) ? attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_eeprom); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_tolerance); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp_en); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + if (ad525x_is_command_attr(attr)) + return data->feat & F_CMD_INC ? attr->mode : 0; + + return attr->mode; +} + +static const struct attribute_group ad525x_group = { + .attrs = ad525x_attributes, + .is_visible = ad525x_is_visible, +}; + +const struct attribute_group *ad_dpot_groups[] = { + &ad525x_group, + NULL +}; +EXPORT_SYMBOL(ad_dpot_groups); + int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name) { struct dpot_data *data; - int i, err = 0; + int i; data = kzalloc_obj(struct dpot_data); if (!data) { - err = -ENOMEM; - goto exit; + dev_err(dev, "failed to create client for %s ID 0x%lX\n", + name, devid); + return -ENOMEM; } dev_set_drvdata(dev, data); @@ -705,51 +771,22 @@ int ad_dpot_probe(struct device *dev, data->wipers = DPOT_WIPERS(devid); for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) { - err = ad_dpot_add_files(dev, data->feat, i); - if (err) - goto exit_remove_files; + if (data->wipers & BIT(i)) { /* power-up midscale */ if (data->feat & F_RDACS_WONLY) data->rdac_cache[i] = data->max_pos / 2; } - if (data->feat & F_CMD_INC) - err = sysfs_create_group(&dev->kobj, &ad525x_group_commands); - - if (err) { - dev_err(dev, "failed to register sysfs hooks\n"); - goto exit_free; - } - dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", name, data->max_pos); return 0; - -exit_remove_files: - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); - -exit_free: - kfree(data); - dev_set_drvdata(dev, NULL); -exit: - dev_err(dev, "failed to create client for %s ID 0x%lX\n", - name, devid); - return err; } EXPORT_SYMBOL(ad_dpot_probe); void ad_dpot_remove(struct device *dev) { struct dpot_data *data = dev_get_drvdata(dev); - int i; - - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); kfree(data); } diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h index 72a9d6801937..2e877c89523b 100644 --- a/drivers/misc/ad525x_dpot.h +++ b/drivers/misc/ad525x_dpot.h @@ -10,6 +10,8 @@ #include +struct attribute_group; + #define DPOT_CONF(features, wipers, max_pos, uid) \ (((features) << 18) | (((wipers) & 0xFF) << 10) | \ ((max_pos & 0xF) << 6) | (uid & 0x3F)) @@ -210,5 +212,6 @@ struct ad_dpot_bus_data { int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name); void ad_dpot_remove(struct device *dev); +extern const struct attribute_group *ad_dpot_groups[]; #endif diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig index 30e7fad7356c..1a96b71f8506 100644 --- a/drivers/misc/amd-sbi/Kconfig +++ b/drivers/misc/amd-sbi/Kconfig @@ -20,3 +20,16 @@ config AMD_SBRMI_HWMON This provides support for RMI device hardware monitoring. If enabled, a hardware monitoring device will be created for each socket in the system. + +config AMD_SBTSI + tristate "AMD side band TSI support" + depends on I3C_OR_I2C + depends on ARM || ARM64 || COMPILE_TEST + select AUXILIARY_BUS + help + Enables support for the AMD SB-TSI (Side Band Temperature Sensor + Interface) driver, which provides access to emulated CPU temperature + sensors on AMD SoCs via an I2C/I3C connected BMC device. + + This driver can also be built as a module. If so, the module will + be called sbtsi. diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile index 38eaaa651fd9..ce9321f5c601 100644 --- a/drivers/misc/amd-sbi/Makefile +++ b/drivers/misc/amd-sbi/Makefile @@ -2,3 +2,6 @@ sbrmi-i2c-objs += rmi-i2c.o rmi-core.o sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o +# SBTSI Configuration +sbtsi-objs += tsi.o tsi-core.o +obj-$(CONFIG_AMD_SBTSI) += sbtsi.o diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index d4238ebad3c6..95c9109101b5 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -581,6 +581,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.name) + return -ENOMEM; data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR; data->sbrmi_misc_dev.fops = &sbrmi_fops; data->sbrmi_misc_dev.parent = dev; @@ -588,6 +590,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.nodename) + return -ENOMEM; data->sbrmi_misc_dev.mode = 0600; return misc_register(&data->sbrmi_misc_dev); diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 37e5ea83bf97..463a9a757464 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -141,8 +141,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sbrmi_id[] = { - {"sbrmi-i2c"}, - {} + { .name = "sbrmi-i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbrmi_id); @@ -226,7 +226,7 @@ static const struct i3c_device_id sbrmi_i3c_id[] = { I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ - {} + { } }; MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id); diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c new file mode 100644 index 000000000000..5c178702c67a --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi-core.c - file defining SB-TSI protocols compliant + * AMD SoC device. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include "tsi-core.h" + +static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data) +{ + return container_of(data, struct sbtsi_i3c_priv, data); +} + +void sbtsi_data_release(struct kref *kref) +{ + struct sbtsi_data *data = container_of(kref, struct sbtsi_data, kref); + + mutex_destroy(&data->lock); + if (data->is_i3c) + kfree(to_sbtsi_i3c_priv(data)); + else + kfree(data); +} + +/* I2C transfer function */ +static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + if (is_read) { + int ret = i2c_smbus_read_byte_data(data->client, reg); + + if (ret < 0) + return ret; + *val = ret; + return 0; + } + return i2c_smbus_write_byte_data(data->client, reg, *val); +} + +/* I3C read transfer function */ +static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers[2] = { }; + int ret; + + priv->tx[0] = reg; + + /* Write the register address (DMA_TO_DEVICE). */ + xfers[0].rnw = false; + xfers[0].len = 1; + xfers[0].data.out = priv->tx; + + /* Read the data byte into a separate buffer (DMA_FROM_DEVICE). */ + xfers[1].rnw = true; + xfers[1].len = 1; + xfers[1].data.in = &priv->rx; + + ret = i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR); + if (ret) + return ret; + + *val = priv->rx; + return ret; +} + +/* I3C write transfer function */ +static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers = { + .rnw = false, + .len = 2, + .data.out = priv->tx, + }; + + priv->tx[0] = reg; + priv->tx[1] = val; + + return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR); +} + +/* Unified transfer function for I2C and I3C access */ +int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + if (data->is_i3c) + return is_read ? sbtsi_i3c_read(data, reg, val) + : sbtsi_i3c_write(data, reg, *val); + return sbtsi_i2c_xfer(data, reg, val, is_read); +} +EXPORT_SYMBOL_GPL(sbtsi_xfer); + +/* + * The mutex protects against concurrent register transfers to the device + * over the shared bus. + */ +static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + guard(sbtsi)(data); + + if (data->detached) + return -ENODEV; + + return sbtsi_xfer(data, reg, val, is_read); +} + +static int apml_tsi_reg_xfer(struct sbtsi_data *data, + struct apml_tsi_xfer_msg __user *arg) +{ + struct apml_tsi_xfer_msg msg = { 0 }; + int ret; + + if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + + /* + * rflag is a boolean direction flag (0 = write, 1 = read). Reject + * any other value so the upper values stay reserved for future + * extensions instead of being silently treated as a read. + */ + if (msg.pad || msg.rflag > 1) + return -EINVAL; + + ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag); + + if (msg.rflag && !ret) { + if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + } + return ret; +} + +static int sbtsi_open(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + scoped_guard(sbtsi, data) { + if (data->detached) + return -ENODEV; + } + + kref_get(&data->kref); + + return 0; +} + +static int sbtsi_release(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + kref_put(&data->kref, sbtsi_data_release); + return 0; +} + +static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + switch (cmd) { + case SBTSI_IOCTL_REG_XFER_CMD: + return apml_tsi_reg_xfer(data, argp); + default: + return -ENOTTY; + } +} + +static const struct file_operations sbtsi_fops = { + .owner = THIS_MODULE, + .open = sbtsi_open, + .release = sbtsi_release, + .unlocked_ioctl = sbtsi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev) +{ + int ret; + + data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.name) + return -ENOMEM; + data->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR; + data->sbtsi_misc_dev.fops = &sbtsi_fops; + data->sbtsi_misc_dev.parent = dev; + data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.nodename) + return -ENOMEM; + data->sbtsi_misc_dev.mode = 0600; + + ret = misc_register(&data->sbtsi_misc_dev); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h new file mode 100644 index 000000000000..4cf55c46230e --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SBTSI core driver private definitions. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#ifndef _LINUX_TSI_CORE_H_ +#define _LINUX_TSI_CORE_H_ + +#include +#include + +/** + * struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers) + * @data: public device state exposed via dev_set_drvdata() + * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value + * @rx: incoming I3C data byte (DMA_FROM_DEVICE) + */ +struct sbtsi_i3c_priv { + struct sbtsi_data data; + u8 tx[2]; + u8 rx __aligned(ARCH_DMA_MINALIGN); +}; + +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev); + +void sbtsi_data_release(struct kref *kref); +#endif /* _LINUX_TSI_CORE_H_ */ diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c new file mode 100644 index 000000000000..f06f417f451c --- /dev/null +++ b/drivers/misc/amd-sbi/tsi.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C + * and publishes an auxiliary device on the auxiliary bus. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include "tsi-core.h" + +#define SBTSI_REG_CONFIG 0x03 /* RO */ + +/* + * Bit for reporting value with temperature measurement range. + * bit == 0: Use default temperature range (0C to 255.875C). + * bit == 1: Use extended temperature range (-49C to +206.875C). + */ +#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 + +/* + * ReadOrder bit specifies the reading order of integer and decimal part of + * CPU temperature for atomic reads. If bit == 0, reading integer part triggers + * latching of the decimal part, so integer part should be read first. + */ +#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 + +static void sbtsi_adev_release(struct device *dev) +{ + kfree(to_auxiliary_dev(dev)); +} + +static void sbtsi_unregister_hwmon_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static void sbtsi_misc_unregister(void *arg) +{ + struct sbtsi_data *data = arg; + + misc_deregister(&data->sbtsi_misc_dev); + + guard(sbtsi)(data); + data->detached = true; +} + +static void sbtsi_driver_unref(void *arg) +{ + struct sbtsi_data *data = arg; + + kref_put(&data->kref, sbtsi_data_release); +} + +/* + * Create and publish an auxiliary device. The hwmon driver in + * drivers/hwmon/sbtsi_temp.c binds to this device. + * + * @dev: I2C device (parent of the auxiliary device) + * @dev_addr: I2C address — used as the auxiliary device instance ID so that + * each socket gets a unique name. + */ +static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc_obj(*adev); + if (!adev) + return -ENOMEM; + + adev->name = AMD_SBTSI_AUX_HWMON; + adev->id = dev_addr; + adev->dev.parent = dev; + adev->dev.release = sbtsi_adev_release; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); +} + +static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) +{ + u8 val; + int err; + + mutex_init(&data->lock); + kref_init(&data->kref); + + err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data); + if (err) + return err; + + err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); + if (err) + return err; + + data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val); + data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); + + dev_set_drvdata(dev, data); + err = sbtsi_create_hwmon_adev(dev, data->dev_addr); + if (err < 0) + return err; + + err = create_misc_tsi_device(data, dev); + if (err) + return err; + + return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data); +} + +static int sbtsi_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sbtsi_data *data; + + data = kzalloc_obj(*data); + if (!data) + return -ENOMEM; + + data->is_i3c = false; + data->client = client; + + /* In a multi-socket system, devices that are otherwise identical do not + * share the same static address; each instance resides at a unique I2C + * client address on the same or different bus. Use the I2C client + * address as the auxiliary device instance ID to ensure each socket + * receives a distinct auxiliary device name. + */ + data->dev_addr = client->addr; + return sbtsi_probe_common(dev, data); +} + +static const struct i2c_device_id sbtsi_id[] = { + { .name = "sbtsi" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sbtsi_id); + +static const struct of_device_id __maybe_unused sbtsi_of_match[] = { + { + .compatible = "amd,sbtsi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbtsi_of_match); + +static struct i2c_driver sbtsi_driver = { + .driver = { + .name = "sbtsi-i2c", + .of_match_table = of_match_ptr(sbtsi_of_match), + }, + .probe = sbtsi_i2c_probe, + .id_table = sbtsi_id, +}; + +static int sbtsi_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct i3c_device_info devinfo; + struct sbtsi_i3c_priv *i3c_priv; + struct sbtsi_data *data; + + /* + * AMD OOB devices differ on basis of Instance ID, + * for SBTSI, instance ID is 0. + * As the device Id match is not on basis of Instance ID, + * add the below check to probe the SBTSI device only and + * not other OOB devices. + */ + i3c_device_get_info(i3cdev, &devinfo); + if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) + return -ENXIO; + + i3c_priv = kzalloc_obj(*i3c_priv); + if (!i3c_priv) + return -ENOMEM; + + data = &i3c_priv->data; + data->i3cdev = i3cdev; + data->is_i3c = true; + /* + * In a multi-socket system, otherwise identical devices do not share + * the same address; each instance is enumerated with a distinct dynamic + * (assigned) address on the I3C bus. Use that address (passed in as + * dev_addr) as the auxiliary device instance ID so that every socket + * gets a unique auxiliary device name. + */ + data->dev_addr = devinfo.dyn_addr; + + return sbtsi_probe_common(dev, data); +} + +static const struct i3c_device_id sbtsi_i3c_id[] = { + /* PID for AMD SBTSI device */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ + {} +}; +MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id); + +static struct i3c_driver sbtsi_i3c_driver = { + .driver = { + .name = "sbtsi-i3c", + }, + .probe = sbtsi_i3c_probe, + .id_table = sbtsi_i3c_id, +}; + +module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver); + +MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index fc504bd8d916..1488e28819b7 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, #endif /* CONFIG_PM */ static const struct i2c_device_id apds9802als_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index 5773ffb46f0f..88ee3a417616 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1370,21 +1370,15 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_msgq_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } } /* one irq for notification from VK */ err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_notf_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } vk->num_irqs++; for (i = 0; @@ -1393,11 +1387,8 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_tty_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed request tty IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } bcm_vk_tty_set_irq_enabled(vk, i); } @@ -1632,7 +1623,7 @@ static void bcm_vk_shutdown(struct pci_dev *pdev) } static const struct pci_device_id bcm_vk_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE) }, { } }; MODULE_DEVICE_TABLE(pci, bcm_vk_ids); diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c index 3916ec07ecad..17114092a284 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.c +++ b/drivers/misc/bcm-vk/bcm_vk_msg.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq, bool bcm_vk_drv_access_ok(struct bcm_vk *vk) { - return (!!atomic_read(&vk->msgq_inited)); + /* Pair with the release store after message queue initialization. */ + return !!atomic_read_acquire(&vk->msgq_inited); } void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask) @@ -501,7 +503,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync) msgq++; } } - atomic_set(&vk->msgq_inited, 1); + /* Publish message queue info before allowing driver access. */ + atomic_set_release(&vk->msgq_inited, 1); return ret; } @@ -1088,6 +1091,7 @@ ssize_t bcm_vk_write(struct file *p_file, u32 q_num; u32 msg_size; u32 msgq_size; + size_t entry_size; if (!bcm_vk_drv_access_ok(vk)) return -EPERM; @@ -1095,20 +1099,26 @@ ssize_t bcm_vk_write(struct file *p_file, dev_dbg(dev, "Msg count %zu\n", count); /* first, do sanity check where count should be multiple of basic blk */ - if (count & (VK_MSGQ_BLK_SIZE - 1)) { - dev_err(dev, "Failure with size %zu not multiple of %zu\n", + if (!count || count & (VK_MSGQ_BLK_SIZE - 1)) { + dev_err(dev, "Failure with size %zu not a positive multiple of %zu\n", count, VK_MSGQ_BLK_SIZE); rc = -EINVAL; goto write_err; } + if (check_add_overflow(sizeof(*entry), count, &entry_size) || + check_add_overflow(entry_size, vk->ib_sgl_size, &entry_size)) { + rc = -EOVERFLOW; + goto write_err; + } + /* allocate the work entry + buffer for size count and inband sgl */ - entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size, - GFP_KERNEL); + entry = kzalloc(entry_size, GFP_KERNEL); if (!entry) { rc = -ENOMEM; goto write_err; } + entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; /* now copy msg from user space, and then formulate the work entry */ if (copy_from_user(&entry->to_v_msg[0], buf, count)) { @@ -1116,7 +1126,6 @@ ssize_t bcm_vk_write(struct file *p_file, goto write_free_ent; } - entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; entry->ctx = ctx; /* do a check on the blk size which could not exceed queue space */ @@ -1353,4 +1362,3 @@ void bcm_vk_msg_remove(struct bcm_vk *vk) bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL); bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL); } - diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 45f8fc69a711..98fc5970b2bb 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1363,9 +1363,9 @@ static int bh1770_runtime_resume(struct device *dev) #endif static const struct i2c_device_id bh1770_id[] = { - { "bh1770glc" }, - { "sfh7770" }, - {} + { .name = "bh1770glc" }, + { .name = "sfh7770" }, + { } }; MODULE_DEVICE_TABLE(i2c, bh1770_id); diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 8e7ea2c9142d..9e0f4f91763e 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -47,7 +47,7 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&au6621_cfg }, { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625), .driver_data = (kernel_ulong_t)&au6625_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index c4d54ca2fa80..c6e602523538 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1196,6 +1196,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Gating real mcu clock */ err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1, RTS5261_MCU_CLOCK_GATING, 0); + if (err < 0) + return err; err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, SSC_POWER_DOWN, 0); } else { diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 2dd212f04fed..8e0d11e59c4e 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -292,9 +292,8 @@ static void cb710_remove_one(struct pci_dev *pdev) } static const struct pci_device_id cb710_pci_tbl[] = { - { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH, - PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH) }, + { } }; static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume); diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index cb09e056531a..4500d7d0ea77 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -271,14 +271,14 @@ static void ds1682_remove(struct i2c_client *client) } static const struct i2c_device_id ds1682_id[] = { - { "ds1682" }, + { .name = "ds1682" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); static const struct of_device_id ds1682_of_match[] = { - { .compatible = "dallas,ds1682", }, - {} + { .compatible = "dallas,ds1682" }, + { } }; MODULE_DEVICE_TABLE(of, ds1682_of_match); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 4d0ce47aa282..d6af02588c66 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -1,52 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "EEPROM support" -config EEPROM_AT24 - tristate "I2C EEPROMs / RAMs / ROMs from most vendors" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - select REGMAP - select REGMAP_I2C - help - Enable this driver to get read/write support to most I2C EEPROMs - and compatible devices like FRAMs, SRAMs, ROMs etc. After you - configure the driver to know about each chip on your target - board. Use these generic chip names, instead of vendor-specific - ones like at24c64, 24lc02 or fm24c04: - - 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 - - Unless you like data loss puzzles, always be sure that any chip - you configure as a 24c32 (32 kbit) or larger is NOT really a - 24c16 (16 kbit) or smaller, and vice versa. Marking the chip - as read-only won't help recover from this. Also, if your chip - has any software write-protect mechanism you may want to review the - code to make sure this driver won't turn it on by accident. - - If you use this with an SMBus adapter instead of an I2C adapter, - full functionality is not available. Only smaller devices are - supported (24c16 and below, max 4 kByte). - - This driver can also be built as a module. If so, the module - will be called at24. - -config EEPROM_AT25 - tristate "SPI EEPROMs (FRAMs) from most vendors" - depends on SPI && SYSFS - select NVMEM - select NVMEM_SYSFS - select SPI_MEM - help - Enable this driver to get read/write support to most SPI EEPROMs - and Cypress FRAMs, - after you configure the board init code to know about each eeprom - on your target board. - - This driver can also be built as a module. If so, the module - will be called at25. - config EEPROM_MAX6875 tristate "Maxim MAX6874/5 power supply supervisor" depends on I2C @@ -69,22 +23,6 @@ config EEPROM_93CX6 If unsure, say N. -config EEPROM_93XX46 - tristate "Microwire EEPROM 93XX46 support" - depends on SPI && SYSFS - select REGMAP - select NVMEM - select NVMEM_SYSFS - help - Driver for the microwire EEPROM chipsets 93xx46x. The driver - supports both read and write commands and also the command to - erase the whole EEPROM. - - This driver can also be built as a module. If so, the module - will be called eeprom_93xx46. - - If unsure, say N. - config EEPROM_DIGSY_MTC_CFG bool "DigsyMTC display configuration EEPROMs device" depends on GPIO_MPC5200 && SPI_GPIO @@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX This driver can also be built as a module. If so, the module will be called idt_89hpesx. -config EEPROM_EE1004 - tristate "SPD EEPROMs on DDR4 memory modules" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - help - Enable this driver to get read support to SPD EEPROMs following - the JEDEC EE1004 standard. These are typically found on DDR4 - SDRAM memory modules. - - This driver can also be built as a module. If so, the module - will be called ee1004. - -config EEPROM_M24LR - tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" - depends on I2C && SYSFS - select REGMAP_I2C - select NVMEM - select NVMEM_SYSFS - help - This enables support for STMicroelectronics M24LR RFID/NFC EEPROM - chips. These dual-interface devices expose two I2C addresses: - one for EEPROM memory access and another for control and system - configuration (e.g. UID, password handling). - - This driver provides a sysfs interface for control functions and - integrates with the nvmem subsystem for EEPROM access. - - To compile this driver as a module, choose M here: the - module will be called m24lr. - endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 8f311fd6a4ce..44a20cf025a1 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_EEPROM_AT24) += at24.o -obj-$(CONFIG_EEPROM_AT25) += at25.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o -obj-$(CONFIG_EEPROM_EE1004) += ee1004.o -obj-$(CONFIG_EEPROM_M24LR) += m24lr.o diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index e056d2dea8c3..ea7478cfdd27 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1375,12 +1375,12 @@ static void idt_remove(struct i2c_client *client) * ee_ids - array of supported EEPROMs */ static const struct i2c_device_id ee_ids[] = { - { "24c32", 4096}, - { "24c64", 8192}, - { "24c128", 16384}, - { "24c256", 32768}, - { "24c512", 65536}, - {} + { .name = "24c32", .driver_data = 4096 }, + { .name = "24c64", .driver_data = 8192 }, + { .name = "24c128", .driver_data = 16384 }, + { .name = "24c256", .driver_data = 32768 }, + { .name = "24c512", .driver_data = 65536 }, + { } }; MODULE_DEVICE_TABLE(i2c, ee_ids); @@ -1388,115 +1388,115 @@ MODULE_DEVICE_TABLE(i2c, ee_ids); * idt_ids - supported IDT 89HPESx devices */ static const struct i2c_device_id idt_ids[] = { - { "89hpes8nt2" }, - { "89hpes12nt3" }, + { .name = "89hpes8nt2" }, + { .name = "89hpes12nt3" }, - { "89hpes24nt6ag2" }, - { "89hpes32nt8ag2" }, - { "89hpes32nt8bg2" }, - { "89hpes12nt12g2" }, - { "89hpes16nt16g2" }, - { "89hpes24nt24g2" }, - { "89hpes32nt24ag2" }, - { "89hpes32nt24bg2" }, + { .name = "89hpes24nt6ag2" }, + { .name = "89hpes32nt8ag2" }, + { .name = "89hpes32nt8bg2" }, + { .name = "89hpes12nt12g2" }, + { .name = "89hpes16nt16g2" }, + { .name = "89hpes24nt24g2" }, + { .name = "89hpes32nt24ag2" }, + { .name = "89hpes32nt24bg2" }, - { "89hpes12n3" }, - { "89hpes12n3a" }, - { "89hpes24n3" }, - { "89hpes24n3a" }, + { .name = "89hpes12n3" }, + { .name = "89hpes12n3a" }, + { .name = "89hpes24n3" }, + { .name = "89hpes24n3a" }, - { "89hpes32h8" }, - { "89hpes32h8g2" }, - { "89hpes48h12" }, - { "89hpes48h12g2" }, - { "89hpes48h12ag2" }, - { "89hpes16h16" }, - { "89hpes22h16" }, - { "89hpes22h16g2" }, - { "89hpes34h16" }, - { "89hpes34h16g2" }, - { "89hpes64h16" }, - { "89hpes64h16g2" }, - { "89hpes64h16ag2" }, + { .name = "89hpes32h8" }, + { .name = "89hpes32h8g2" }, + { .name = "89hpes48h12" }, + { .name = "89hpes48h12g2" }, + { .name = "89hpes48h12ag2" }, + { .name = "89hpes16h16" }, + { .name = "89hpes22h16" }, + { .name = "89hpes22h16g2" }, + { .name = "89hpes34h16" }, + { .name = "89hpes34h16g2" }, + { .name = "89hpes64h16" }, + { .name = "89hpes64h16g2" }, + { .name = "89hpes64h16ag2" }, - /* { "89hpes3t3" }, // No SMBus-slave iface */ - { "89hpes12t3g2" }, - { "89hpes24t3g2" }, - /* { "89hpes4t4" }, // No SMBus-slave iface */ - { "89hpes16t4" }, - { "89hpes4t4g2" }, - { "89hpes10t4g2" }, - { "89hpes16t4g2" }, - { "89hpes16t4ag2" }, - { "89hpes5t5" }, - { "89hpes6t5" }, - { "89hpes8t5" }, - { "89hpes8t5a" }, - { "89hpes24t6" }, - { "89hpes6t6g2" }, - { "89hpes24t6g2" }, - { "89hpes16t7" }, - { "89hpes32t8" }, - { "89hpes32t8g2" }, - { "89hpes48t12" }, - { "89hpes48t12g2" }, + /* { .name = "89hpes3t3" }, // No SMBus-slave iface */ + { .name = "89hpes12t3g2" }, + { .name = "89hpes24t3g2" }, + /* { .name = "89hpes4t4" }, // No SMBus-slave iface */ + { .name = "89hpes16t4" }, + { .name = "89hpes4t4g2" }, + { .name = "89hpes10t4g2" }, + { .name = "89hpes16t4g2" }, + { .name = "89hpes16t4ag2" }, + { .name = "89hpes5t5" }, + { .name = "89hpes6t5" }, + { .name = "89hpes8t5" }, + { .name = "89hpes8t5a" }, + { .name = "89hpes24t6" }, + { .name = "89hpes6t6g2" }, + { .name = "89hpes24t6g2" }, + { .name = "89hpes16t7" }, + { .name = "89hpes32t8" }, + { .name = "89hpes32t8g2" }, + { .name = "89hpes48t12" }, + { .name = "89hpes48t12g2" }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, idt_ids); static const struct of_device_id idt_of_match[] = { - { .compatible = "idt,89hpes8nt2", }, - { .compatible = "idt,89hpes12nt3", }, + { .compatible = "idt,89hpes8nt2" }, + { .compatible = "idt,89hpes12nt3" }, - { .compatible = "idt,89hpes24nt6ag2", }, - { .compatible = "idt,89hpes32nt8ag2", }, - { .compatible = "idt,89hpes32nt8bg2", }, - { .compatible = "idt,89hpes12nt12g2", }, - { .compatible = "idt,89hpes16nt16g2", }, - { .compatible = "idt,89hpes24nt24g2", }, - { .compatible = "idt,89hpes32nt24ag2", }, - { .compatible = "idt,89hpes32nt24bg2", }, + { .compatible = "idt,89hpes24nt6ag2" }, + { .compatible = "idt,89hpes32nt8ag2" }, + { .compatible = "idt,89hpes32nt8bg2" }, + { .compatible = "idt,89hpes12nt12g2" }, + { .compatible = "idt,89hpes16nt16g2" }, + { .compatible = "idt,89hpes24nt24g2" }, + { .compatible = "idt,89hpes32nt24ag2" }, + { .compatible = "idt,89hpes32nt24bg2" }, - { .compatible = "idt,89hpes12n3", }, - { .compatible = "idt,89hpes12n3a", }, - { .compatible = "idt,89hpes24n3", }, - { .compatible = "idt,89hpes24n3a", }, + { .compatible = "idt,89hpes12n3" }, + { .compatible = "idt,89hpes12n3a" }, + { .compatible = "idt,89hpes24n3" }, + { .compatible = "idt,89hpes24n3a" }, - { .compatible = "idt,89hpes32h8", }, - { .compatible = "idt,89hpes32h8g2", }, - { .compatible = "idt,89hpes48h12", }, - { .compatible = "idt,89hpes48h12g2", }, - { .compatible = "idt,89hpes48h12ag2", }, - { .compatible = "idt,89hpes16h16", }, - { .compatible = "idt,89hpes22h16", }, - { .compatible = "idt,89hpes22h16g2", }, - { .compatible = "idt,89hpes34h16", }, - { .compatible = "idt,89hpes34h16g2", }, - { .compatible = "idt,89hpes64h16", }, - { .compatible = "idt,89hpes64h16g2", }, - { .compatible = "idt,89hpes64h16ag2", }, + { .compatible = "idt,89hpes32h8" }, + { .compatible = "idt,89hpes32h8g2" }, + { .compatible = "idt,89hpes48h12" }, + { .compatible = "idt,89hpes48h12g2" }, + { .compatible = "idt,89hpes48h12ag2" }, + { .compatible = "idt,89hpes16h16" }, + { .compatible = "idt,89hpes22h16" }, + { .compatible = "idt,89hpes22h16g2" }, + { .compatible = "idt,89hpes34h16" }, + { .compatible = "idt,89hpes34h16g2" }, + { .compatible = "idt,89hpes64h16" }, + { .compatible = "idt,89hpes64h16g2" }, + { .compatible = "idt,89hpes64h16ag2" }, - { .compatible = "idt,89hpes12t3g2", }, - { .compatible = "idt,89hpes24t3g2", }, + { .compatible = "idt,89hpes12t3g2" }, + { .compatible = "idt,89hpes24t3g2" }, - { .compatible = "idt,89hpes16t4", }, - { .compatible = "idt,89hpes4t4g2", }, - { .compatible = "idt,89hpes10t4g2", }, - { .compatible = "idt,89hpes16t4g2", }, - { .compatible = "idt,89hpes16t4ag2", }, - { .compatible = "idt,89hpes5t5", }, - { .compatible = "idt,89hpes6t5", }, - { .compatible = "idt,89hpes8t5", }, - { .compatible = "idt,89hpes8t5a", }, - { .compatible = "idt,89hpes24t6", }, - { .compatible = "idt,89hpes6t6g2", }, - { .compatible = "idt,89hpes24t6g2", }, - { .compatible = "idt,89hpes16t7", }, - { .compatible = "idt,89hpes32t8", }, - { .compatible = "idt,89hpes32t8g2", }, - { .compatible = "idt,89hpes48t12", }, - { .compatible = "idt,89hpes48t12g2", }, - { }, + { .compatible = "idt,89hpes16t4" }, + { .compatible = "idt,89hpes4t4g2" }, + { .compatible = "idt,89hpes10t4g2" }, + { .compatible = "idt,89hpes16t4g2" }, + { .compatible = "idt,89hpes16t4ag2" }, + { .compatible = "idt,89hpes5t5" }, + { .compatible = "idt,89hpes6t5" }, + { .compatible = "idt,89hpes8t5" }, + { .compatible = "idt,89hpes8t5a" }, + { .compatible = "idt,89hpes24t6" }, + { .compatible = "idt,89hpes6t6g2" }, + { .compatible = "idt,89hpes24t6g2" }, + { .compatible = "idt,89hpes16t7" }, + { .compatible = "idt,89hpes32t8" }, + { .compatible = "idt,89hpes32t8g2" }, + { .compatible = "idt,89hpes48t12" }, + { .compatible = "idt,89hpes48t12g2" }, + { } }; MODULE_DEVICE_TABLE(of, idt_of_match); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 5731d2dc8a57..be5e6d19be10 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client) } static const struct i2c_device_id max6875_id[] = { - { "max6875" }, + { .name = "max6875" }, { } }; MODULE_DEVICE_TABLE(i2c, max6875_id); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index cf6382981777..de457378c501 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -184,8 +184,8 @@ EXPORT_SYMBOL_GPL(enclosure_unregister); static void enclosure_link_name(struct enclosure_component *cdev, char *name) { - strcpy(name, "enclosure_device:"); - strcat(name, dev_name(&cdev->cdev)); + snprintf(name, ENCLOSURE_NAME_SIZE, "enclosure_device:%s", + dev_name(&cdev->cdev)); } static void enclosure_remove_links(struct enclosure_component *cdev) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index eb6c2a78d3c7..90fd669636ec 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -22,7 +22,11 @@ #include #include #include +#include #include +#include +#include +#include #define ADSP_DOMAIN_ID (0) #define MDSP_DOMAIN_ID (1) @@ -37,7 +41,27 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_DSP_UTILITIES_HANDLE 2 -#define FASTRPC_CTXID_MASK (0xFF0) +/* + * Maximum handle value for static handles. + * Static handles are pre-defined, fixed numeric values statically assigned + * in the IDL file or FastRPC framework. + */ +#define FASTRPC_MAX_STATIC_HANDLE (20) +#define FASTRPC_CTXID_MASK GENMASK(15, 8) +/* Sequence number occupies bits 63:16 of the ctxid / message context */ +#define FASTRPC_CTXID_SEQ_SHIFT 16 +#define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16) + +/* + * Newer DSP firmware implements a PD (Protection Domain) notification + * framework that sends PD state notifications upon request. The PD exit + * notification is unconditionally sent by the DSP with this fixed sentinel + * in the context field rather than the context of an outstanding invocation. + * Since the fastrpc driver does not support the DSP PD notification framework, + * this message must be dropped rather than matched against the context idr. + */ +#define FASTRPC_DSP_PD_NOTIFY_CTX 0xABCDABCD + #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" @@ -105,6 +129,12 @@ #define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev) +/* Poll response number from remote processor for call completion */ +#define FASTRPC_POLL_RESPONSE (0xdecaf) + +/* Polling mode timeout limit */ +#define FASTRPC_POLL_MAX_TIMEOUT_US (10000) + struct fastrpc_phy_page { dma_addr_t addr; /* dma address */ u64 size; /* size of contiguous region */ @@ -233,9 +263,16 @@ struct fastrpc_invoke_ctx { int pid; int client_id; u32 sc; + u64 *fdlist; u32 *crc; + /* Poll memory that DSP updates */ + u32 *poll_addr; u64 ctxid; u64 msg_sz; + /* work done status flag */ + bool is_work_done; + /* process updates poll memory instead of glink response */ + bool is_polled; struct kref refcount; struct list_head node; /* list of ctxs */ struct completion work; @@ -261,6 +298,7 @@ struct fastrpc_soc_data { u32 sid_pos; u32 dma_addr_bits_cdsp; u32 dma_addr_bits_default; + bool poll_mode_supported; }; struct fastrpc_channel_ctx { @@ -283,6 +321,9 @@ struct fastrpc_channel_ctx { struct list_head invoke_interrupted_mmaps; bool secure; bool unsigned_support; + bool poll_mode_supported; + /* Per-channel sequence counter; incremented on every context allocation */ + atomic_t ctx_seq; u64 dma_mask; const struct fastrpc_soc_data *soc_data; }; @@ -306,6 +347,8 @@ struct fastrpc_user { int client_id; int pd; bool is_secure_dev; + /* Flags poll mode state */ + bool poll_mode; /* Lock for lists */ spinlock_t lock; /* lock for allocations */ @@ -422,6 +465,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, static void fastrpc_buf_free(struct fastrpc_buf *buf) { + if (!buf) + return; + dma_free_coherent(buf->dev, buf->size, buf->virt, fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr)); kfree(buf); @@ -515,8 +561,7 @@ static void fastrpc_user_free(struct kref *ref) struct fastrpc_map *map, *m; struct fastrpc_buf *buf, *b; - if (fl->init_mem) - fastrpc_buf_free(fl->init_mem); + fastrpc_buf_free(fl->init_mem); list_for_each_entry_safe(ctx, n, &fl->pending, node) { list_del(&ctx->node); @@ -561,11 +606,10 @@ static void fastrpc_context_free(struct kref *ref) for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); - if (ctx->buf) - fastrpc_buf_free(ctx->buf); + fastrpc_buf_free(ctx->buf); spin_lock_irqsave(&cctx->lock, flags); - idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4); + idr_remove(&cctx->ctx_idr, FIELD_GET(FASTRPC_CTXID_MASK, ctx->ctxid)); spin_unlock_irqrestore(&cctx->lock, flags); kfree(ctx->maps); @@ -703,7 +747,9 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( spin_unlock_irqrestore(&cctx->lock, flags); goto err_idr; } - ctx->ctxid = ret << 4; + ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret) | + FIELD_PREP(FASTRPC_CTXID_SEQ_MASK, + (u64)atomic_inc_return(&cctx->ctx_seq)); spin_unlock_irqrestore(&cctx->lock, flags); kref_init(&ctx->refcount); @@ -968,7 +1014,8 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx) sizeof(struct fastrpc_invoke_buf) + sizeof(struct fastrpc_phy_page)) * ctx->nscalars + sizeof(u64) * FASTRPC_MAX_FDLIST + - sizeof(u32) * FASTRPC_MAX_CRCLIST; + sizeof(u32) * FASTRPC_MAX_CRCLIST + + sizeof(u32); return size; } @@ -1063,6 +1110,10 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) rpra = ctx->buf->virt; list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); pages = fastrpc_phy_page_start(list, ctx->nscalars); + ctx->fdlist = (u64 *)(pages + ctx->nscalars); + ctx->poll_addr = (u32 *)((uintptr_t)ctx->fdlist + sizeof(u64) * FASTRPC_MAX_FDLIST + + sizeof(u32) * FASTRPC_MAX_CRCLIST); + args = (uintptr_t)ctx->buf->virt + metalen; rlen = pkt_size - metalen; ctx->rpra = rpra; @@ -1165,18 +1216,11 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, union fastrpc_remote_arg *rpra = ctx->rpra; struct fastrpc_user *fl = ctx->fl; struct fastrpc_map *mmap = NULL; - struct fastrpc_invoke_buf *list; - struct fastrpc_phy_page *pages; - u64 *fdlist; - int i, inbufs, outbufs, handles; + u64 *fdlist = ctx->fdlist; + int i, inbufs; int ret = 0; inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); - outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); - handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc); - list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); - pages = fastrpc_phy_page_start(list, ctx->nscalars); - fdlist = (uint64_t *)(pages + inbufs + outbufs + handles); for (i = inbufs; i < ctx->nbufs; ++i) { if (!ctx->maps[i]) { @@ -1239,6 +1283,71 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx, } +static u32 fastrpc_read_poll_addr(struct fastrpc_invoke_ctx *ctx) +{ + dma_rmb(); + return READ_ONCE(*ctx->poll_addr); +} + +static int poll_for_remote_response(struct fastrpc_invoke_ctx *ctx) +{ + u32 val; + int ret; + + /* + * Poll until DSP writes FASTRPC_POLL_RESPONSE into *ctx->poll_addr + * or until another path marks the work done. + */ + ret = read_poll_timeout_atomic(fastrpc_read_poll_addr, val, + (val == FASTRPC_POLL_RESPONSE) || ctx->is_work_done, 1, + FASTRPC_POLL_MAX_TIMEOUT_US, false, ctx); + + if (!ret && val == FASTRPC_POLL_RESPONSE) { + /* + * DSP writes FASTRPC_POLL_RESPONSE to signal successful + * completion via the poll path. + */ + ctx->is_work_done = true; + ctx->retval = 0; + } + + if (ret == -ETIMEDOUT) + ret = -EIO; + + return ret; +} + +static inline int fastrpc_wait_for_response(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err = 0; + + if (kernel) { + if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) + err = -ETIMEDOUT; + } else { + err = wait_for_completion_interruptible(&ctx->work); + } + + return err; +} + +static int fastrpc_wait_for_completion(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err; + + if (ctx->is_polled) { + err = poll_for_remote_response(ctx); + if (!err) + return 0; + /* If polling timed out or failed, move to normal response mode */ + ctx->is_polled = false; + } + + return fastrpc_wait_for_response(ctx, kernel); +} + static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, u32 handle, u32 sc, struct fastrpc_invoke_args *args) @@ -1274,13 +1383,14 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, if (err) goto bail; - if (kernel) { - if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) - err = -ETIMEDOUT; - } else { - err = wait_for_completion_interruptible(&ctx->work); - } + /* + * Set message context as polled if the call is for a user PD + * dynamic module and user has enabled poll mode. + */ + if (handle > FASTRPC_MAX_STATIC_HANDLE && fl->pd == USER_PD && fl->poll_mode) + ctx->is_polled = true; + err = fastrpc_wait_for_completion(ctx, kernel); if (err) goto bail; @@ -1846,6 +1956,35 @@ static int fastrpc_get_info_from_kernel(struct fastrpc_ioctl_capability *cap, return 0; } +static int fastrpc_set_option(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_ioctl_set_option opt = {0}; + int i; + + if (copy_from_user(&opt, argp, sizeof(opt))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(opt.reserved); i++) { + if (opt.reserved[i] != 0) + return -EINVAL; + } + + if (opt.request_id != FASTRPC_POLL_MODE) + return -EINVAL; + + if (!fl->cctx->poll_mode_supported) + return -EOPNOTSUPP; + + if (opt.value == FASTRPC_POLL_MODE_ENABLE) + fl->poll_mode = true; + else if (opt.value == FASTRPC_POLL_MODE_DISABLE) + fl->poll_mode = false; + else + return -EINVAL; + + return 0; +} + static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp) { struct fastrpc_ioctl_capability cap = {0}; @@ -2207,6 +2346,9 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, case FASTRPC_IOCTL_MEM_UNMAP: err = fastrpc_req_mem_unmap(fl, argp); break; + case FASTRPC_IOCTL_SET_OPTION: + err = fastrpc_set_option(fl, argp); + break; case FASTRPC_IOCTL_GET_DSP_INFO: err = fastrpc_get_dsp_info(fl, argp); break; @@ -2232,19 +2374,22 @@ static int fastrpc_cb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int i, sessions = 0; unsigned long flags; - int rc; u32 dma_bits; + u32 sid = 0; + int rc; cctx = dev_get_drvdata(dev->parent); if (!cctx) return -EINVAL; of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions); + if (of_property_read_u32(dev->of_node, "reg", &sid)) + dev_info(dev, "FastRPC Session ID not specified in DT\n"); spin_lock_irqsave(&cctx->lock, flags); if (cctx->sesscount >= FASTRPC_MAX_SESSIONS) { - dev_err(&pdev->dev, "too many sessions\n"); spin_unlock_irqrestore(&cctx->lock, flags); + dev_err(&pdev->dev, "too many sessions\n"); return -ENOSPC; } dma_bits = cctx->soc_data->dma_addr_bits_default; @@ -2253,13 +2398,11 @@ static int fastrpc_cb_probe(struct platform_device *pdev) sess->valid = true; sess->dev = dev; dev_set_drvdata(dev, sess); + sess->sid = sid; if (cctx->domain_id == CDSP_DOMAIN_ID) dma_bits = cctx->soc_data->dma_addr_bits_cdsp; - if (of_property_read_u32(dev->of_node, "reg", &sess->sid)) - dev_info(dev, "FastRPC Session ID not specified in DT\n"); - if (sessions > 0) { struct fastrpc_session_ctx *dup_sess; @@ -2298,8 +2441,8 @@ static void fastrpc_cb_remove(struct platform_device *pdev) } static const struct of_device_id fastrpc_match_table[] = { - { .compatible = "qcom,fastrpc-compute-cb", }, - {} + { .compatible = "qcom,fastrpc-compute-cb" }, + { } }; static struct platform_driver fastrpc_cb_driver = { @@ -2362,6 +2505,7 @@ static const struct fastrpc_soc_data kaanapali_soc_data = { .sid_pos = 56, .dma_addr_bits_cdsp = 34, .dma_addr_bits_default = 32, + .poll_mode_supported = true, }; static const struct fastrpc_soc_data default_soc_data = { @@ -2370,6 +2514,29 @@ static const struct fastrpc_soc_data default_soc_data = { .dma_addr_bits_default = 32, }; +/* + * Exception list for older platforms that use default_soc_data but whose + * DSP firmware supports FastRPC polling mode. + * + * NOTE: This list is intentionally closed. + * Do NOT add new platforms here. New SoCs must advertise polling mode + * support via their soc_data. + */ + +static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unused = { + { .compatible = "qcom,milos" }, + { .compatible = "qcom,qcs8300" }, + { .compatible = "qcom,sa8775p" }, + { .compatible = "qcom,sar2130p" }, + { .compatible = "qcom,sm8450" }, + { .compatible = "qcom,sm8550" }, + { .compatible = "qcom,sm8650" }, + { .compatible = "qcom,sm8750" }, + { .compatible = "qcom,x1e80100" }, + { .compatible = "qcom,x1p42100" }, + {}, +}; + static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *rdev = &rpdev->dev; @@ -2436,6 +2603,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); data->secure = secure_dsp; data->soc_data = soc_data; + data->poll_mode_supported = soc_data->poll_mode_supported || + of_machine_get_match(fastrpc_poll_supported_machines); switch (domain_id) { case ADSP_DOMAIN_ID: @@ -2465,6 +2634,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } kref_init(&data->refcount); + atomic_set(&data->ctx_seq, 0); rdev->dma_mask = &data->dma_mask; dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32)); @@ -2551,7 +2721,15 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, if (!cctx) return -ENODEV; - ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); + /* + * A PD exit notification from the DSP PD notification framework carries + * this sentinel rather than a real context. Drop it: a real context is + * (idr_index << 4) | pd and can never collide with this value. + */ + if (rsp->ctx == FASTRPC_DSP_PD_NOTIFY_CTX) + return 0; + + ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx); spin_lock_irqsave(&cctx->lock, flags); ctx = idr_find(&cctx->ctx_idr, ctxid); @@ -2562,7 +2740,25 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, return -ENOENT; } + /* + * Validate the sequence number embedded in the upper bits of the + * context ID. Under high concurrency the IDR slot can be recycled + * for a new request before a late (or duplicate) glink COMPLETE + * response for the previous request arrives. Without this check the + * driver would call complete() on the wrong context, waking a thread + * whose buffers are still being accessed by the DSP. + */ + if (FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) != + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) { + dev_dbg(&rpdev->dev, + "Stale glink response ctx 0x%llx (expected seq 0x%llx), dropping\n", + rsp->ctx, + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)); + return 0; + } + ctx->retval = rsp->retval; + ctx->is_work_done = true; complete(&ctx->work); /* @@ -2578,7 +2774,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, static const struct of_device_id fastrpc_rpmsg_of_match[] = { { .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data }, { .compatible = "qcom,fastrpc", .data = &default_soc_data }, - { }, + { } }; MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match); diff --git a/drivers/misc/gehc-achc.c b/drivers/misc/gehc-achc.c index b8fca4d393c6..9792189c0923 100644 --- a/drivers/misc/gehc-achc.c +++ b/drivers/misc/gehc-achc.c @@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi) } static const struct spi_device_id gehc_achc_id[] = { - { "ge,achc", 0 }, - { "achc", 0 }, + { .name = "ge,achc" }, + { .name = "achc" }, { } }; MODULE_DEVICE_TABLE(spi, gehc_achc_id); diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 86bfa82723ff..e602eb3c51fa 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -48,60 +48,42 @@ static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; /* PCI structure for identifying device by PCI vendor and device ID */ static const struct pci_device_id genwqe_device_table[] = { - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Initial SR-IOV bring-up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Fixed up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Even one more ... */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_NEW, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { 0, } /* 0 terminated list. */ + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5 << 8, + .class_mask = ~0, + }, { + /* Initial SR-IOV bring-up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Fixed up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Even one more ... */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5_NEW), + .class = (PCI_CLASSCODE_GENWQE5 << 8), + .class_mask = ~0, + }, + { } /* 0 terminated list. */ }; - MODULE_DEVICE_TABLE(pci, genwqe_device_table); /** diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index a2c4a9b4f871..e3b5fb337182 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -413,7 +413,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, size -= size_to_map; map_offs = 0; - if (prev_daddr == daddr) { + if (last_s && prev_daddr == daddr) { u32 prev_len = be32_to_cpu(last_s->len); /* pr_info("daddr combining: " diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 5ba40222eb12..033f30014aa6 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -274,18 +274,15 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) NULL, IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND, "pmic", priv); - if (ret < 0) { - dev_err(dev, "Failed to start IRQ handling thread: error %d\n", - ret); + if (ret < 0) return ret; - } return 0; } static const struct platform_device_id hi6421v600_irq_table[] = { { .name = "hi6421v600-irq" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table); diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c deleted file mode 100644 index 79f06001259b..000000000000 --- a/drivers/misc/hisi_hikey_usb.c +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for usb functionality of Hikey series boards - * based on Hisilicon Kirin Soc. - * - * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. - * http://www.huawei.com - * - * Authors: Yu Chen - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEVICE_DRIVER_NAME "hisi_hikey_usb" - -#define HUB_VBUS_POWER_ON 1 -#define HUB_VBUS_POWER_OFF 0 -#define USB_SWITCH_TO_HUB 1 -#define USB_SWITCH_TO_TYPEC 0 -#define TYPEC_VBUS_POWER_ON 1 -#define TYPEC_VBUS_POWER_OFF 0 - -struct hisi_hikey_usb { - struct device *dev; - struct gpio_desc *otg_switch; - struct gpio_desc *typec_vbus; - struct gpio_desc *reset; - - struct regulator *regulator; - - struct usb_role_switch *hub_role_sw; - - struct usb_role_switch *dev_role_sw; - enum usb_role role; - - struct mutex lock; - struct work_struct work; - - struct notifier_block nb; -}; - -static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value) -{ - int ret, status; - - if (!hisi_hikey_usb->regulator) - return; - - status = regulator_is_enabled(hisi_hikey_usb->regulator); - if (status == !!value) - return; - - if (value) - ret = regulator_enable(hisi_hikey_usb->regulator); - else - ret = regulator_disable(hisi_hikey_usb->regulator); - - if (ret) - dev_err(hisi_hikey_usb->dev, - "Can't switch regulator state to %s\n", - str_enabled_disabled(value)); -} - -static void usb_switch_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int switch_to) -{ - if (!hisi_hikey_usb->otg_switch) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->otg_switch, switch_to); -} - -static void usb_typec_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int value) -{ - if (!hisi_hikey_usb->typec_vbus) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->typec_vbus, value); -} - -static void relay_set_role_switch(struct work_struct *work) -{ - struct hisi_hikey_usb *hisi_hikey_usb = container_of(work, - struct hisi_hikey_usb, - work); - struct usb_role_switch *sw; - enum usb_role role; - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return; - - mutex_lock(&hisi_hikey_usb->lock); - switch (hisi_hikey_usb->role) { - case USB_ROLE_NONE: - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_HUB); - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_ON); - break; - case USB_ROLE_HOST: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_ON); - break; - case USB_ROLE_DEVICE: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - break; - default: - break; - } - sw = hisi_hikey_usb->dev_role_sw; - role = hisi_hikey_usb->role; - mutex_unlock(&hisi_hikey_usb->lock); - - usb_role_switch_set_role(sw, role); -} - -static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) -{ - struct hisi_hikey_usb *hisi_hikey_usb = usb_role_switch_get_drvdata(sw); - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return -EINVAL; - - mutex_lock(&hisi_hikey_usb->lock); - hisi_hikey_usb->role = role; - mutex_unlock(&hisi_hikey_usb->lock); - - schedule_work(&hisi_hikey_usb->work); - - return 0; -} - -static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev, - struct hisi_hikey_usb *hisi_hikey_usb) -{ - struct device *dev = &pdev->dev; - struct usb_role_switch_desc hub_role_switch = {NULL}; - - if (!device_property_read_bool(dev, "usb-role-switch")) - return 0; - - hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->otg_switch)) { - dev_err(dev, "get otg-switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->otg_switch)); - return PTR_ERR(hisi_hikey_usb->otg_switch); - } - - hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus", - GPIOD_OUT_LOW); - if (IS_ERR(hisi_hikey_usb->typec_vbus)) { - dev_err(dev, "get typec-vbus failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->typec_vbus)); - return PTR_ERR(hisi_hikey_usb->typec_vbus); - } - - hisi_hikey_usb->reset = devm_gpiod_get_optional(dev, - "hub-reset-en", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->reset)) { - dev_err(dev, "get hub-reset-en failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->reset)); - return PTR_ERR(hisi_hikey_usb->reset); - } - - hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev); - if (!hisi_hikey_usb->dev_role_sw) - return -EPROBE_DEFER; - if (IS_ERR(hisi_hikey_usb->dev_role_sw)) { - dev_err(dev, "get device role switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->dev_role_sw)); - return PTR_ERR(hisi_hikey_usb->dev_role_sw); - } - - INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch); - - hub_role_switch.fwnode = dev_fwnode(dev); - hub_role_switch.set = hub_usb_role_switch_set; - hub_role_switch.driver_data = hisi_hikey_usb; - - hisi_hikey_usb->hub_role_sw = usb_role_switch_register(dev, - &hub_role_switch); - - if (IS_ERR(hisi_hikey_usb->hub_role_sw)) { - dev_err(dev, - "failed to register hub role with error %ld\n", - PTR_ERR(hisi_hikey_usb->hub_role_sw)); - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - return PTR_ERR(hisi_hikey_usb->hub_role_sw); - } - - return 0; -} - -static int hisi_hikey_usb_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct hisi_hikey_usb *hisi_hikey_usb; - int ret; - - hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL); - if (!hisi_hikey_usb) - return -ENOMEM; - - hisi_hikey_usb->dev = &pdev->dev; - mutex_init(&hisi_hikey_usb->lock); - - hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd"); - if (IS_ERR(hisi_hikey_usb->regulator)) { - if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) { - dev_info(dev, "waiting for hub-vdd-supply\n"); - return PTR_ERR(hisi_hikey_usb->regulator); - } - dev_err(dev, "get hub-vdd-supply failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->regulator)); - return PTR_ERR(hisi_hikey_usb->regulator); - } - - ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb); - if (ret) - return ret; - - platform_set_drvdata(pdev, hisi_hikey_usb); - - return 0; -} - -static void hisi_hikey_usb_remove(struct platform_device *pdev) -{ - struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev); - - if (hisi_hikey_usb->hub_role_sw) { - usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw); - - if (hisi_hikey_usb->dev_role_sw) - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - } else { - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - } -} - -static const struct of_device_id id_table_hisi_hikey_usb[] = { - { .compatible = "hisilicon,usbhub" }, - {} -}; -MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb); - -static struct platform_driver hisi_hikey_usb_driver = { - .probe = hisi_hikey_usb_probe, - .remove = hisi_hikey_usb_remove, - .driver = { - .name = DEVICE_DRIVER_NAME, - .of_match_table = id_table_hisi_hikey_usb, - }, -}; - -module_platform_driver(hisi_hikey_usb_driver); - -MODULE_AUTHOR("Yu Chen "); -MODULE_DESCRIPTION("Driver Support for USB functionality of Hikey"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index ff92c6edff6b..275dc054ca04 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client) } static const struct i2c_device_id hmc6352_id[] = { - { "hmc6352" }, + { .name = "hmc6352" }, { } }; diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index ff3f03ea577e..8988858c2f31 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -33,10 +33,10 @@ static unsigned int max_ccb = 16; static char ilo_hwdev[MAX_ILO_DEV]; static const struct pci_device_id ilo_blacklist[] = { /* auxiliary iLO */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979) }, /* CL */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)}, - {} + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, + { } }; static inline int get_entry_id(int entry) @@ -160,11 +160,16 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, ret = fifo_dequeue(hw, fifobar, &entry); if (ret) { + int pkt_len; + pkt_id = get_entry_id(entry); + pkt_len = get_entry_len(entry); + if (pkt_id >= NR_QENTRY || pkt_len > desc_mem_sz(1)) + return 0; if (id) *id = pkt_id; if (len) - *len = get_entry_len(entry); + *len = pkt_len; if (pkt) *pkt = (void *)(desc + desc_mem_sz(pkt_id)); } diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile deleted file mode 100644 index 1b9dd0f44411..000000000000 --- a/drivers/misc/ibmasm/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_IBM_ASM) := ibmasm.o - -ibmasm-y := module.o \ - ibmasmfs.o \ - event.o \ - command.o \ - remote.o \ - heartbeat.o \ - r_heartbeat.o \ - dot_command.o \ - lowlevel.o - -ibmasm-$(CONFIG_SERIAL_8250) += uart.o - diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c deleted file mode 100644 index 6b037675ff45..000000000000 --- a/drivers/misc/ibmasm/command.c +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - -static void exec_next_command(struct service_processor *sp); - -static atomic_t command_count = ATOMIC_INIT(0); - -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size) -{ - struct command *cmd; - - if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE) - return NULL; - - cmd = kzalloc_obj(struct command); - if (cmd == NULL) - return NULL; - - - cmd->buffer = kzalloc(buffer_size, GFP_KERNEL); - if (cmd->buffer == NULL) { - kfree(cmd); - return NULL; - } - cmd->buffer_size = buffer_size; - - kref_init(&cmd->kref); - cmd->lock = &sp->lock; - - cmd->status = IBMASM_CMD_PENDING; - init_waitqueue_head(&cmd->wait); - INIT_LIST_HEAD(&cmd->queue_node); - - atomic_inc(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - - return cmd; -} - -void ibmasm_free_command(struct kref *kref) -{ - struct command *cmd = to_command(kref); - - list_del(&cmd->queue_node); - atomic_dec(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - kfree(cmd->buffer); - kfree(cmd); -} - -static void enqueue_command(struct service_processor *sp, struct command *cmd) -{ - list_add_tail(&cmd->queue_node, &sp->command_queue); -} - -static struct command *dequeue_command(struct service_processor *sp) -{ - struct command *cmd; - struct list_head *next; - - if (list_empty(&sp->command_queue)) - return NULL; - - next = sp->command_queue.next; - list_del_init(next); - cmd = list_entry(next, struct command, queue_node); - - return cmd; -} - -static inline void do_exec_command(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - if (ibmasm_send_i2o_message(sp)) { - sp->current_command->status = IBMASM_CMD_FAILED; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); - } -} - -/* - * exec_command - * send a command to a service processor - * Commands are executed sequentially. One command (sp->current_command) - * is sent to the service processor. Once the interrupt handler gets a - * message of type command_response, the message is copied into - * the current commands buffer, - */ -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - - if (!sp->current_command) { - sp->current_command = cmd; - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - enqueue_command(sp, cmd); - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -static void exec_next_command(struct service_processor *sp) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - sp->current_command = dequeue_command(sp); - if (sp->current_command) { - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -/* - * Sleep until a command has failed or a response has been received - * and the command status been updated by the interrupt handler. - * (see receive_response). - */ -void ibmasm_wait_for_response(struct command *cmd, int timeout) -{ - wait_event_interruptible_timeout(cmd->wait, - cmd->status == IBMASM_CMD_COMPLETE || - cmd->status == IBMASM_CMD_FAILED, - timeout * HZ); -} - -/* - * receive_command_response - * called by the interrupt handler when a dot command of type command_response - * was received. - */ -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size) -{ - struct command *cmd = sp->current_command; - - if (!sp->current_command) - return; - - memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size)); - cmd->status = IBMASM_CMD_COMPLETE; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); -} diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c deleted file mode 100644 index df389bd4c9df..000000000000 --- a/drivers/misc/ibmasm/dot_command.c +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Dispatch an incoming message to the specific handler for the message. - * Called from interrupt context. - */ -void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size) -{ - u32 size; - struct dot_command_header *header = (struct dot_command_header *)message; - - if (message_size == 0) - return; - - size = get_dot_command_size(message); - if (size == 0) - return; - - if (size > message_size) - size = message_size; - - switch (header->type) { - case sp_event: - ibmasm_receive_event(sp, message, size); - break; - case sp_command_response: - ibmasm_receive_command_response(sp, message, size); - break; - case sp_heartbeat: - ibmasm_receive_heartbeat(sp, message, size); - break; - default: - dev_err(sp->dev, "Received unknown message from service processor\n"); - } -} - - -#define INIT_BUFFER_SIZE 32 - - -/* - * send the 4.3.5.10 dot command (driver VPD) to the service processor - */ -int ibmasm_send_driver_vpd(struct service_processor *sp) -{ - struct command *command; - struct dot_command_header *header; - u8 *vpd_command; - u8 *vpd_data; - int result = 0; - - command = ibmasm_new_command(sp, INIT_BUFFER_SIZE); - if (command == NULL) - return -ENOMEM; - - header = (struct dot_command_header *)command->buffer; - header->type = sp_write; - header->command_size = 4; - header->data_size = 16; - header->status = 0; - header->reserved = 0; - - vpd_command = command->buffer + sizeof(struct dot_command_header); - vpd_command[0] = 0x4; - vpd_command[1] = 0x3; - vpd_command[2] = 0x5; - vpd_command[3] = 0xa; - - vpd_data = vpd_command + header->command_size; - vpd_data[0] = 0; - strcat(vpd_data, IBMASM_DRIVER_VPD); - vpd_data[10] = 0; - vpd_data[15] = 0; - - ibmasm_exec_command(sp, command); - ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL); - - if (command->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(command); - - return result; -} - -struct os_state_command { - struct dot_command_header header; - unsigned char command[3]; - unsigned char data; -}; - -/* - * send the 4.3.6 dot command (os state) to the service processor - * During driver init this function is called with os state "up". - * This causes the service processor to start sending heartbeats the - * driver. - * During driver exit the function is called with os state "down", - * causing the service processor to stop the heartbeats. - */ -int ibmasm_send_os_state(struct service_processor *sp, int os_state) -{ - struct command *cmd; - struct os_state_command *os_state_cmd; - int result = 0; - - cmd = ibmasm_new_command(sp, sizeof(struct os_state_command)); - if (cmd == NULL) - return -ENOMEM; - - os_state_cmd = (struct os_state_command *)cmd->buffer; - os_state_cmd->header.type = sp_write; - os_state_cmd->header.command_size = 3; - os_state_cmd->header.data_size = 1; - os_state_cmd->header.status = 0; - os_state_cmd->command[0] = 4; - os_state_cmd->command[1] = 3; - os_state_cmd->command[2] = 6; - os_state_cmd->data = os_state; - - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(cmd); - return result; -} diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h deleted file mode 100644 index e03399ebe239..000000000000 --- a/drivers/misc/ibmasm/dot_command.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#ifndef __DOT_COMMAND_H__ -#define __DOT_COMMAND_H__ - -/* - * dot commands are the protocol used to communicate with the service - * processor. - * They consist of header, a command of variable length and data of - * variable length. - */ - -/* dot command types */ -#define sp_write 0 -#define sp_write_next 1 -#define sp_read 2 -#define sp_read_next 3 -#define sp_command_response 4 -#define sp_event 5 -#define sp_heartbeat 6 - -#pragma pack(1) -struct dot_command_header { - u8 type; - u8 command_size; - u16 data_size; - u8 status; - u8 reserved; -}; -#pragma pack() - -static inline size_t get_dot_command_size(void *buffer) -{ - struct dot_command_header *cmd = (struct dot_command_header *)buffer; - return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size; -} - -static inline unsigned int get_dot_command_timeout(void *buffer) -{ - struct dot_command_header *header = (struct dot_command_header *)buffer; - unsigned char *cmd = buffer + sizeof(struct dot_command_header); - - /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */ - - if (header->command_size == 3) { - if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - } else if (header->command_size == 2) { - if ((cmd[0] == 7) && (cmd[1] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - if (cmd[0] == 8) - return IBMASM_CMD_TIMEOUT_EXTRA; - } - return IBMASM_CMD_TIMEOUT_NORMAL; -} - -#endif /* __DOT_COMMAND_H__ */ diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c deleted file mode 100644 index 3990bf2b1728..000000000000 --- a/drivers/misc/ibmasm/event.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - -/* - * ASM service processor event handling routines. - * - * Events are signalled to the device drivers through interrupts. - * They have the format of dot commands, with the type field set to - * sp_event. - * The driver does not interpret the events, it simply stores them in a - * circular buffer. - */ - -static void wake_up_event_readers(struct service_processor *sp) -{ - struct event_reader *reader; - - list_for_each_entry(reader, &sp->event_buffer->readers, node) - wake_up_interruptible(&reader->wait); -} - -/* - * receive_event - * Called by the interrupt handler when a dot command of type sp_event is - * received. - * Store the event in the circular event buffer, wake up any sleeping - * event readers. - * There is no reader marker in the buffer, therefore readers are - * responsible for keeping up with the writer, or they will lose events. - */ -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned long flags; - - data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); - - spin_lock_irqsave(&sp->lock, flags); - /* copy the event into the next slot in the circular buffer */ - event = &buffer->events[buffer->next_index]; - memcpy_fromio(event->data, data, data_size); - event->data_size = data_size; - event->serial_number = buffer->next_serial_number; - - /* advance indices in the buffer */ - buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; - buffer->next_serial_number++; - spin_unlock_irqrestore(&sp->lock, flags); - - wake_up_event_readers(sp); -} - -static inline int event_available(struct event_buffer *b, struct event_reader *r) -{ - return (r->next_serial_number < b->next_serial_number); -} - -/* - * get_next_event - * Called by event readers (initiated from user space through the file - * system). - * Sleeps until a new event is available. - */ -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned int index; - unsigned long flags; - - reader->cancelled = 0; - - if (wait_event_interruptible(reader->wait, - event_available(buffer, reader) || reader->cancelled)) - return -ERESTARTSYS; - - if (!event_available(buffer, reader)) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - - index = buffer->next_index; - event = &buffer->events[index]; - while (event->serial_number < reader->next_serial_number) { - index = (index + 1) % IBMASM_NUM_EVENTS; - event = &buffer->events[index]; - } - memcpy(reader->data, event->data, event->data_size); - reader->data_size = event->data_size; - reader->next_serial_number = event->serial_number + 1; - - spin_unlock_irqrestore(&sp->lock, flags); - - return event->data_size; -} - -void ibmasm_cancel_next_event(struct event_reader *reader) -{ - reader->cancelled = 1; - wake_up_interruptible(&reader->wait); -} - -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - reader->next_serial_number = sp->event_buffer->next_serial_number; - init_waitqueue_head(&reader->wait); - spin_lock_irqsave(&sp->lock, flags); - list_add(&reader->node, &sp->event_buffer->readers); - spin_unlock_irqrestore(&sp->lock, flags); -} - -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - spin_lock_irqsave(&sp->lock, flags); - list_del(&reader->node); - spin_unlock_irqrestore(&sp->lock, flags); -} - -int ibmasm_event_buffer_init(struct service_processor *sp) -{ - struct event_buffer *buffer; - struct ibmasm_event *event; - int i; - - buffer = kmalloc_obj(struct event_buffer); - if (!buffer) - return -ENOMEM; - - buffer->next_index = 0; - buffer->next_serial_number = 1; - - event = buffer->events; - for (i=0; iserial_number = 0; - - INIT_LIST_HEAD(&buffer->readers); - - sp->event_buffer = buffer; - - return 0; -} - -void ibmasm_event_buffer_exit(struct service_processor *sp) -{ - kfree(sp->event_buffer); -} diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c deleted file mode 100644 index 59c9a0d95659..000000000000 --- a/drivers/misc/ibmasm/heartbeat.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "dot_command.h" -#include "lowlevel.h" - -static int suspend_heartbeats = 0; - -/* - * Once the driver indicates to the service processor that it is running - * - see send_os_state() - the service processor sends periodic heartbeats - * to the driver. The driver must respond to the heartbeats or else the OS - * will be rebooted. - * In the case of a panic the interrupt handler continues to work and thus - * continues to respond to heartbeats, making the service processor believe - * the OS is still running and thus preventing a reboot. - * To prevent this from happening a callback is added the panic_notifier_list. - * Before responding to a heartbeat the driver checks if a panic has happened, - * if yes it suspends heartbeat, causing the service processor to reboot as - * expected. - */ -static int panic_happened(struct notifier_block *n, unsigned long val, void *v) -{ - suspend_heartbeats = 1; - return 0; -} - -static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; - -void ibmasm_register_panic_notifier(void) -{ - atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); -} - -void ibmasm_unregister_panic_notifier(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - &panic_notifier); -} - - -int ibmasm_heartbeat_init(struct service_processor *sp) -{ - sp->heartbeat = ibmasm_new_command(sp, HEARTBEAT_BUFFER_SIZE); - if (sp->heartbeat == NULL) - return -ENOMEM; - - return 0; -} - -void ibmasm_heartbeat_exit(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - ibmasm_wait_for_response(sp->heartbeat, IBMASM_CMD_TIMEOUT_NORMAL); - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - suspend_heartbeats = 1; - command_put(sp->heartbeat); -} - -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size) -{ - struct command *cmd = sp->heartbeat; - struct dot_command_header *header = (struct dot_command_header *)cmd->buffer; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - if (suspend_heartbeats) - return; - - /* return the received dot command to sender */ - cmd->status = IBMASM_CMD_PENDING; - size = min(size, cmd->buffer_size); - memcpy_fromio(cmd->buffer, message, size); - header->type = sp_write; - ibmasm_exec_command(sp, cmd); -} diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h deleted file mode 100644 index 468fa84a3257..000000000000 --- a/drivers/misc/ibmasm/i2o.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#pragma pack(1) -struct i2o_header { - u8 version; - u8 message_flags; - u16 message_size; - u8 target; - u8 initiator_and_target; - u8 initiator; - u8 function; - u32 initiator_context; -}; -#pragma pack() - -#define I2O_HEADER_TEMPLATE \ - { .version = 0x01, \ - .message_flags = 0x00, \ - .function = 0xFF, \ - .initiator = 0x00, \ - .initiator_and_target = 0x40, \ - .target = 0x00, \ - .initiator_context = 0x0 } - -#define I2O_MESSAGE_SIZE 0x1000 -#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header)) - -#pragma pack(1) -struct i2o_message { - struct i2o_header header; - void *data; -}; -#pragma pack() - -static inline unsigned short outgoing_message_size(unsigned int data_size) -{ - unsigned int size; - unsigned short i2o_size; - - if (data_size > I2O_COMMAND_SIZE) - data_size = I2O_COMMAND_SIZE; - - size = sizeof(struct i2o_header) + data_size; - - i2o_size = size / sizeof(u32); - - if (size % sizeof(u32)) - i2o_size++; - - return i2o_size; -} - -static inline u32 incoming_data_size(struct i2o_message *i2o_message) -{ - return (sizeof(u32) * i2o_message->header.message_size); -} diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h deleted file mode 100644 index a5ced88ca923..000000000000 --- a/drivers/misc/ibmasm/ibmasm.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Driver identification */ -#define DRIVER_NAME "ibmasm" -#define DRIVER_VERSION "1.0" -#define DRIVER_AUTHOR "Max Asbock , Vernon Mauery " -#define DRIVER_DESC "IBM ASM Service Processor Driver" - -#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME) -#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME) - -extern int ibmasm_debug; -#define dbg(STR, ARGS...) \ - do { \ - if (ibmasm_debug) \ - printk(KERN_DEBUG STR , ##ARGS); \ - } while (0) - -static inline char *get_timestamp(char *buf) -{ - struct timespec64 now; - - ktime_get_real_ts64(&now); - sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, - now.tv_nsec / NSEC_PER_USEC); - return buf; -} - -#define IBMASM_CMD_PENDING 0 -#define IBMASM_CMD_COMPLETE 1 -#define IBMASM_CMD_FAILED 2 - -#define IBMASM_CMD_TIMEOUT_NORMAL 45 -#define IBMASM_CMD_TIMEOUT_EXTRA 240 - -#define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000 - -#define REVERSE_HEARTBEAT_TIMEOUT 120 - -#define HEARTBEAT_BUFFER_SIZE 0x400 - -#ifdef IA64 -#define IBMASM_DRIVER_VPD "Lin64 6.08 " -#else -#define IBMASM_DRIVER_VPD "Lin32 6.08 " -#endif - -#define SYSTEM_STATE_OS_UP 5 -#define SYSTEM_STATE_OS_DOWN 4 - -#define IBMASM_NAME_SIZE 16 - -#define IBMASM_NUM_EVENTS 10 -#define IBMASM_EVENT_MAX_SIZE 2048u - - -struct command { - struct list_head queue_node; - wait_queue_head_t wait; - unsigned char *buffer; - size_t buffer_size; - int status; - struct kref kref; - spinlock_t *lock; -}; -#define to_command(c) container_of(c, struct command, kref) - -void ibmasm_free_command(struct kref *kref); -static inline void command_put(struct command *cmd) -{ - unsigned long flags; - spinlock_t *lock = cmd->lock; - - spin_lock_irqsave(lock, flags); - kref_put(&cmd->kref, ibmasm_free_command); - spin_unlock_irqrestore(lock, flags); -} - -static inline void command_get(struct command *cmd) -{ - kref_get(&cmd->kref); -} - - -struct ibmasm_event { - unsigned int serial_number; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct event_buffer { - struct ibmasm_event events[IBMASM_NUM_EVENTS]; - unsigned int next_serial_number; - unsigned int next_index; - struct list_head readers; -}; - -struct event_reader { - int cancelled; - unsigned int next_serial_number; - wait_queue_head_t wait; - struct list_head node; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct reverse_heartbeat { - wait_queue_head_t wait; - unsigned int stopped; -}; - -struct ibmasm_remote { - struct input_dev *keybd_dev; - struct input_dev *mouse_dev; -}; - -struct service_processor { - struct list_head node; - spinlock_t lock; - void __iomem *base_address; - unsigned int irq; - struct command *current_command; - struct command *heartbeat; - struct list_head command_queue; - struct event_buffer *event_buffer; - char dirname[IBMASM_NAME_SIZE]; - char devname[IBMASM_NAME_SIZE]; - unsigned int number; - struct ibmasm_remote remote; - int serial_line; - struct device *dev; -}; - -/* command processing */ -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size); -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd); -void ibmasm_wait_for_response(struct command *cmd, int timeout); -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size); - -/* event processing */ -int ibmasm_event_buffer_init(struct service_processor *sp); -void ibmasm_event_buffer_exit(struct service_processor *sp); -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size); -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader); -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader); -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader); -void ibmasm_cancel_next_event(struct event_reader *reader); - -/* heartbeat - from SP to OS */ -void ibmasm_register_panic_notifier(void); -void ibmasm_unregister_panic_notifier(void); -int ibmasm_heartbeat_init(struct service_processor *sp); -void ibmasm_heartbeat_exit(struct service_processor *sp); -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size); - -/* reverse heartbeat - from OS to SP */ -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb); - -/* dot commands */ -void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size); -int ibmasm_send_driver_vpd(struct service_processor *sp); -int ibmasm_send_os_state(struct service_processor *sp, int os_state); - -/* low level message processing */ -int ibmasm_send_i2o_message(struct service_processor *sp); -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id); - -/* remote console */ -void ibmasm_handle_mouse_interrupt(struct service_processor *sp); -int ibmasm_init_remote_input_dev(struct service_processor *sp); -void ibmasm_free_remote_input_dev(struct service_processor *sp); - -/* file system */ -int ibmasmfs_register(void); -void ibmasmfs_unregister(void); -void ibmasmfs_add_sp(struct service_processor *sp); - -/* uart */ -#if IS_ENABLED(CONFIG_SERIAL_8250) -void ibmasm_register_uart(struct service_processor *sp); -void ibmasm_unregister_uart(struct service_processor *sp); -#else -#define ibmasm_register_uart(sp) do { } while(0) -#define ibmasm_unregister_uart(sp) do { } while(0) -#endif diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c deleted file mode 100644 index dfdfa9ba4747..000000000000 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ /dev/null @@ -1,604 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -/* - * Parts of this code are based on an article by Jonathan Corbet - * that appeared in Linux Weekly News. - */ - - -/* - * The IBMASM file virtual filesystem. It creates the following hierarchy - * dynamically when mounted from user space: - * - * /ibmasm - * |-- 0 - * | |-- command - * | |-- event - * | |-- reverse_heartbeat - * | `-- remote_video - * | |-- depth - * | |-- height - * | `-- width - * . - * . - * . - * `-- n - * |-- command - * |-- event - * |-- reverse_heartbeat - * `-- remote_video - * |-- depth - * |-- height - * `-- width - * - * For each service processor the following files are created: - * - * command: execute dot commands - * write: execute a dot command on the service processor - * read: return the result of a previously executed dot command - * - * events: listen for service processor events - * read: sleep (interruptible) until an event occurs - * write: wakeup sleeping event listener - * - * reverse_heartbeat: send a heartbeat to the service processor - * read: sleep (interruptible) until the reverse heartbeat fails - * write: wakeup sleeping heartbeat listener - * - * remote_video/width - * remote_video/height - * remote_video/width: control remote display settings - * write: set value - * read: read value - */ - -#include -#include -#include -#include -#include -#include -#include "ibmasm.h" -#include "remote.h" -#include "dot_command.h" - -#define IBMASMFS_MAGIC 0x66726f67 - -static LIST_HEAD(service_processors); - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode); -static void ibmasmfs_create_files (struct super_block *sb); -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc); - -static int ibmasmfs_get_tree(struct fs_context *fc) -{ - return get_tree_single(fc, ibmasmfs_fill_super); -} - -static const struct fs_context_operations ibmasmfs_context_ops = { - .get_tree = ibmasmfs_get_tree, -}; - -static int ibmasmfs_init_fs_context(struct fs_context *fc) -{ - fc->ops = &ibmasmfs_context_ops; - return 0; -} - -static const struct super_operations ibmasmfs_s_ops = { - .statfs = simple_statfs, - .drop_inode = inode_just_drop, -}; - -static struct file_system_type ibmasmfs_type = { - .owner = THIS_MODULE, - .name = "ibmasmfs", - .init_fs_context = ibmasmfs_init_fs_context, - .kill_sb = kill_anon_super, -}; -MODULE_ALIAS_FS("ibmasmfs"); - -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc) -{ - struct inode *root; - - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - sb->s_magic = IBMASMFS_MAGIC; - sb->s_op = &ibmasmfs_s_ops; - sb->s_time_gran = 1; - - root = ibmasmfs_make_inode (sb, S_IFDIR | 0500); - if (!root) - return -ENOMEM; - - root->i_op = &simple_dir_inode_operations; - root->i_fop = &simple_dir_operations; - - sb->s_root = d_make_root(root); - if (!sb->s_root) - return -ENOMEM; - - ibmasmfs_create_files(sb); - return 0; -} - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) -{ - struct inode *ret = new_inode(sb); - - if (ret) { - ret->i_ino = get_next_ino(); - ret->i_mode = mode; - simple_inode_init_ts(ret); - } - return ret; -} - -static int ibmasmfs_create_file(struct dentry *parent, - const char *name, - const struct file_operations *fops, - void *data, - int mode) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return -ENOMEM; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode); - if (!inode) { - dput(dentry); - return -ENOMEM; - } - - inode->i_fop = fops; - inode->i_private = data; - - d_make_persistent(dentry, inode); - dput(dentry); - return 0; -} - -static struct dentry *ibmasmfs_create_dir(struct dentry *parent, - const char *name) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return NULL; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500); - if (!inode) { - dput(dentry); - return NULL; - } - - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - d_make_persistent(dentry, inode); - dput(dentry); - return dentry; // borrowed -} - -int ibmasmfs_register(void) -{ - return register_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_unregister(void) -{ - unregister_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_add_sp(struct service_processor *sp) -{ - list_add(&sp->node, &service_processors); -} - -/* struct to save state between command file operations */ -struct ibmasmfs_command_data { - struct service_processor *sp; - struct command *command; -}; - -/* struct to save state between event file operations */ -struct ibmasmfs_event_data { - struct service_processor *sp; - struct event_reader reader; - int active; -}; - -/* struct to save state between reverse heartbeat file operations */ -struct ibmasmfs_heartbeat_data { - struct service_processor *sp; - struct reverse_heartbeat heartbeat; - int active; -}; - -static int command_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data; - - if (!inode->i_private) - return -ENODEV; - - command_data = kmalloc_obj(struct ibmasmfs_command_data); - if (!command_data) - return -ENOMEM; - - command_data->command = NULL; - command_data->sp = inode->i_private; - file->private_data = command_data; - return 0; -} - -static int command_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - - if (command_data->command) - command_put(command_data->command); - - kfree(command_data); - return 0; -} - -static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - int len; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&command_data->sp->lock, flags); - cmd = command_data->command; - if (cmd == NULL) { - spin_unlock_irqrestore(&command_data->sp->lock, flags); - return 0; - } - command_data->command = NULL; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - if (cmd->status != IBMASM_CMD_COMPLETE) { - command_put(cmd); - return -EIO; - } - len = min(count, cmd->buffer_size); - if (copy_to_user(buf, cmd->buffer, len)) { - command_put(cmd); - return -EFAULT; - } - command_put(cmd); - - return len; -} - -static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) - return 0; - if (count < sizeof(struct dot_command_header)) - return -EINVAL; - if (*offset != 0) - return 0; - - /* commands are executed sequentially, only one command at a time */ - if (command_data->command) - return -EAGAIN; - - cmd = ibmasm_new_command(command_data->sp, count); - if (!cmd) - return -ENOMEM; - - if (copy_from_user(cmd->buffer, ubuff, count)) { - command_put(cmd); - return -EFAULT; - } - - if (count < get_dot_command_size(cmd->buffer)) { - command_put(cmd); - return -EINVAL; - } - - spin_lock_irqsave(&command_data->sp->lock, flags); - if (command_data->command) { - spin_unlock_irqrestore(&command_data->sp->lock, flags); - command_put(cmd); - return -EAGAIN; - } - command_data->command = cmd; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - ibmasm_exec_command(command_data->sp, cmd); - ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer)); - - return count; -} - -static int event_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data; - struct service_processor *sp; - - if (!inode->i_private) - return -ENODEV; - - sp = inode->i_private; - - event_data = kmalloc_obj(struct ibmasmfs_event_data); - if (!event_data) - return -ENOMEM; - - ibmasm_event_reader_register(sp, &event_data->reader); - - event_data->sp = sp; - event_data->active = 0; - file->private_data = event_data; - return 0; -} - -static int event_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - ibmasm_event_reader_unregister(event_data->sp, &event_data->reader); - kfree(event_data); - return 0; -} - -static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - struct event_reader *reader = &event_data->reader; - struct service_processor *sp = event_data->sp; - int ret; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_EVENT_MAX_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - if (event_data->active) { - spin_unlock_irqrestore(&sp->lock, flags); - return -EBUSY; - } - event_data->active = 1; - spin_unlock_irqrestore(&sp->lock, flags); - - ret = ibmasm_get_next_event(sp, reader); - if (ret <= 0) - goto out; - - if (count < reader->data_size) { - ret = -EINVAL; - goto out; - } - - if (copy_to_user(buf, reader->data, reader->data_size)) { - ret = -EFAULT; - goto out; - } - ret = reader->data_size; - -out: - event_data->active = 0; - return ret; -} - -static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - ibmasm_cancel_next_event(&event_data->reader); - return 0; -} - -static int r_heartbeat_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat; - - if (!inode->i_private) - return -ENODEV; - - rhbeat = kmalloc_obj(struct ibmasmfs_heartbeat_data); - if (!rhbeat) - return -ENOMEM; - - rhbeat->sp = inode->i_private; - rhbeat->active = 0; - ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - file->private_data = rhbeat; - return 0; -} - -static int r_heartbeat_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - kfree(rhbeat); - return 0; -} - -static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - unsigned long flags; - int result; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - /* allow only one reverse heartbeat per process */ - spin_lock_irqsave(&rhbeat->sp->lock, flags); - if (rhbeat->active) { - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - return -EBUSY; - } - rhbeat->active = 1; - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - - result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - rhbeat->active = 0; - - return result; -} - -static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - if (rhbeat->active) - ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat); - - return 1; -} - -static int remote_settings_file_close(struct inode *inode, struct file *file) -{ - return 0; -} - -static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - int len = 0; - unsigned int value; - char lbuf[20]; - - value = readl(address); - len = snprintf(lbuf, sizeof(lbuf), "%d\n", value); - - return simple_read_from_buffer(buf, count, offset, lbuf, len); -} - -static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - char *buff; - unsigned int value; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - buff = memdup_user_nul(ubuff, count); - if (IS_ERR(buff)) - return PTR_ERR(buff); - - value = simple_strtoul(buff, NULL, 10); - writel(value, address); - kfree(buff); - - return count; -} - -static const struct file_operations command_fops = { - .open = command_file_open, - .release = command_file_close, - .read = command_file_read, - .write = command_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations event_fops = { - .open = event_file_open, - .release = event_file_close, - .read = event_file_read, - .write = event_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations r_heartbeat_fops = { - .open = r_heartbeat_file_open, - .release = r_heartbeat_file_close, - .read = r_heartbeat_file_read, - .write = r_heartbeat_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations remote_settings_fops = { - .open = simple_open, - .release = remote_settings_file_close, - .read = remote_settings_file_read, - .write = remote_settings_file_write, - .llseek = generic_file_llseek, -}; - - -static void ibmasmfs_create_files (struct super_block *sb) -{ - struct list_head *entry; - struct service_processor *sp; - - list_for_each(entry, &service_processors) { - struct dentry *dir; - struct dentry *remote_dir; - sp = list_entry(entry, struct service_processor, node); - dir = ibmasmfs_create_dir(sb->s_root, sp->dirname); - if (!dir) - continue; - - ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR); - - remote_dir = ibmasmfs_create_dir(dir, "remote_video"); - if (!remote_dir) - continue; - - ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR); - } -} diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c deleted file mode 100644 index 5313230f36ad..000000000000 --- a/drivers/misc/ibmasm/lowlevel.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include "ibmasm.h" -#include "lowlevel.h" -#include "i2o.h" -#include "dot_command.h" -#include "remote.h" - -static struct i2o_header header = I2O_HEADER_TEMPLATE; - - -int ibmasm_send_i2o_message(struct service_processor *sp) -{ - u32 mfa; - size_t command_size; - struct i2o_message *message; - struct command *command = sp->current_command; - - command_size = get_dot_command_size(command->buffer); - if (command_size > command->buffer_size) - return 1; - if (command_size > I2O_COMMAND_SIZE) - command_size = I2O_COMMAND_SIZE; - - mfa = get_mfa_inbound(sp->base_address); - if (!mfa) - return 1; - - header.message_size = outgoing_message_size((unsigned int)command_size); - message = get_i2o_message(sp->base_address, mfa); - - memcpy_toio(&message->header, &header, sizeof(struct i2o_header)); - memcpy_toio(&message->data, command->buffer, command_size); - - set_mfa_inbound(sp->base_address, mfa); - - return 0; -} - -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id) -{ - u32 mfa; - struct service_processor *sp = (struct service_processor *)dev_id; - void __iomem *base_address = sp->base_address; - char tsbuf[32]; - - if (!sp_interrupt_pending(base_address)) - return IRQ_NONE; - - dbg("respond to interrupt at %s\n", get_timestamp(tsbuf)); - - if (mouse_interrupt_pending(sp)) { - ibmasm_handle_mouse_interrupt(sp); - clear_mouse_interrupt(sp); - } - - mfa = get_mfa_outbound(base_address); - if (valid_mfa(mfa)) { - struct i2o_message *msg = get_i2o_message(base_address, mfa); - ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg)); - } else - dbg("didn't get a valid MFA\n"); - - set_mfa_outbound(base_address, mfa); - dbg("finished interrupt at %s\n", get_timestamp(tsbuf)); - - return IRQ_HANDLED; -} diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h deleted file mode 100644 index 25f1ed07c3c5..000000000000 --- a/drivers/misc/ibmasm/lowlevel.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -/* Condor service processor specific hardware definitions */ - -#ifndef __IBMASM_CONDOR_H__ -#define __IBMASM_CONDOR_H__ - -#include - -#define VENDORID_IBM 0x1014 -#define DEVICEID_RSA 0x010F - -#define GET_MFA_ADDR(x) (x & 0xFFFFFF00) - -#define MAILBOX_FULL(x) (x & 0x00000001) - -#define NO_MFAS_AVAILABLE 0xFFFFFFFF - - -#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */ -#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */ - -#define SP_INTR_MASK 0x00000008 -#define UART_INTR_MASK 0x00000010 - -#define INTR_STATUS_REGISTER 0x13A0 -#define INTR_CONTROL_REGISTER 0x13A4 - -#define SCOUT_COM_A_BASE 0x0000 -#define SCOUT_COM_B_BASE 0x0100 -#define SCOUT_COM_C_BASE 0x0200 -#define SCOUT_COM_D_BASE 0x0300 - -static inline int sp_interrupt_pending(void __iomem *base_address) -{ - return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline int uart_interrupt_pending(void __iomem *base_address) -{ - return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) & ~mask, ctrl_reg); -} - -static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) | mask, ctrl_reg); -} - -static inline void enable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void disable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void enable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, UART_INTR_MASK); -} - -static inline void disable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, UART_INTR_MASK); -} - -#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE ) - -static inline u32 get_mfa_outbound(void __iomem *base_address) -{ - int retry; - u32 mfa; - - for (retry=0; retry<=10; retry++) { - mfa = readl(base_address + OUTBOUND_QUEUE_PORT); - if (valid_mfa(mfa)) - break; - } - return mfa; -} - -static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + OUTBOUND_QUEUE_PORT); -} - -static inline u32 get_mfa_inbound(void __iomem *base_address) -{ - u32 mfa = readl(base_address + INBOUND_QUEUE_PORT); - - if (MAILBOX_FULL(mfa)) - return 0; - - return mfa; -} - -static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + INBOUND_QUEUE_PORT); -} - -static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa) -{ - return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address); -} - -#endif /* __IBMASM_CONDOR_H__ */ diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c deleted file mode 100644 index 4509c15a76a8..000000000000 --- a/drivers/misc/ibmasm/module.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - * - * This driver is based on code originally written by Pete Reynolds - * and others. - */ - -/* - * The ASM device driver does the following things: - * - * 1) When loaded it sends a message to the service processor, - * indicating that an OS is * running. This causes the service processor - * to send periodic heartbeats to the OS. - * - * 2) Answers the periodic heartbeats sent by the service processor. - * Failure to do so would result in system reboot. - * - * 3) Acts as a pass through for dot commands sent from user applications. - * The interface for this is the ibmasmfs file system. - * - * 4) Allows user applications to register for event notification. Events - * are sent to the driver through interrupts. They can be read from user - * space through the ibmasmfs file system. - * - * 5) Allows user space applications to send heartbeats to the service - * processor (aka reverse heartbeats). Again this happens through ibmasmfs. - * - * 6) Handles remote mouse and keyboard event interrupts and makes them - * available to user applications through ibmasmfs. - * - */ - -#include -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" -#include "remote.h" - -int ibmasm_debug = 0; -module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off"); - - -static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int result; - struct service_processor *sp; - - if ((result = pci_enable_device(pdev))) { - dev_err(&pdev->dev, "Failed to enable PCI device\n"); - return result; - } - if ((result = pci_request_regions(pdev, DRIVER_NAME))) { - dev_err(&pdev->dev, "Failed to allocate PCI resources\n"); - goto error_resources; - } - /* vnc client won't work without bus-mastering */ - pci_set_master(pdev); - - sp = kzalloc_obj(struct service_processor); - if (sp == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory\n"); - result = -ENOMEM; - goto error_kmalloc; - } - - spin_lock_init(&sp->lock); - INIT_LIST_HEAD(&sp->command_queue); - - pci_set_drvdata(pdev, (void *)sp); - sp->dev = &pdev->dev; - sp->number = pdev->bus->number; - snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number); - snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number); - - result = ibmasm_event_buffer_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate event buffer\n"); - goto error_eventbuffer; - } - - result = ibmasm_heartbeat_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate heartbeat command\n"); - goto error_heartbeat; - } - - sp->irq = pdev->irq; - sp->base_address = pci_ioremap_bar(pdev, 0); - if (!sp->base_address) { - dev_err(sp->dev, "Failed to ioremap pci memory\n"); - result = -ENODEV; - goto error_ioremap; - } - - result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp); - if (result) { - dev_err(sp->dev, "Failed to register interrupt handler\n"); - goto error_request_irq; - } - - enable_sp_interrupts(sp->base_address); - - result = ibmasm_init_remote_input_dev(sp); - if (result) { - dev_err(sp->dev, "Failed to initialize remote queue\n"); - goto error_init_remote; - } - - result = ibmasm_send_driver_vpd(sp); - if (result) { - dev_err(sp->dev, "Failed to send driver VPD to service processor\n"); - goto error_send_message; - } - result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP); - if (result) { - dev_err(sp->dev, "Failed to send OS state to service processor\n"); - goto error_send_message; - } - ibmasmfs_add_sp(sp); - - ibmasm_register_uart(sp); - - return 0; - -error_send_message: - ibmasm_free_remote_input_dev(sp); -error_init_remote: - disable_sp_interrupts(sp->base_address); - free_irq(sp->irq, (void *)sp); -error_request_irq: - iounmap(sp->base_address); -error_ioremap: - ibmasm_heartbeat_exit(sp); -error_heartbeat: - ibmasm_event_buffer_exit(sp); -error_eventbuffer: - kfree(sp); -error_kmalloc: - pci_release_regions(pdev); -error_resources: - pci_disable_device(pdev); - - return result; -} - -static void ibmasm_remove_one(struct pci_dev *pdev) -{ - struct service_processor *sp = pci_get_drvdata(pdev); - - dbg("Unregistering UART\n"); - ibmasm_unregister_uart(sp); - dbg("Sending OS down message\n"); - if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN)) - err("failed to get response to 'Send OS State' command\n"); - dbg("Disabling heartbeats\n"); - ibmasm_heartbeat_exit(sp); - dbg("Disabling interrupts\n"); - disable_sp_interrupts(sp->base_address); - dbg("Freeing SP irq\n"); - free_irq(sp->irq, (void *)sp); - dbg("Cleaning up\n"); - ibmasm_free_remote_input_dev(sp); - iounmap(sp->base_address); - ibmasm_event_buffer_exit(sp); - kfree(sp); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static struct pci_device_id ibmasm_pci_table[] = -{ - { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) }, - {}, -}; - -static struct pci_driver ibmasm_driver = { - .name = DRIVER_NAME, - .id_table = ibmasm_pci_table, - .probe = ibmasm_init_one, - .remove = ibmasm_remove_one, -}; - -static void __exit ibmasm_exit (void) -{ - ibmasm_unregister_panic_notifier(); - ibmasmfs_unregister(); - pci_unregister_driver(&ibmasm_driver); - info(DRIVER_DESC " version " DRIVER_VERSION " unloaded"); -} - -static int __init ibmasm_init(void) -{ - int result = pci_register_driver(&ibmasm_driver); - if (result) - return result; - - result = ibmasmfs_register(); - if (result) { - pci_unregister_driver(&ibmasm_driver); - err("Failed to register ibmasmfs file system"); - return result; - } - - ibmasm_register_panic_notifier(); - info(DRIVER_DESC " version " DRIVER_VERSION " loaded"); - return 0; -} - -module_init(ibmasm_init); -module_exit(ibmasm_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, ibmasm_pci_table); - diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c deleted file mode 100644 index 21c9b6a6f2c3..000000000000 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Reverse Heartbeat, i.e. heartbeats sent from the driver to the - * service processor. - * These heartbeats are initiated by user level programs. - */ - -/* the reverse heartbeat dot command */ -#pragma pack(1) -static struct { - struct dot_command_header header; - unsigned char command[3]; -} rhb_dot_cmd = { - .header = { - .type = sp_read, - .command_size = 3, - .data_size = 0, - .status = 0 - }, - .command = { 4, 3, 6 } -}; -#pragma pack() - -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - init_waitqueue_head(&rhb->wait); - rhb->stopped = 0; -} - -/* - * start_reverse_heartbeat - * Loop forever, sending a reverse heartbeat dot command to the service - * processor, then sleeping. The loop comes to an end if the service - * processor fails to respond 3 times or we were interrupted. - */ -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - struct command *cmd; - int times_failed = 0; - int result = 1; - - cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd); - if (!cmd) - return -ENOMEM; - - while (times_failed < 3) { - memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd); - cmd->status = IBMASM_CMD_PENDING; - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - times_failed++; - - wait_event_interruptible_timeout(rhb->wait, - rhb->stopped, - REVERSE_HEARTBEAT_TIMEOUT * HZ); - - if (signal_pending(current) || rhb->stopped) { - result = -EINTR; - break; - } - } - command_put(cmd); - rhb->stopped = 0; - - return result; -} - -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb) -{ - rhb->stopped = 1; - wake_up_interruptible(&rhb->wait); -} diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c deleted file mode 100644 index 521531738c9a..000000000000 --- a/drivers/misc/ibmasm/remote.c +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Authors: Max Asböck - * Vernon Mauery - */ - -/* Remote mouse and keyboard event handling functions */ - -#include -#include "ibmasm.h" -#include "remote.h" - -#define MOUSE_X_MAX 1600 -#define MOUSE_Y_MAX 1200 - -static const unsigned short xlate_high[XLATE_SIZE] = { - [KEY_SYM_ENTER & 0xff] = KEY_ENTER, - [KEY_SYM_KPSLASH & 0xff] = KEY_KPSLASH, - [KEY_SYM_KPSTAR & 0xff] = KEY_KPASTERISK, - [KEY_SYM_KPMINUS & 0xff] = KEY_KPMINUS, - [KEY_SYM_KPDOT & 0xff] = KEY_KPDOT, - [KEY_SYM_KPPLUS & 0xff] = KEY_KPPLUS, - [KEY_SYM_KP0 & 0xff] = KEY_KP0, - [KEY_SYM_KP1 & 0xff] = KEY_KP1, - [KEY_SYM_KP2 & 0xff] = KEY_KP2, [KEY_SYM_KPDOWN & 0xff] = KEY_KP2, - [KEY_SYM_KP3 & 0xff] = KEY_KP3, - [KEY_SYM_KP4 & 0xff] = KEY_KP4, [KEY_SYM_KPLEFT & 0xff] = KEY_KP4, - [KEY_SYM_KP5 & 0xff] = KEY_KP5, - [KEY_SYM_KP6 & 0xff] = KEY_KP6, [KEY_SYM_KPRIGHT & 0xff] = KEY_KP6, - [KEY_SYM_KP7 & 0xff] = KEY_KP7, - [KEY_SYM_KP8 & 0xff] = KEY_KP8, [KEY_SYM_KPUP & 0xff] = KEY_KP8, - [KEY_SYM_KP9 & 0xff] = KEY_KP9, - [KEY_SYM_BK_SPC & 0xff] = KEY_BACKSPACE, - [KEY_SYM_TAB & 0xff] = KEY_TAB, - [KEY_SYM_CTRL & 0xff] = KEY_LEFTCTRL, - [KEY_SYM_ALT & 0xff] = KEY_LEFTALT, - [KEY_SYM_INSERT & 0xff] = KEY_INSERT, - [KEY_SYM_DELETE & 0xff] = KEY_DELETE, - [KEY_SYM_SHIFT & 0xff] = KEY_LEFTSHIFT, - [KEY_SYM_UARROW & 0xff] = KEY_UP, - [KEY_SYM_DARROW & 0xff] = KEY_DOWN, - [KEY_SYM_LARROW & 0xff] = KEY_LEFT, - [KEY_SYM_RARROW & 0xff] = KEY_RIGHT, - [KEY_SYM_ESCAPE & 0xff] = KEY_ESC, - [KEY_SYM_PAGEUP & 0xff] = KEY_PAGEUP, - [KEY_SYM_PAGEDOWN & 0xff] = KEY_PAGEDOWN, - [KEY_SYM_HOME & 0xff] = KEY_HOME, - [KEY_SYM_END & 0xff] = KEY_END, - [KEY_SYM_F1 & 0xff] = KEY_F1, - [KEY_SYM_F2 & 0xff] = KEY_F2, - [KEY_SYM_F3 & 0xff] = KEY_F3, - [KEY_SYM_F4 & 0xff] = KEY_F4, - [KEY_SYM_F5 & 0xff] = KEY_F5, - [KEY_SYM_F6 & 0xff] = KEY_F6, - [KEY_SYM_F7 & 0xff] = KEY_F7, - [KEY_SYM_F8 & 0xff] = KEY_F8, - [KEY_SYM_F9 & 0xff] = KEY_F9, - [KEY_SYM_F10 & 0xff] = KEY_F10, - [KEY_SYM_F11 & 0xff] = KEY_F11, - [KEY_SYM_F12 & 0xff] = KEY_F12, - [KEY_SYM_CAP_LOCK & 0xff] = KEY_CAPSLOCK, - [KEY_SYM_NUM_LOCK & 0xff] = KEY_NUMLOCK, - [KEY_SYM_SCR_LOCK & 0xff] = KEY_SCROLLLOCK, -}; - -static const unsigned short xlate[XLATE_SIZE] = { - [NO_KEYCODE] = KEY_RESERVED, - [KEY_SYM_SPACE] = KEY_SPACE, - [KEY_SYM_TILDE] = KEY_GRAVE, [KEY_SYM_BKTIC] = KEY_GRAVE, - [KEY_SYM_ONE] = KEY_1, [KEY_SYM_BANG] = KEY_1, - [KEY_SYM_TWO] = KEY_2, [KEY_SYM_AT] = KEY_2, - [KEY_SYM_THREE] = KEY_3, [KEY_SYM_POUND] = KEY_3, - [KEY_SYM_FOUR] = KEY_4, [KEY_SYM_DOLLAR] = KEY_4, - [KEY_SYM_FIVE] = KEY_5, [KEY_SYM_PERCENT] = KEY_5, - [KEY_SYM_SIX] = KEY_6, [KEY_SYM_CARAT] = KEY_6, - [KEY_SYM_SEVEN] = KEY_7, [KEY_SYM_AMPER] = KEY_7, - [KEY_SYM_EIGHT] = KEY_8, [KEY_SYM_STAR] = KEY_8, - [KEY_SYM_NINE] = KEY_9, [KEY_SYM_LPAREN] = KEY_9, - [KEY_SYM_ZERO] = KEY_0, [KEY_SYM_RPAREN] = KEY_0, - [KEY_SYM_MINUS] = KEY_MINUS, [KEY_SYM_USCORE] = KEY_MINUS, - [KEY_SYM_EQUAL] = KEY_EQUAL, [KEY_SYM_PLUS] = KEY_EQUAL, - [KEY_SYM_LBRKT] = KEY_LEFTBRACE, [KEY_SYM_LCURLY] = KEY_LEFTBRACE, - [KEY_SYM_RBRKT] = KEY_RIGHTBRACE, [KEY_SYM_RCURLY] = KEY_RIGHTBRACE, - [KEY_SYM_SLASH] = KEY_BACKSLASH, [KEY_SYM_PIPE] = KEY_BACKSLASH, - [KEY_SYM_TIC] = KEY_APOSTROPHE, [KEY_SYM_QUOTE] = KEY_APOSTROPHE, - [KEY_SYM_SEMIC] = KEY_SEMICOLON, [KEY_SYM_COLON] = KEY_SEMICOLON, - [KEY_SYM_COMMA] = KEY_COMMA, [KEY_SYM_LT] = KEY_COMMA, - [KEY_SYM_PERIOD] = KEY_DOT, [KEY_SYM_GT] = KEY_DOT, - [KEY_SYM_BSLASH] = KEY_SLASH, [KEY_SYM_QMARK] = KEY_SLASH, - [KEY_SYM_A] = KEY_A, [KEY_SYM_a] = KEY_A, - [KEY_SYM_B] = KEY_B, [KEY_SYM_b] = KEY_B, - [KEY_SYM_C] = KEY_C, [KEY_SYM_c] = KEY_C, - [KEY_SYM_D] = KEY_D, [KEY_SYM_d] = KEY_D, - [KEY_SYM_E] = KEY_E, [KEY_SYM_e] = KEY_E, - [KEY_SYM_F] = KEY_F, [KEY_SYM_f] = KEY_F, - [KEY_SYM_G] = KEY_G, [KEY_SYM_g] = KEY_G, - [KEY_SYM_H] = KEY_H, [KEY_SYM_h] = KEY_H, - [KEY_SYM_I] = KEY_I, [KEY_SYM_i] = KEY_I, - [KEY_SYM_J] = KEY_J, [KEY_SYM_j] = KEY_J, - [KEY_SYM_K] = KEY_K, [KEY_SYM_k] = KEY_K, - [KEY_SYM_L] = KEY_L, [KEY_SYM_l] = KEY_L, - [KEY_SYM_M] = KEY_M, [KEY_SYM_m] = KEY_M, - [KEY_SYM_N] = KEY_N, [KEY_SYM_n] = KEY_N, - [KEY_SYM_O] = KEY_O, [KEY_SYM_o] = KEY_O, - [KEY_SYM_P] = KEY_P, [KEY_SYM_p] = KEY_P, - [KEY_SYM_Q] = KEY_Q, [KEY_SYM_q] = KEY_Q, - [KEY_SYM_R] = KEY_R, [KEY_SYM_r] = KEY_R, - [KEY_SYM_S] = KEY_S, [KEY_SYM_s] = KEY_S, - [KEY_SYM_T] = KEY_T, [KEY_SYM_t] = KEY_T, - [KEY_SYM_U] = KEY_U, [KEY_SYM_u] = KEY_U, - [KEY_SYM_V] = KEY_V, [KEY_SYM_v] = KEY_V, - [KEY_SYM_W] = KEY_W, [KEY_SYM_w] = KEY_W, - [KEY_SYM_X] = KEY_X, [KEY_SYM_x] = KEY_X, - [KEY_SYM_Y] = KEY_Y, [KEY_SYM_y] = KEY_Y, - [KEY_SYM_Z] = KEY_Z, [KEY_SYM_z] = KEY_Z, -}; - -static void print_input(struct remote_input *input) -{ - if (input->type == INPUT_TYPE_MOUSE) { - unsigned char buttons = input->mouse_buttons; - dbg("remote mouse movement: (x,y)=(%d,%d)%s%s%s%s\n", - input->data.mouse.x, input->data.mouse.y, - (buttons) ? " -- buttons:" : "", - (buttons & REMOTE_BUTTON_LEFT) ? "left " : "", - (buttons & REMOTE_BUTTON_MIDDLE) ? "middle " : "", - (buttons & REMOTE_BUTTON_RIGHT) ? "right" : "" - ); - } else { - dbg("remote keypress (code, flag, down):" - "%d (0x%x) [0x%x] [0x%x]\n", - input->data.keyboard.key_code, - input->data.keyboard.key_code, - input->data.keyboard.key_flag, - input->data.keyboard.key_down - ); - } -} - -static void send_mouse_event(struct input_dev *dev, struct remote_input *input) -{ - unsigned char buttons = input->mouse_buttons; - - input_report_abs(dev, ABS_X, input->data.mouse.x); - input_report_abs(dev, ABS_Y, input->data.mouse.y); - input_report_key(dev, BTN_LEFT, buttons & REMOTE_BUTTON_LEFT); - input_report_key(dev, BTN_MIDDLE, buttons & REMOTE_BUTTON_MIDDLE); - input_report_key(dev, BTN_RIGHT, buttons & REMOTE_BUTTON_RIGHT); - input_sync(dev); -} - -static void send_keyboard_event(struct input_dev *dev, - struct remote_input *input) -{ - unsigned int key; - unsigned short code = input->data.keyboard.key_code; - - if (code & 0xff00) - key = xlate_high[code & 0xff]; - else - key = xlate[code]; - input_report_key(dev, key, input->data.keyboard.key_down); - input_sync(dev); -} - -void ibmasm_handle_mouse_interrupt(struct service_processor *sp) -{ - unsigned long reader; - unsigned long writer; - struct remote_input input; - - reader = get_queue_reader(sp); - writer = get_queue_writer(sp); - - while (reader != writer) { - if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) { - set_queue_reader(sp, 0); - break; - } - - memcpy_fromio(&input, get_queue_entry(sp, reader), - sizeof(struct remote_input)); - - print_input(&input); - if (input.type == INPUT_TYPE_MOUSE) { - send_mouse_event(sp->remote.mouse_dev, &input); - } else if (input.type == INPUT_TYPE_KEYBOARD) { - send_keyboard_event(sp->remote.keybd_dev, &input); - } else - break; - - reader = advance_queue_reader(sp, reader); - writer = get_queue_writer(sp); - } -} - -int ibmasm_init_remote_input_dev(struct service_processor *sp) -{ - /* set up the mouse input device */ - struct input_dev *mouse_dev, *keybd_dev; - struct pci_dev *pdev = to_pci_dev(sp->dev); - int error = -ENOMEM; - int i; - - sp->remote.mouse_dev = mouse_dev = input_allocate_device(); - sp->remote.keybd_dev = keybd_dev = input_allocate_device(); - - if (!mouse_dev || !keybd_dev) - goto err_free_devices; - - mouse_dev->id.bustype = BUS_PCI; - mouse_dev->id.vendor = pdev->vendor; - mouse_dev->id.product = pdev->device; - mouse_dev->id.version = 1; - mouse_dev->dev.parent = sp->dev; - mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - set_bit(BTN_TOUCH, mouse_dev->keybit); - mouse_dev->name = "ibmasm RSA I remote mouse"; - input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0); - input_set_abs_params(mouse_dev, ABS_Y, 0, MOUSE_Y_MAX, 0, 0); - - keybd_dev->id.bustype = BUS_PCI; - keybd_dev->id.vendor = pdev->vendor; - keybd_dev->id.product = pdev->device; - keybd_dev->id.version = 2; - keybd_dev->dev.parent = sp->dev; - keybd_dev->evbit[0] = BIT_MASK(EV_KEY); - keybd_dev->name = "ibmasm RSA I remote keyboard"; - - for (i = 0; i < XLATE_SIZE; i++) { - if (xlate_high[i]) - set_bit(xlate_high[i], keybd_dev->keybit); - if (xlate[i]) - set_bit(xlate[i], keybd_dev->keybit); - } - - error = input_register_device(mouse_dev); - if (error) - goto err_free_devices; - - error = input_register_device(keybd_dev); - if (error) - goto err_unregister_mouse_dev; - - enable_mouse_interrupts(sp); - - printk(KERN_INFO "ibmasm remote responding to events on RSA card %d\n", sp->number); - - return 0; - - err_unregister_mouse_dev: - input_unregister_device(mouse_dev); - mouse_dev = NULL; /* so we don't try to free it again below */ - err_free_devices: - input_free_device(mouse_dev); - input_free_device(keybd_dev); - - return error; -} - -void ibmasm_free_remote_input_dev(struct service_processor *sp) -{ - disable_mouse_interrupts(sp); - input_unregister_device(sp->remote.mouse_dev); - input_unregister_device(sp->remote.keybd_dev); -} - diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h deleted file mode 100644 index ec4e78ec5a67..000000000000 --- a/drivers/misc/ibmasm/remote.h +++ /dev/null @@ -1,257 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - * - * Originally written by Pete Reynolds - */ - -#ifndef _IBMASM_REMOTE_H_ -#define _IBMASM_REMOTE_H_ - -#include - -/* pci offsets */ -#define CONDOR_MOUSE_DATA 0x000AC000 -#define CONDOR_MOUSE_ISR_CONTROL 0x00 -#define CONDOR_MOUSE_ISR_STATUS 0x04 -#define CONDOR_MOUSE_Q_READER 0x08 -#define CONDOR_MOUSE_Q_WRITER 0x0C -#define CONDOR_MOUSE_Q_BEGIN 0x10 -#define CONDOR_MOUSE_MAX_X 0x14 -#define CONDOR_MOUSE_MAX_Y 0x18 - -#define CONDOR_INPUT_DESKTOP_INFO 0x1F0 -#define CONDOR_INPUT_DISPLAY_RESX 0x1F4 -#define CONDOR_INPUT_DISPLAY_RESY 0x1F8 -#define CONDOR_INPUT_DISPLAY_BITS 0x1FC -#define CONDOR_OUTPUT_VNC_STATUS 0x200 - -#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001 - -#define INPUT_TYPE_MOUSE 0x1 -#define INPUT_TYPE_KEYBOARD 0x2 - - -/* mouse button states received from SP */ -#define REMOTE_DOUBLE_CLICK 0xF0 -#define REMOTE_BUTTON_LEFT 0x01 -#define REMOTE_BUTTON_MIDDLE 0x02 -#define REMOTE_BUTTON_RIGHT 0x04 - -/* size of keysym/keycode translation matrices */ -#define XLATE_SIZE 256 - -struct mouse_input { - unsigned short y; - unsigned short x; -}; - - -struct keyboard_input { - unsigned short key_code; - unsigned char key_flag; - unsigned char key_down; -}; - - - -struct remote_input { - union { - struct mouse_input mouse; - struct keyboard_input keyboard; - } data; - - unsigned char type; - unsigned char pad1; - unsigned char mouse_buttons; - unsigned char pad3; -}; - -#define mouse_addr(sp) (sp->base_address + CONDOR_MOUSE_DATA) -#define display_width(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX) -#define display_height(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY) -#define display_depth(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS) -#define desktop_info(sp) (mouse_addr(sp) + CONDOR_INPUT_DESKTOP_INFO) -#define vnc_status(sp) (mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS) -#define isr_control(sp) (mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) -#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -/* remote input queue operations */ -#define REMOTE_QUEUE_SIZE 60 - -#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER) -#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER) -#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER) - -#define queue_begin (mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN) - -#define get_queue_entry(sp, read_index) \ - ((void*)(queue_begin + read_index * sizeof(struct remote_input))) - -static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader) -{ - reader++; - if (reader == REMOTE_QUEUE_SIZE) - reader = 0; - - set_queue_reader(sp, reader); - return reader; -} - -#define NO_KEYCODE 0 -#define KEY_SYM_BK_SPC 0xFF08 -#define KEY_SYM_TAB 0xFF09 -#define KEY_SYM_ENTER 0xFF0D -#define KEY_SYM_SCR_LOCK 0xFF14 -#define KEY_SYM_ESCAPE 0xFF1B -#define KEY_SYM_HOME 0xFF50 -#define KEY_SYM_LARROW 0xFF51 -#define KEY_SYM_UARROW 0xFF52 -#define KEY_SYM_RARROW 0xFF53 -#define KEY_SYM_DARROW 0xFF54 -#define KEY_SYM_PAGEUP 0xFF55 -#define KEY_SYM_PAGEDOWN 0xFF56 -#define KEY_SYM_END 0xFF57 -#define KEY_SYM_INSERT 0xFF63 -#define KEY_SYM_NUM_LOCK 0xFF7F -#define KEY_SYM_KPSTAR 0xFFAA -#define KEY_SYM_KPPLUS 0xFFAB -#define KEY_SYM_KPMINUS 0xFFAD -#define KEY_SYM_KPDOT 0xFFAE -#define KEY_SYM_KPSLASH 0xFFAF -#define KEY_SYM_KPRIGHT 0xFF96 -#define KEY_SYM_KPUP 0xFF97 -#define KEY_SYM_KPLEFT 0xFF98 -#define KEY_SYM_KPDOWN 0xFF99 -#define KEY_SYM_KP0 0xFFB0 -#define KEY_SYM_KP1 0xFFB1 -#define KEY_SYM_KP2 0xFFB2 -#define KEY_SYM_KP3 0xFFB3 -#define KEY_SYM_KP4 0xFFB4 -#define KEY_SYM_KP5 0xFFB5 -#define KEY_SYM_KP6 0xFFB6 -#define KEY_SYM_KP7 0xFFB7 -#define KEY_SYM_KP8 0xFFB8 -#define KEY_SYM_KP9 0xFFB9 -#define KEY_SYM_F1 0xFFBE // 1B 5B 5B 41 -#define KEY_SYM_F2 0xFFBF // 1B 5B 5B 42 -#define KEY_SYM_F3 0xFFC0 // 1B 5B 5B 43 -#define KEY_SYM_F4 0xFFC1 // 1B 5B 5B 44 -#define KEY_SYM_F5 0xFFC2 // 1B 5B 5B 45 -#define KEY_SYM_F6 0xFFC3 // 1B 5B 31 37 7E -#define KEY_SYM_F7 0xFFC4 // 1B 5B 31 38 7E -#define KEY_SYM_F8 0xFFC5 // 1B 5B 31 39 7E -#define KEY_SYM_F9 0xFFC6 // 1B 5B 32 30 7E -#define KEY_SYM_F10 0xFFC7 // 1B 5B 32 31 7E -#define KEY_SYM_F11 0xFFC8 // 1B 5B 32 33 7E -#define KEY_SYM_F12 0xFFC9 // 1B 5B 32 34 7E -#define KEY_SYM_SHIFT 0xFFE1 -#define KEY_SYM_CTRL 0xFFE3 -#define KEY_SYM_ALT 0xFFE9 -#define KEY_SYM_CAP_LOCK 0xFFE5 -#define KEY_SYM_DELETE 0xFFFF -#define KEY_SYM_TILDE 0x60 -#define KEY_SYM_BKTIC 0x7E -#define KEY_SYM_ONE 0x31 -#define KEY_SYM_BANG 0x21 -#define KEY_SYM_TWO 0x32 -#define KEY_SYM_AT 0x40 -#define KEY_SYM_THREE 0x33 -#define KEY_SYM_POUND 0x23 -#define KEY_SYM_FOUR 0x34 -#define KEY_SYM_DOLLAR 0x24 -#define KEY_SYM_FIVE 0x35 -#define KEY_SYM_PERCENT 0x25 -#define KEY_SYM_SIX 0x36 -#define KEY_SYM_CARAT 0x5E -#define KEY_SYM_SEVEN 0x37 -#define KEY_SYM_AMPER 0x26 -#define KEY_SYM_EIGHT 0x38 -#define KEY_SYM_STAR 0x2A -#define KEY_SYM_NINE 0x39 -#define KEY_SYM_LPAREN 0x28 -#define KEY_SYM_ZERO 0x30 -#define KEY_SYM_RPAREN 0x29 -#define KEY_SYM_MINUS 0x2D -#define KEY_SYM_USCORE 0x5F -#define KEY_SYM_EQUAL 0x2B -#define KEY_SYM_PLUS 0x3D -#define KEY_SYM_LBRKT 0x5B -#define KEY_SYM_LCURLY 0x7B -#define KEY_SYM_RBRKT 0x5D -#define KEY_SYM_RCURLY 0x7D -#define KEY_SYM_SLASH 0x5C -#define KEY_SYM_PIPE 0x7C -#define KEY_SYM_TIC 0x27 -#define KEY_SYM_QUOTE 0x22 -#define KEY_SYM_SEMIC 0x3B -#define KEY_SYM_COLON 0x3A -#define KEY_SYM_COMMA 0x2C -#define KEY_SYM_LT 0x3C -#define KEY_SYM_PERIOD 0x2E -#define KEY_SYM_GT 0x3E -#define KEY_SYM_BSLASH 0x2F -#define KEY_SYM_QMARK 0x3F -#define KEY_SYM_A 0x41 -#define KEY_SYM_B 0x42 -#define KEY_SYM_C 0x43 -#define KEY_SYM_D 0x44 -#define KEY_SYM_E 0x45 -#define KEY_SYM_F 0x46 -#define KEY_SYM_G 0x47 -#define KEY_SYM_H 0x48 -#define KEY_SYM_I 0x49 -#define KEY_SYM_J 0x4A -#define KEY_SYM_K 0x4B -#define KEY_SYM_L 0x4C -#define KEY_SYM_M 0x4D -#define KEY_SYM_N 0x4E -#define KEY_SYM_O 0x4F -#define KEY_SYM_P 0x50 -#define KEY_SYM_Q 0x51 -#define KEY_SYM_R 0x52 -#define KEY_SYM_S 0x53 -#define KEY_SYM_T 0x54 -#define KEY_SYM_U 0x55 -#define KEY_SYM_V 0x56 -#define KEY_SYM_W 0x57 -#define KEY_SYM_X 0x58 -#define KEY_SYM_Y 0x59 -#define KEY_SYM_Z 0x5A -#define KEY_SYM_a 0x61 -#define KEY_SYM_b 0x62 -#define KEY_SYM_c 0x63 -#define KEY_SYM_d 0x64 -#define KEY_SYM_e 0x65 -#define KEY_SYM_f 0x66 -#define KEY_SYM_g 0x67 -#define KEY_SYM_h 0x68 -#define KEY_SYM_i 0x69 -#define KEY_SYM_j 0x6A -#define KEY_SYM_k 0x6B -#define KEY_SYM_l 0x6C -#define KEY_SYM_m 0x6D -#define KEY_SYM_n 0x6E -#define KEY_SYM_o 0x6F -#define KEY_SYM_p 0x70 -#define KEY_SYM_q 0x71 -#define KEY_SYM_r 0x72 -#define KEY_SYM_s 0x73 -#define KEY_SYM_t 0x74 -#define KEY_SYM_u 0x75 -#define KEY_SYM_v 0x76 -#define KEY_SYM_w 0x77 -#define KEY_SYM_x 0x78 -#define KEY_SYM_y 0x79 -#define KEY_SYM_z 0x7A -#define KEY_SYM_SPACE 0x20 -#endif /* _IBMASM_REMOTE_H_ */ diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c deleted file mode 100644 index a5d4c8e010ee..000000000000 --- a/drivers/misc/ibmasm/uart.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - - -void ibmasm_register_uart(struct service_processor *sp) -{ - struct uart_8250_port uart; - void __iomem *iomem_base; - - iomem_base = sp->base_address + SCOUT_COM_B_BASE; - - /* read the uart scratch register to determine if the UART - * is dedicated to the service processor or if the OS can use it - */ - if (0 == readl(iomem_base + UART_SCR)) { - dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n"); - sp->serial_line = -1; - return; - } - - memset(&uart, 0, sizeof(uart)); - uart.port.irq = sp->irq; - uart.port.uartclk = 3686400; - uart.port.flags = UPF_SHARE_IRQ; - uart.port.iotype = UPIO_MEM; - uart.port.membase = iomem_base; - - sp->serial_line = serial8250_register_8250_port(&uart); - if (sp->serial_line < 0) { - dev_err(sp->dev, "Failed to register serial port\n"); - return; - } - enable_uart_interrupts(sp->base_address); -} - -void ibmasm_unregister_uart(struct service_processor *sp) -{ - if (sp->serial_line < 0) - return; - - disable_uart_interrupts(sp->base_address); - serial8250_unregister_port(sp->serial_line); -} diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index beb18c34f20d..da82b1edb7c9 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -1040,7 +1040,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct inode *inode; - struct ibmvmc_buffer *vmc_buffer; + struct ibmvmc_buffer *vmc_buffer = NULL; struct ibmvmc_file_session *session; struct crq_server_adapter *adapter; struct ibmvmc_hmc *hmc; @@ -1130,9 +1130,15 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", (unsigned long)file, (unsigned long)count); - ibmvmc_send_msg(adapter, vmc_buffer, hmc, count); + if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) { + ret = -EIO; + goto out; + } + vmc_buffer = NULL; ret = p - buffer; out: + if (vmc_buffer) + vmc_buffer->free = 1; spin_unlock_irqrestore(&hmc->lock, flags); return (ssize_t)(ret); } @@ -1653,6 +1659,13 @@ static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, return -1; } + if (msg_len > buffer->size) { + dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n", + msg_len, buffer->size); + spin_unlock_irqrestore(&hmc->lock, flags); + return -1; + } + /* RDMA the data into the partition. */ rc = h_copy_rdma(msg_len, adapter->riobn, @@ -2298,8 +2311,8 @@ static void ibmvmc_remove(struct vio_dev *vdev) } static struct vio_device_id ibmvmc_device_table[] = { - { "ibm,vmc", "IBM,vmc" }, - { "", "" } + { .type = "ibm,vmc", .compat = "IBM,vmc" }, + { } }; MODULE_DEVICE_TABLE(vio, ibmvmc_device_table); diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 015710762a65..e8bcf0df2da8 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client, static void ics932s401_remove(struct i2c_client *client); static const struct i2c_device_id ics932s401_id[] = { - { "ics932s401" }, + { .name = "ics932s401" }, { } }; MODULE_DEVICE_TABLE(i2c, ics932s401_id); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 95480e16ae5f..c9b526a212d0 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -449,8 +449,8 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id isl29003_id[] = { - { "isl29003" }, - {} + { .name = "isl29003" }, + { } }; MODULE_DEVICE_TABLE(i2c, isl29003_id); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index c288aeec16c0..dd966db28ec4 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client) } static const struct i2c_device_id isl29020_id[] = { - { "isl29020" }, + { .name = "isl29020" }, { } }; diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 index 000000000000..12a3fd809969 --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +config INTEL_SSEI + tristate "Intel Silicon Security Engine Interface" + help + The ISSEI (Intel Silicon Security Engine Interface) + subsystem provides a communication channel between the host and the + Silicon Security Engine. + Enable this driver to get SPDM and other features on Intel client CPUs + released in 2024 (Lunar Lake) or later. + + If selected, the /dev/isseiX device will be created. + If in doubt, select N. + +if INTEL_SSEI + +config INTEL_SSEI_HW_HECI + tristate "Intel Silicon Security Engine Interface Hardware" + depends on X86 && PCI + help + HECI interface of communication channel between + the host and the Silicon Security Engine. + + Implementation of ISSEI communication channel over + the HECI hardware PCI device. + This device is available on Intel client CPUs released in 2024 + (Lunar Lake) or later. + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 index 000000000000..8fac360c3342 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' + +obj-$(CONFIG_INTEL_SSEI) += issei.o +issei-objs += cdev.o +issei-objs += dma.o +issei-objs += fw_client.o +issei-objs += host_client.o +issei-objs += ham.o +issei-objs += main.o + +obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o +issei-heci-objs := pci_heci.o +issei-heci-objs += hw_heci.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 index 000000000000..b7d65e2d0813 --- /dev/null +++ b/drivers/misc/issei/cdev.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "host_client.h" +#include "cdev.h" + +struct class *issei_class; +static dev_t issei_devt; + +#define ISSEI_MAX_DEVS MINORMASK + +static DEFINE_XARRAY_ALLOC(issei_minor_xa); + +static int issei_open(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl; + struct issei_device *idev; + + xa_lock(&issei_minor_xa); + idev = xa_load(&issei_minor_xa, iminor(inode)); + if (idev) + get_device(&idev->dev); + xa_unlock(&issei_minor_xa); + if (!idev) + return -ENODEV; + + cl = issei_cl_create(idev, fp); + if (IS_ERR(cl)) { + put_device(&idev->dev); + return PTR_ERR(cl); + } + fp->private_data = cl; + + return nonseekable_open(inode, fp); +} + +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl = fp->private_data; + struct issei_device *idev = cl->idev; + + issei_cl_remove(cl); + put_device(&idev->dev); + + return 0; +} + +static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data) +{ + struct issei_host_client *cl = file->private_data; + struct issei_connect_client_data conn; + struct issei_device *idev = cl->idev; + int ret; + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + + ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, + &conn.out_client_properties.max_msg_length, + &conn.out_client_properties.protocol_version, + &conn.out_client_properties.flags); + if (ret) + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + issei_cl_disconnect(cl); + return -EFAULT; + } + return 0; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + return issei_cl_disconnect(cl); + + default: + return -ENOIOCTLCMD; + } +} + +static ssize_t issei_write(struct file *file, const char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { + dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + u8 *buf __free(kfree) = memdup_user(ubuf, length); + if (IS_ERR(buf)) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + + do { + ret = issei_cl_write(cl, buf, length); + if (ret < 0 && ret != -EAGAIN) + return ret; + /* buf is consumed by issei_cl_write on success */ + if (ret >= 0) + retain_and_null_ptr(buf); + if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) { + issei_cl_clean_all_wbuf(cl); + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + } while (ret == -EAGAIN); + + return ret; +} + +static ssize_t issei_read(struct file *file, char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + u8 *data = NULL; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { + dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) { + if (ret != -ENOENT) + return ret; + + if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) + return ret; + } + + if (copy_to_user(ubuf, data, ret)) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret = -EFAULT; + } else { + *offset = 0; + } + + kfree(data); + + return ret; +} + +static __poll_t issei_poll(struct file *file, poll_table *wait) +{ + __poll_t req_events = poll_requested_events(wait); + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + __poll_t mask = 0; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + + if (req_events & (EPOLLIN | EPOLLRDNORM)) { + poll_wait(file, &cl->read_wait, wait); + ret = issei_cl_check_read(cl); + if (ret == 1) + mask |= EPOLLIN | EPOLLRDNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { + poll_wait(file, &cl->write_wait, wait); + ret = issei_cl_check_write(cl); + if (ret == 0) + mask |= EPOLLOUT | EPOLLWRNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + return mask; +} + +static ssize_t fw_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); +} +static DEVICE_ATTR_RO(fw_ver); + +static struct attribute *issei_attrs[] = { + &dev_attr_fw_ver.attr, + NULL +}; +ATTRIBUTE_GROUPS(issei); + +static const struct file_operations issei_fops = { + .owner = THIS_MODULE, + .open = issei_open, + .unlocked_ioctl = issei_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .write = issei_write, + .read = issei_read, + .release = issei_release, + .poll = issei_poll, +}; + +static void issei_device_release(struct device *dev) +{ + kfree(dev_get_drvdata(dev)); +} + +static void issei_device_init(struct issei_device *idev, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + idev->parent = parent; + idev->power_down = false; + init_waitqueue_head(&idev->wait_has_data); + idev->has_data = false; + init_waitqueue_head(&idev->wait_rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + + mutex_init(&idev->client_lock); + INIT_LIST_HEAD(&idev->host_client_list); + idev->host_client_last_id = 0; + idev->host_client_count = 0; + INIT_LIST_HEAD(&idev->fw_client_list); + INIT_LIST_HEAD(&idev->write_queue); + idev->last_write_ts = 0; + + idev->dma.length = *dma_length; + + idev->ops = ops; +} + +/** + * issei_register: register issei character device + * @hw_size: size of the hardware structure to allocate + * @parent: parent device + * @dma_length: structure with DMA sizes + * @ops: hardware-related operations + * + * Return: pointer allocated to issei_device structure, error on failure + */ +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + struct issei_device *idev; + u32 minor; + int ret, devno; + + idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + issei_device_init(idev, parent, dma_length, ops); + + ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); + if (ret < 0) { + dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); + kfree(idev); + return ERR_PTR(ret); + } + + idev->minor = minor; + devno = MKDEV(MAJOR(issei_devt), idev->minor); + + device_initialize(&idev->dev); + idev->dev.devt = devno; + idev->dev.class = issei_class; + idev->dev.parent = parent; + idev->dev.groups = issei_groups; + idev->dev.release = issei_device_release; + dev_set_drvdata(&idev->dev, idev); + + idev->cdev = cdev_alloc(); + if (!idev->cdev) { + ret = -ENOMEM; + goto err; + } + idev->cdev->ops = &issei_fops; + if (parent->driver) + idev->cdev->owner = parent->driver->owner; + cdev_set_parent(idev->cdev, &idev->dev.kobj); + + ret = cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = dev_set_name(&idev->dev, "issei%u", idev->minor); + if (ret) { + dev_err(parent, "unable to set name to device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = device_add(&idev->dev); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); + if (!idev->fw_clients) { + ret = -ENOMEM; + goto err_del_dev; + } + + return idev; + +err_del_dev: + device_del(&idev->dev); +err_del_cdev: + cdev_del(idev->cdev); +err: + put_device(&idev->dev); + xa_erase(&issei_minor_xa, minor); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(issei_register); + +/** + * issei_deregister: remove issei character device + * @idev: the device structure + */ +void issei_deregister(struct issei_device *idev) +{ + u32 minor = idev->minor; + + cdev_del(idev->cdev); + + kset_unregister(idev->fw_clients); + + device_del(&idev->dev); + + put_device(&idev->dev); + + xa_erase(&issei_minor_xa, minor); +} +EXPORT_SYMBOL_GPL(issei_deregister); + +static int __init issei_cdev_init(void) +{ + int ret; + + issei_class = class_create("issei"); + if (IS_ERR(issei_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(issei_class); + } + + ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); + if (ret < 0) { + pr_err("unable to allocate char dev region\n"); + class_destroy(issei_class); + return ret; + } + + return 0; +} + +static void __exit issei_cdev_exit(void) +{ + unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); + class_destroy(issei_class); +} + +module_init(issei_cdev_init); +module_exit(issei_cdev_exit); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 index 000000000000..30075a624f2d --- /dev/null +++ b/drivers/misc/issei/cdev.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_CDEV_H_ +#define _ISSEI_CDEV_H_ + +struct device; +struct issei_device; +struct issei_dma_length; +struct issei_hw_ops; + +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops); +void issei_deregister(struct issei_device *idev); + +#endif /* _ISSEI_CDEV_H_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 index 000000000000..457d28f31c06 --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "hw_msg.h" + +static inline size_t __issei_dma_size(const struct issei_dma *dma) +{ + return dma->length.h2f + dma->length.f2h + dma->length.ctl; +} + +/** + * issei_dmam_setup - setup DMA buffer and clean it + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_dmam_setup(struct issei_device *idev) +{ + struct issei_dma *dma = &idev->dma; + size_t size; + + size = __issei_dma_size(dma); + if (!size) + return -EINVAL; + + if (!dma->vaddr) + dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, + GFP_KERNEL | __GFP_ZERO); + if (dma->vaddr) + memset(dma->vaddr, 0, size); + return dma->vaddr ? 0 : -ENOMEM; +} + +static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma) +{ + return dma->vaddr + dma->length.h2f + dma->length.f2h; +} + +static bool __issei_dma_is_read_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->f2h_counter_wr != ctl->f2h_counter_rd; +} + +static bool __issei_dma_is_write_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->h2f_counter_wr != ctl->h2f_counter_rd; +} + +static void __issei_dma_read_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->f2h_counter_rd++; +} + +static void __issei_dma_write_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->h2f_counter_wr++; +} + +/** + * issei_dma_write - write data package to DMA + * @idev: issei device object + * @data: data atructure + * + * Return: 0 on success, <0 on failures + */ +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data) +{ + u8 *write_buf = idev->dma.vaddr; + struct ham_message_header *hdr = (struct ham_message_header *)write_buf; + + if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { + dev_err(&idev->dev, "Message is too big\n"); + return -EMSGSIZE; + } + + if (__issei_dma_is_write_busy(&idev->dma)) { + if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { + dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } + dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + + hdr->length = data->length; + hdr->fw_id = data->fw_id; + hdr->host_id = data->host_id; + hdr->flags = data->flags; + hdr->status = data->status; + hdr->reserved = 0; + + memcpy(write_buf + sizeof(*hdr), data->buf, data->length); + + __issei_dma_write_finalize(idev); + idev->last_write_ts = ktime_get(); + return 0; +} + +/** + * issei_dma_read - read data package from DMA + * @idev: issei device object + * @data: data atructure + * + * Return: %0 on success, <0 on failures + */ +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) +{ + u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f; + struct ham_message_header *hdr = (struct ham_message_header *)read_buf; + + if (!__issei_dma_is_read_busy(&idev->dma)) { + dev_dbg(&idev->dev, "Nothing to read\n"); + return -ENODATA; + } + + dev_dbg(&idev->dev, "Reading header\n"); + data->length = hdr->length; + data->fw_id = hdr->fw_id; + data->host_id = hdr->host_id; + data->flags = hdr->flags; + data->status = hdr->status; + + if (data->length > idev->dma.length.f2h - sizeof(*hdr)) { + dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n", + data->length, idev->dma.length.f2h - sizeof(*hdr)); + return -EIO; + } + + dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); + data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; + __issei_dma_read_finalize(idev); + return 0; +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 index 000000000000..e6b7aeb50ae6 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DMA_H_ +#define _ISSEI_DMA_H_ + +#include + +struct issei_device; + +/** + * struct issei_dma_length - sizes of DMA memory portions + * @h2f: host to firmware buffer size + * @f2h: firmware to host buffer size + * @ctl: control buffer size + */ +struct issei_dma_length { + size_t h2f; + size_t f2h; + size_t ctl; +}; + +/** + * struct issei_dma - DMA memory structure + * @vaddr: virtual address + * @daddr: physical address + * @length: memory sizes structure + */ +struct issei_dma { + void *vaddr; + dma_addr_t daddr; + struct issei_dma_length length; +}; + +/* Operation statuses */ +#define HAMS_SUCCESS 0x00 +#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01 +#define HAMS_DEPRECATED_BUS_MSG 0x02 +#define HAMS_CLIENT_NOT_EXISTS 0x03 +#define HAMS_MSG_TOO_BIG 0x04 +#define HAMS_MSG_NOT_CONSUMED 0x05 +#define HAMS_CORRUPTED_BUS_MSG 0x06 +#define HAMS_CORRUPTED_HEADER 0x07 +#define HAMS_INVALID_LENGTH 0x08 +#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09 +#define HAMS_GENERAL_FATAL_ERROR 0xff + +/** + * struct issei_dma_data - data passed through channel + * @fw_id: firmware client id + * @host_id: host client id + * @flags: flags bitmap + * @status: operation status + * @length: data length + * @buf: pointer to data buffer + */ +struct issei_dma_data { + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 length; + void *buf; +}; + +int issei_dmam_setup(struct issei_device *idev); +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data); +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data); + +#endif /*_ISSEI_DMA_H_*/ diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 index 000000000000..b8e48dbf1c9c --- /dev/null +++ b/drivers/misc/issei/fw_client.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "fw_client.h" + +/* + * Specific attribute handlers for fw_clients kset. + * Provide only show function as all fw_client attributes are read-only. + */ + +struct issei_fw_cl_attr { + struct attribute attr; + ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr, + char *buf); +}; +#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr) + +static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + const struct issei_fw_cl_attr *issei_attr; + struct issei_fw_client *fw_cl; + + issei_attr = to_issei_fw_cl_attr(attr); + fw_cl = to_issei_fw_client(kobj); + + if (!issei_attr->show) + return -EIO; + + return issei_attr->show(fw_cl, issei_attr, buf); +} + +static const struct sysfs_ops fw_cl_sysfs_ops = { + .show = fw_cl_attr_show, +}; + +#define FW_CL_ATTR_RO(_name) \ + struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name) + +/* fw_client attributes */ + +static ssize_t id_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->id); +} +static FW_CL_ATTR_RO(id); + +static ssize_t ver_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->ver); +} +static FW_CL_ATTR_RO(ver); + +static ssize_t uuid_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid); +} +static FW_CL_ATTR_RO(uuid); + +static ssize_t mtu_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->mtu); +} +static FW_CL_ATTR_RO(mtu); + +static const struct attribute *const fw_cl_attrs[] = { + &fw_cl_attr_id.attr, + &fw_cl_attr_ver.attr, + &fw_cl_attr_uuid.attr, + &fw_cl_attr_mtu.attr, + NULL, +}; + +static const struct attribute_group fw_cl_group = { + .attrs_const = fw_cl_attrs, +}; +__ATTRIBUTE_GROUPS(fw_cl); + +static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid, + u32 mtu, u32 flags) +{ + INIT_LIST_HEAD(&fw_cl->list); + fw_cl->id = id; + fw_cl->ver = ver; + fw_cl->uuid = *uuid; + fw_cl->mtu = mtu; + fw_cl->flags = flags; +} + +static void fw_cl_release(struct kobject *kobj) +{ + struct issei_fw_client *fw_cl = to_issei_fw_client(kobj); + + kfree(fw_cl); +} + +static const struct kobj_type fw_client_ktype = { + .sysfs_ops = &fw_cl_sysfs_ops, + .release = fw_cl_release, + .default_groups = fw_cl_groups, +}; + +/** + * issei_fw_cl_create - create firmware client object and add to list + * @idev: issei device object + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client unique id + * @mtu: firmware client maximum message size + * @flags: firmware client flags + * + * Should be called under idev->client_lock + * + * Return: pointer to newly created object on success, ERR_PTR on failure + */ +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags) +{ + int ret; + struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl); + + if (!fw_cl) + return ERR_PTR(-ENOMEM); + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags); + fw_cl->kobj.kset = idev->fw_clients; + + ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id); + if (ret) { + kobject_put(&fw_cl->kobj); + return ERR_PTR(ret); + } + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + + dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + + kobject_uevent(&fw_cl->kobj, KOBJ_ADD); + + return fw_cl; +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl) +{ + WARN(fw_cl->cl, "Removing connected client!\n"); + + dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + + list_del(&fw_cl->list); + kobject_put(&fw_cl->kobj); +} + +/** + * issei_fw_cl_remove_all - remove all firmware client objects + * @idev: issei device object + */ +void issei_fw_cl_remove_all(struct issei_device *idev) +{ + struct issei_fw_client *fw_cl, *next; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list) + __issei_fw_cl_remove(idev, fw_cl); +} + +/** + * issei_fw_cl_find_by_uuid - find firmware client by uuid + * @idev: issei device object + * @uuid: uuid to search by it + * + * Should be called under idev->client_lock + * + * Return: pointer to firmware client object if found, NULL on failure + */ +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid) +{ + struct issei_fw_client *fw_cl; + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + list_for_each_entry(fw_cl, &idev->fw_client_list, list) { + if (uuid_equal(&fw_cl->uuid, uuid)) { + kobject_get(&fw_cl->kobj); + return fw_cl; + } + } + return NULL; +} + +/** + * issei_fw_cl_connect - connect firmware and host client + * @fw_cl: firmware client + * @cl: host client + * + * Should be called under idev->client_lock + * + * Return: 0 on success, -EBUSY if already connected + */ +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl) +{ + if (fw_cl->cl) + return -EBUSY; + + kobject_get(&fw_cl->kobj); + fw_cl->cl = cl; + return 0; +} + +/** + * issei_fw_cl_disconnect - disconnect firmware and host client + * @fw_cl: firmware client + * + * Should be called under idev->client_lock + */ +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl) +{ + WARN_ON(!fw_cl->cl); + + fw_cl->cl = NULL; + kobject_put(&fw_cl->kobj); +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 index 000000000000..377f733d7e91 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_FW_CLIENT_H_ +#define _ISSEI_FW_CLIENT_H_ + +#include +#include +#include +#include + +struct issei_device; +struct issei_host_client; + +/** + * struct issei_fw_client - represents firmware queue + * @kobj: associated kobject + * @list: link in firmware clients list + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client protocol id + * @mtu: firmware client maximum buffer size + * @flags: firmware client flags + * @cl: pointer to host client, if connected + */ +struct issei_fw_client { + struct kobject kobj; + struct list_head list; + u16 id; + u8 ver; + uuid_t uuid; + u32 mtu; + u32 flags; + struct issei_host_client *cl; +}; +#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj) + +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags); +void issei_fw_cl_remove_all(struct issei_device *idev); +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid); + +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl); +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl); + +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 index 000000000000..674d9733d16c --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include + +#include "dma.h" +#include "fw_client.h" +#include "ham.h" +#include "hw_msg.h" +#include "issei_dev.h" + +static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf) +{ + struct issei_dma_data data = { }; + int ret; + + data.length = length; + data.buf = buf; + ret = issei_dma_write(idev, &data); + if (ret) + return ret; + return idev->ops->irq_write_generate(idev); +} + +/** + * issei_ham_send_start_req - send start request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_start_req(struct issei_device *idev) +{ + struct ham_start_message_req req; + + req.header.cmd = HAM_BUS_CMD_START_REQ; + req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER; + req.heci_capabilities_length = 0; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +/** + * issei_ham_send_clients_req - send clients request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_clients_req(struct issei_device *idev) +{ + struct ham_get_clients_req req; + + req.header.cmd = HAM_BUS_CMD_CLIENT_REQ; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_start_message_res *res = (struct ham_start_message_res *)buf; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_START) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->heci_capabilities_length) { + dev_err(&idev->dev, "Wrong start response size %zu != %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver = res->supported_version; + dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + + ret = issei_ham_send_clients_req(idev); + if (ret == -EBUSY) + ret = 0; + + return ret; +} + +static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf; + struct ham_client_properties *client; + + if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { + dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; + } + + guard(mutex)(&idev->client_lock); + + for (size_t i = 0; i < res->client_count; i++) { + client = &res->clients_props[i]; + dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, + &client->client_uuid, client->client_mtu, client->flags); + } + return 0; +} + +static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_bus_message *hdr = (struct ham_bus_message *)buf; + + if (length < sizeof(*hdr)) { + dev_err(&idev->dev, "Small bus message size %zu < %zu\n", + length, sizeof(*hdr)); + return -EPROTO; + } + + switch (hdr->cmd) { + case HAM_BUS_CMD_START_RSP: + return issei_ham_start_rsp(idev, buf, length); + + case HAM_BUS_CMD_CLIENT_RSP: + return issei_ham_client_rsp(idev, buf, length); + + default: + dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + return -EPROTO; + } +} + +/** + * issei_ham_process_ham_rsp - process response from firmware and release buffer + * @idev: issei device object + * @buf: response buffer + * @length: response buffer length + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + int ret; + + ret = __issei_ham_process_ham_rsp(idev, buf, length); + kfree(buf); + return ret; +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 index 000000000000..37ea64bb2920 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HAM_H_ +#define _ISSEI_HAM_H_ + +#include + +struct issei_device; + +int issei_ham_send_start_req(struct issei_device *idev); +int issei_ham_send_clients_req(struct issei_device *idev); + +static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id) +{ + return fw_id == 0 && host_id == 0; +} + +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length); + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c new file mode 100644 index 000000000000..8f36d1c319ec --- /dev/null +++ b/drivers/misc/issei/host_client.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fw_client.h" +#include "host_client.h" +#include "issei_dev.h" + +static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} + +#define ISSEI_CL_FMT "cl:host=%02d fw=%02d " + +#define cl_dbg(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_warn(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_err(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) +{ + list_del(&wbuf->list); + kfree(wbuf->data); + kfree(wbuf); +} + +static void __issei_cl_release_rbuf(struct issei_host_client *cl) +{ + cl->read_data = NULL; + cl->read_data_size = 0; +} + +static void __issei_cl_clean_rbuf(struct issei_host_client *cl) +{ + kfree(cl->read_data); + __issei_cl_release_rbuf(cl); +} + +static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl) +{ + struct issei_write_buf *wbuf, *next; + + if (!cl->write_in_progress) + return; + list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) { + if (wbuf->cl == cl) { + __issei_cl_clean_wbuf(wbuf); + break; + } + } + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); +} + +static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id) +{ + struct issei_host_client *cl; + + list_for_each_entry(cl, &idev->host_client_list, list) { + if (cl->id == id) + return cl; + } + return NULL; +} + +static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl) +{ + if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED) + return; + + __issei_cl_clean_all_wbuf(idev, cl); + + if (!WARN_ON(!cl->fw_cl)) { + issei_fw_cl_disconnect(cl->fw_cl); + cl->fw_cl = NULL; + } + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + + if (cl->read_data) + __issei_cl_clean_rbuf(cl); + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Disconnected\n"); +} + +static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev, + u16 id, struct file *fp) +{ + INIT_LIST_HEAD(&cl->list); + cl->idev = idev; + cl->id = id; + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + cl->fp = fp; + init_waitqueue_head(&cl->write_wait); + init_waitqueue_head(&cl->read_wait); + __issei_cl_release_rbuf(cl); +} + +/** + * issei_cl_create - create the host client + * @idev: issei device + * @fp: file pointer to associate with host client + * + * Return: client pointer on success, ERR_PTR on error + */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp) +{ + struct issei_host_client *cl; + u16 id; + + guard(mutex)(&idev->client_lock); + + if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) { + dev_err(&idev->dev, "Maximum open clients %d is reached.\n", + ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + + do { + if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */ + id = 1; + idev->host_client_last_id = id; + /* Not an endless loop as we have less clients then id's */ + } while (__issei_cl_by_id(idev, id)); + + cl = kzalloc_obj(*cl); + if (!cl) + return ERR_PTR(-ENOMEM); + + __issei_cl_init(cl, idev, id, fp); + list_add_tail(&cl->list, &idev->host_client_list); + idev->host_client_count++; + + cl_dbg(idev, cl, "Created\n"); + return cl; +} + +/** + * issei_cl_remove - disconnect and free the host client + * @cl: host client + */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; + + /* don't shout on error exit path */ + if (!cl) + return; + + idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + idev->host_client_count--; + list_del(&cl->list); + + __issei_cl_disconnect(idev, cl); + + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); +} + +/** + * issei_cl_connect - connect between FW and host client + * @cl: host client + * @uuid: FW client unique ID + * @mtu: memory for FW client max message size + * @ver: memory for FW client version + * @flags: memory for FW client flags + * + * Search for firmware client by UUID and connect it to provided + * host client, if not already connected to some client. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags) +{ + struct issei_device *idev = cl->idev; + struct issei_fw_client *fw_cl; + int ret; + + guard(mutex)(&idev->client_lock); + + if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) { + cl_err(idev, cl, "Already connected\n"); + return -EISCONN; + } + + fw_cl = issei_fw_cl_find_by_uuid(idev, uuid); + if (!fw_cl) { + cl_dbg(idev, cl, "FW client %pUb not found\n", uuid); + return -ENOTTY; + } + + ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */ + kobject_put(&fw_cl->kobj); + if (ret) { + cl_err(idev, cl, "FW client is already connected ret = %d\n", ret); + return ret; + } + + cl->fw_cl = fw_cl; + cl->state = ISSEI_HOST_CL_STATE_CONNECTED; + + *mtu = fw_cl->mtu; + *ver = fw_cl->ver; + *flags = fw_cl->flags; + cl_dbg(idev, cl, "Connected\n"); + return 0; +} + +/** + * issei_cl_disconnect - disconnect between FW and host client + * @cl: host client + * + * Return: 0 on success, -ENOTCONN if not connected + */ +int issei_cl_disconnect(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + __issei_cl_disconnect(idev, cl); + return 0; +} + +/** + * issei_cl_all_disconnect - disconnect all FW clients + * @idev: issei device + */ +void issei_cl_all_disconnect(struct issei_device *idev) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry(cl, &idev->host_client_list, list) + __issei_cl_disconnect(idev, cl); +} + +/** + * issei_cl_write - enqueue write request + * @cl: host client + * @buf: buffer to write + * @buf_size: buffer size + * + * Add write request to the write queue and wakes working thread. + * This call takes ownership of buf memory, if succeeded. + * + * Return: size of data on success, <0 on error + */ +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + struct issei_write_buf *wbuf; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (cl->write_in_progress) { + cl_dbg(idev, cl, "Another write is in progress\n"); + return -EAGAIN; + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + return -EFBIG; + } + + wbuf = kmalloc_obj(*wbuf); + if (!wbuf) + return -ENOMEM; + wbuf->cl = cl; + wbuf->data = buf; + wbuf->data_size = buf_size; + list_add_tail(&wbuf->list, &idev->write_queue); + cl->write_in_progress = true; + cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size); + + issei_poke_process_thread(idev); + + return buf_size; +} + +/** + * issei_cl_write_from_queue - writes first request from queue to firmware + * @idev: issei device + * + * Tries to write first request from the write queue to firmware. + * Releases buf memory, if succeeded. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_write_from_queue(struct issei_device *idev) +{ + struct issei_write_buf *wbuf; + struct issei_dma_data data; + struct issei_host_client *cl; + int ret; + + guard(mutex)(&idev->client_lock); + + wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list); + if (!wbuf) + return 0; + + cl = wbuf->cl; + + data.fw_id = cl->fw_cl->id; + data.host_id = cl->id; + data.flags = 0; + data.status = 0; + data.length = wbuf->data_size; + data.buf = (void *)wbuf->data; + ret = issei_dma_write(idev, &data); + if (ret == -EBUSY) + return 0; + if (ret == -EIO) + return ret; + if (ret >= 0) + idev->ops->irq_write_generate(idev); + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); + cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size); + __issei_cl_clean_wbuf(wbuf); + return 0; +} + +static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id, + u16 host_id, size_t buf_size) +{ + struct issei_host_client *cl; + + cl = __issei_cl_by_id(idev, host_id); + if (!cl) { + dev_dbg(&idev->dev, "No client %u\n", host_id); + return ERR_PTR(-ENOTTY); + } + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) { + cl_dbg(idev, cl, "Not connected\n"); + return ERR_PTR(-ENODEV); + } + if (cl->fw_cl->id != fw_id) { + cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id); + return ERR_PTR(-ENODEV); + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + if (cl->read_data) { + cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n"); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + return cl; +} + +/** + * issei_cl_read_buf - process data from firmware + * @idev: issei device + * @fw_id: firmware client id + * @host_id: host client id + * @buf: buffer with data + * @buf_size: buffer size + * + * Puts data from firmware into provided host client storage. + * Free buffer or consume it. + * + * Return: 0 on success or recoverable error, <0 on unrecoverable error + */ +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size); + if (IS_ERR_OR_NULL(cl)) { + kfree(buf); + return PTR_ERR(cl); + } + + cl->read_data = buf; + cl->read_data_size = buf_size; + + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Read %zu bytes\n", buf_size); + + return 0; +} + +/** + * issei_cl_read - read data from queue to provided buffer + * @cl: host client + * @buf: buffer to store data + * @buf_size: buffer size + * + * Tries to take data buffer from client and return it to caller. + * The caller receives ownership of the data buffer. + * + * Return: read data size on success, <0 on error + */ +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + size_t read_data_size; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (!cl->read_data) + return -ENOENT; + + if (cl->read_data_size > buf_size) { + cl_err(idev, cl, "Buffer is too small %zu > %zu\n", + cl->read_data_size, buf_size); + return -EFBIG; + } + + *buf = cl->read_data; + read_data_size = cl->read_data_size; + cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size); + __issei_cl_release_rbuf(cl); + return read_data_size; +} + +/** + * issei_cl_check_read - check if client has data to read + * + * @cl: host client + * + * Return: 1 - data available, 0 - no data, < 0 on error + */ +int issei_cl_check_read(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (!cl->read_data) + return 0; + return 1; +} + +/** + * issei_cl_check_write - check if client is ready to write + * + * @cl: host client + * + * Return: 1 - can not write, 0 - can write, < 0 on error + */ +int issei_cl_check_write(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (cl->write_in_progress) + return 1; + return 0; +} + +void issei_cl_clean_all_wbuf(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + __issei_cl_clean_all_wbuf(idev, cl); +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h new file mode 100644 index 000000000000..05e2f4ade9c3 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HOST_CLIENT_H_ +#define _ISSEI_HOST_CLIENT_H_ + +#include +#include +#include + +struct file; + +struct issei_device; +struct issei_fw_client; + +/** + * enum issei_host_client_state - host client states + * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected + * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected + */ +enum issei_host_client_state { + ISSEI_HOST_CL_STATE_DISCONNECTED, + ISSEI_HOST_CL_STATE_CONNECTED, +}; + +/** + * struct issei_host_client - represents host client + * @list: link in host clients list + * @idev: issei parent device + * @id: host client id + * @fp: file associated with client + * + * @write_wait: waitqueue for pending write data + * @write_in_progress: indicator for write in process + * + * @state: host client state + * @fw_cl: pointer to firmware client, if connected + * + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size + */ +struct issei_host_client { + struct list_head list; + struct issei_device *idev; + u16 id; + const struct file *fp; + + wait_queue_head_t write_wait; + bool write_in_progress; + + enum issei_host_client_state state; + struct issei_fw_client *fw_cl; + + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; +}; + +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp); +void issei_cl_remove(struct issei_host_client *cl); + +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags); +int issei_cl_disconnect(struct issei_host_client *cl); +void issei_cl_all_disconnect(struct issei_device *idev); + +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size); +int issei_cl_write_from_queue(struct issei_device *idev); +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size); +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size); +int issei_cl_check_read(struct issei_host_client *cl); +int issei_cl_check_write(struct issei_host_client *cl); +void issei_cl_clean_all_wbuf(struct issei_host_client *cl); + +#endif /* ISSEI_HOST_CLIENT_H_ */ diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 index 000000000000..501774aa95ff --- /dev/null +++ b/drivers/misc/issei/hw_heci.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hw_heci.h" +#include "hw_heci_regs.h" +#include "hw_msg.h" + +/** + * heci_reg_read - Reads 32bit data from the issei heci device + * @hw: the heci hardware structure + * @offset: offset from which to read the data + * + * Return: register value (u32) + */ +static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset) +{ + return ioread32(hw->mem_addr + offset); +} + +/** + * heci_reg_write - Writes 32bit data to the issei heci device + * + * @hw: the heci hardware structure + * @offset: offset from which to write the data + * @value: register value to write (u32) + */ +static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value) +{ + iowrite32(value, hw->mem_addr + offset); +} + +/** + * heci_fwcbrw_read - Reads 32bit data from heci circular buffer + * @hw: the heci hardware structure + * + * Return: FW_CB_RW register value (u32) + */ +static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw) +{ + return heci_reg_read(hw, FW_CB_RW); +} + +/** + * heci_hcbww_write - write 32bit data to the host circular buffer + * @hw: the heci hardware structure + * @data: 32bit data to be written to the host circular buffer + */ +static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data) +{ + heci_reg_write(hw, H_CB_WW, data); +} + +/** + * heci_irq_src - Filters IRQ source bits from the host CSR + * @hcsr: host CSR register value + * + * Return: interrupt source bits of host CSR + */ +static inline u32 heci_irq_src(u32 hcsr) +{ + return hcsr & H_CSR_IS; +} + +/** + * heci_hcsr_read - Reads 32bit data from the host CSR + * @idev: the device structure + * + * Return: H_CSR register value (u32) + */ +static inline u32 heci_hcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), H_CSR); +} + +/** + * heci_hcsr_write - writes H_CSR register to device + * @idev: the device structure + * @reg: new register value + */ +static inline void heci_hcsr_write(struct issei_device *idev, u32 reg) +{ + heci_reg_write(to_heci_hw(idev), H_CSR, reg); +} + +/** + * heci_hcsr_set - writes H_CSR register to the heci device + * @idev: the device structure + * @reg: new register value + * + * Writes H_CSR register to the heci device + * and ignores the H_IS bit for it is write-one-to-zero. + * + */ +static inline void heci_hcsr_set(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IS; + heci_hcsr_write(idev, reg); +} + +/** + * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG) + * @idev: the device structure + */ +static inline void heci_hcsr_set_hig(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IG; + heci_hcsr_set(idev, reg); +} + +/** + * heci_fwcsr_read - Reads 32bit data from the FW CSR + * @idev: the device structure + * + * Return: FW_CSR_HA register value (u32) + */ +static inline u32 heci_fwcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), FW_CSR_HA); +} + +/** + * heci_count_full_read_slots - counts read full slots. + * @idev: the device structure + * + * Return: -EOVERFLOW if overflow, otherwise filled slots count + */ +static int heci_count_full_read_slots(struct issei_device *idev) +{ + u8 buffer_depth, filled_slots; + u8 read_ptr, write_ptr; + u32 reg; + + reg = heci_fwcsr_read(idev); + buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg); + read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg); + write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg); + filled_slots = write_ptr - read_ptr; + + /* check for overflow */ + if (filled_slots > buffer_depth) + return -EOVERFLOW; + + dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots); + return filled_slots; +} + +/** + * heci_irq_disable - disables heci device interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + * + * disables heci device interrupts using supplied hcsr register value. + */ +static inline void heci_irq_disable(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * heci_irq_clear - clear and stop interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + */ +static inline void heci_irq_clear(struct issei_device *idev, u32 reg) +{ + if (heci_irq_src(reg)) + heci_hcsr_write(idev, reg); +} + +/** + * issei_heci_irq_clear - clear and stop interrupts + * @idev: the device structure + */ +static void issei_heci_irq_clear(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_clear(idev, reg); +} + +/** + * issei_heci_irq_enable - enables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_enable(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_irq_disable - disables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_disable(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_disable(idev, reg); +} + +static void issei_heci_irq_sync(struct issei_device *idev) +{ + synchronize_irq(to_heci_hw(idev)->irq); +} + +/** + * issei_heci_hw_reset_release - release device from the reset + * @idev: the device structure + */ +static void issei_heci_hw_reset_release(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IG; + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); +} + +/** + * heci_hw_is_ready - check whether the hw has turned ready + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_ready(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RDY; +} + +/** + * heci_hw_is_resetting - check whether the hw is in reset + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_resetting(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RST; +} + +/** + * issei_heci_host_set_ready - enable device + * @idev: the device structure + */ +static void issei_heci_host_set_ready(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_hw_reset - resets fw via heci csr register. + * @idev: the device structure + * @enable: if interrupt should be enabled after reset. + * + * Return: 0 on success an error code otherwise + */ +static int issei_heci_hw_reset(struct issei_device *idev, bool enable) +{ + u32 reg; + + if (enable) + issei_heci_irq_enable(idev); + + reg = heci_hcsr_read(idev); + /* + * H_CSR_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_CSR_RST will be ignored, therefore + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { + dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg); + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg = heci_hcsr_read(idev); + } + + reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS; + + if (!enable) + reg &= ~H_CSR_IE; + + heci_hcsr_write(idev, reg); + + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + reg = heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) + dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg); + + if (reg & H_CSR_RDY) + dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); + return 0; +} + +/** + * issei_heci_irq_write_generate - generate interrupt to signal write completion. + * @idev: the device structure + * + * Return: 0 on success, -EIO if hardware is not ready and requires reset + */ +static int issei_heci_irq_write_generate(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + scoped_guard(spinlock_irqsave, &hw->access_lock) + heci_hcsr_set_hig(idev); + if (!heci_hw_is_ready(idev)) + return -EIO; + return 0; +} + +/** + * issei_heci_hw_config - initial hardware configuration. + * @idev: the device structure + * + * Return: 0 always + */ +static int issei_heci_hw_config(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + /* Doesn't change in runtime */ + reg = heci_hcsr_read(idev); + hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg); + + return 0; +} + +/** + * heci_write_hbuf - write to hardware buffer + * @idev: the device structure + * @data: data to write + * @data_len: data size + * + * Return: 0 on success, <0 on error + */ +static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + const u32 *reg_buf = data; + size_t i; + + for (i = 0; i < data_len / CB_SLOT_SIZE; i++) + heci_hcbww_write(hw, reg_buf[i]); + } + + return issei_heci_irq_write_generate(idev); +} + +/** + * heci_read_hbuf - read data from hardware buffer + * @idev: the device structure + * @data: buffer to store read data + * @data_len: buffer size + * + * Return: 0 on success, <0 on error + */ +static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 *reg_buf = data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE) + *reg_buf++ = heci_fwcbrw_read(hw); + } + return 0; +} + +/** + * issei_heci_setup_message_send - send setup message to firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_send(struct issei_device *idev) +{ + struct ham_setup_shared_memory_req req = { + .msg_id = HAM_CB_MESSAGE_ID_REQ, + .ver = HAM_CB_MESSAGE_VER, + .reserved = 0, + .buffer_physical_address = idev->dma.daddr, + .host_to_fw_section_length = idev->dma.length.h2f, + .fw_to_host_section_length = idev->dma.length.f2h, + .control_length = idev->dma.length.ctl + }; + int ret; + + ret = heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) + dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret); + + return ret; +} + +/** + * issei_heci_setup_message_recv - receive setup message response from firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_recv(struct issei_device *idev) +{ + struct ham_setup_shared_memory_res res; + int ret; + + if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) { + dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret = heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { + dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret); + return ret; + } + + if (res.msg_id != HAM_CB_MESSAGE_ID_RES) { + dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status != 0) { + dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status); + return -EPROTO; + } + + return 0; +} + +static const struct issei_hw_ops hw_heci_ops = { + .irq_clear = issei_heci_irq_clear, + .irq_enable = issei_heci_irq_enable, + .irq_disable = issei_heci_irq_disable, + .irq_sync = issei_heci_irq_sync, + + .hw_reset = issei_heci_hw_reset, + .hw_config = issei_heci_hw_config, + .hw_reset_release = issei_heci_hw_reset_release, + .host_set_ready = issei_heci_host_set_ready, + + .hw_is_ready = heci_hw_is_ready, + + .setup_message_send = issei_heci_setup_message_send, + .setup_message_recv = issei_heci_setup_message_recv, + .irq_write_generate = issei_heci_irq_write_generate, +}; + +const struct issei_hw_ops *issei_heci_get_ops(void) +{ + return &hw_heci_ops; +} + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev = dev_id; + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + reg = heci_hcsr_read(idev); + if (!heci_irq_src(reg)) + return IRQ_NONE; + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + reg = heci_hcsr_read(idev); + heci_irq_clear(idev, reg); + + if (heci_hw_is_resetting(idev)) + heci_hcsr_set_hig(idev); + } + + dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + issei_poke_process_thread(idev); + + return IRQ_HANDLED; +} + +static const struct hw_heci_cfg hw_heci_pch_cfg = { + .dma_length.h2f = SZ_64K, + .dma_length.f2h = SZ_64K, + .dma_length.ctl = SZ_4K, +}; + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx) +{ + return &hw_heci_pch_cfg; +} + +/** + * issei_heci_dev_init - initializes the issei device structure with hw_heci + * @idev: device structure + * @mem_addr: memory address on bar + * @cfg: per device generation config + */ +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + spin_lock_init(&hw->access_lock); + hw->mem_addr = mem_addr; + hw->cfg = cfg; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 index 000000000000..0ae2faeedf12 --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_H_ +#define _ISSEI_HW_HECI_H_ +#include +#include +#include + +#include "issei_dev.h" + +/* + * hw_heci_cfg - issei heci device configuration + * + * @dma_length: DMA area length + */ +struct hw_heci_cfg { + const struct issei_dma_length dma_length; +}; + +/** + * struct issei_heci_hw - issei heci hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @irq: device irq number + * @access_lock: spinlock to protect hw access + * @hbuf_depth: depth of hardware host/write buffer in slots + */ +struct issei_heci_hw { + const struct hw_heci_cfg *cfg; + void __iomem *mem_addr; + int irq; + spinlock_t access_lock; + u8 hbuf_depth; +}; + +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); +const struct issei_hw_ops *issei_heci_get_ops(void); + +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h new file mode 100644 index 000000000000..a991b670a9d3 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_REGS_H_ +#define _ISSEI_HW_HECI_REGS_H_ + +#include + +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW 0x0 +/* H_CSR - Host Control Status register */ +#define H_CSR 0x4 +#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */ +#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer */ +#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */ +#define H_CSR_RST BIT(4) /* Host Reset */ +#define H_CSR_RDY BIT(3) /* Host Ready */ +#define H_CSR_IG BIT(2) /* Host Interrupt Generate */ +#define H_CSR_IS BIT(1) /* Host Interrupt Status */ +#define H_CSR_IE BIT(0) /* Host Interrupt Enable */ +/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */ +#define FW_CB_RW 0x8 +/* FW_CSR_HA - FW Control Status Host Access register (read only) */ +#define FW_CSR_HA 0xC +#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */ +#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */ +#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */ +#define FW_CSR_RST BIT(4) /* FW Reset */ +#define FW_CSR_RDY BIT(3) /* FW Ready */ +#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */ +#define FW_CSR_IS BIT(1) /* FW Interrupt Status */ +#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */ + +#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */ + +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 index 000000000000..28fd3775f64c --- /dev/null +++ b/drivers/misc/issei/hw_msg.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_MSG_H_ +#define _ISSEI_HW_MSG_H_ + +#include +#include + +#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe +#define HAM_CB_MESSAGE_ID_RES 0xcafe8086 +#define HAM_CB_MESSAGE_VER 0x1 + +/** + * struct ham_setup_shared_memory_req - shared memory setup request + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ + * @ver: message version (%HAM_CB_MESSAGE_VER) + * @reserved: reserved + * @buffer_physical_address: physical address of DMA buffer + * @host_to_fw_section_length: memory size for host to fw communication + * @fw_to_host_section_length: memory size for fw to host communication + * @control_length: memory size for control buffer + */ +struct ham_setup_shared_memory_req { + u32 msg_id; + u16 ver; + u16 reserved; + u64 buffer_physical_address; + u32 host_to_fw_section_length; + u32 fw_to_host_section_length; + u32 control_length; +} __packed __aligned(4); + +/** + * struct ham_setup_shared_memory_res - shared memory setup response + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES + * @status: operation status + */ +struct ham_setup_shared_memory_res { + u32 msg_id; + u32 status; +}; + +/** + * struct control_buffer - control buffer structure + * @h2f_counter_wr: write counter host to fw + * @h2f_counter_rd: read counter host to fw + * @f2h_counter_wr: write counter fw to host + * @f2h_counter_rd: read counter fw to host + */ +struct control_buffer { + u32 h2f_counter_wr; + u32 h2f_counter_rd; + u32 f2h_counter_wr; + u32 f2h_counter_rd; +}; + +/* HAM messages over DMA */ + +/** + * struct ham_message_header - message header over DMA + * @length: message length (payload only, not including header) + * @fw_id: firmware client id (0 means Bus Message) + * @host_id: host client id (0 means Bus Message) + * @flags: message flags + * @status: operation status + * @reserved: reserved + */ +struct ham_message_header { + u32 length; + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 reserved; +}; + +/* Bus Commands */ +#define HAM_BUS_CMD_START_REQ 0x00 +#define HAM_BUS_CMD_START_RSP 0x80 +#define HAM_BUS_CMD_CLIENT_REQ 0x01 +#define HAM_BUS_CMD_CLIENT_RSP 0x81 + +/** + * struct ham_bus_message - bus message header + * @cmd: command code + */ +struct ham_bus_message { + u32 cmd; +}; + +#define HAM_SUPPORTED_VERSION 0x01 + +/** + * struct ham_start_message_req - start message + * @header: bus message header (%HAM_BUS_CMD_START_REQ) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_req { + struct ham_bus_message header; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_start_message_res - start message response + * @header: bus message header (%HAM_BUS_CMD_START_RSP) + * @fw_version: firmware version (four u16 blocks) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_res { + struct ham_bus_message header; + u16 fw_version[4]; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_get_clients_req - clients list request + * @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ) + */ +struct ham_get_clients_req { + struct ham_bus_message header; +}; + +/** + * struct ham_client_properties - single client properties + * @client_number: client id in firmware + * @protocol_ver: client protocol version + * @reserved: reserved + * @client_uuid: protocol name (UUID) + * @client_mtu: max message length supported by client + * @flags: client flags + */ +struct ham_client_properties { + u16 client_number; + u8 protocol_ver; + u8 reserved; + uuid_t client_uuid; + u32 client_mtu; + u32 flags; +}; + +/** + * struct ham_get_clients_res - client properties response + * @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP) + * @client_count: number of clients in firmware + * @reserved: reserved + * @clients_props: list of client properties + */ +struct ham_get_clients_res { + struct ham_bus_message header; + u16 client_count; + u16 reserved; + struct ham_client_properties clients_props[] __counted_by(client_count); +}; + +#endif /* _ISSEI_HW_MSG_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 index 000000000000..b47c4a7c2da4 --- /dev/null +++ b/drivers/misc/issei/issei_dev.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DEV_H_ +#define _ISSEI_DEV_H_ + +#include +#include +#include +#include +#include +#include + +#include "dma.h" + +struct cdev; +struct kset; + +struct issei_device; +struct issei_host_client; + +extern struct class *issei_class; + +#define ISSEI_HOST_CLIENTS_MAX 255 + +#define ISSEI_SUPPORTED_PROTOCOL_VER 1 + +#define ISSEI_MAX_CONSEC_RESET 3 + +#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_STOP_TIMEOUT_MSEC 500 +#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC) + +/** + * struct issei_write_buf - write buffer object + * @list: linked list pointer + * @cl: host client that requested this write + * @data: data to write + * @data_size: data size + */ +struct issei_write_buf { + struct list_head list; + struct issei_host_client *cl; + const u8 *data; + size_t data_size; +}; + +/** + * struct issei_hw_ops - callbacks for hardware operations + * @irq_clear: clear irq + * @irq_enable: enable irq + * @irq_disable: disable irq + * @irq_sync: sync irq + * @hw_reset: initiate hardware reset + * @hw_config: initial hardware config + * @hw_is_ready: check if hardware is ready + * @hw_reset_release: release hardware from reset + * @host_set_ready: set host ready indicator + * @setup_message_send: send setup message + * @setup_message_recv: receive setup message + * @irq_write_generate: generate interrupt on write complete + */ +struct issei_hw_ops { + void (*irq_clear)(struct issei_device *idev); + void (*irq_enable)(struct issei_device *idev); + void (*irq_disable)(struct issei_device *idev); + void (*irq_sync)(struct issei_device *idev); + int (*hw_reset)(struct issei_device *idev, bool enable); + int (*hw_config)(struct issei_device *idev); + bool (*hw_is_ready)(struct issei_device *idev); + void (*hw_reset_release)(struct issei_device *idev); + void (*host_set_ready)(struct issei_device *idev); + int (*setup_message_send)(struct issei_device *idev); + int (*setup_message_recv)(struct issei_device *idev); + int (*irq_write_generate)(struct issei_device *idev); +}; + +/** + * enum issei_rst_state: driver reset flow states + * @ISSEI_RST_STATE_INIT: initial state + * @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready + * @ISSEI_RST_STATE_SETUP: waiting for channel setup completion + * @ISSEI_RST_STATE_START: waiting for start handshake completion + * @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration + * @ISSEI_RST_STATE_DONE: reset flow is done + * @ISSEI_RST_STATE_DISABLED: flow is disabled + */ +enum issei_rst_state { + ISSEI_RST_STATE_INIT, + ISSEI_RST_STATE_HW_READY, + ISSEI_RST_STATE_SETUP, + ISSEI_RST_STATE_START, + ISSEI_RST_STATE_CLIENT_ENUM, + ISSEI_RST_STATE_DONE, + ISSEI_RST_STATE_DISABLED, +}; + +/** + * struct issei_device - issei device + * @parent: parent device object + * @dev: associated device object + * @cdev: character device + * @minor: allocated minor number + * @wait_has_data: wait queue for data + * @has_data: there are data to process + * @power_down: device is powering down + * @wait_rst_state: waitqueue for reset state processing + * @rst_state: reset state + * @fw_protocol_ver: protocol version + * @fw_version: firmware version + * @process_thread: worker thread + * @reset_count: number of consecutive link reset attempts + * @all_reset_count: cumilative number of link reset attempts + * @client_lock: mutex to protect client lists and write queue + * @host_client_list: host clients list + * @host_client_last_id: last allocated host client id + * @host_client_count: number of active host clients + * @fw_client_list: firmware clients list + * @write_queue: write queue + * @last_write_ts: last write timestamp + * @dma: DMA memory configuration + * @ops: hardware operations + * @hw: hw-specific data + */ +struct issei_device { + struct device *parent; + struct device dev; + struct cdev *cdev; + u32 minor; + wait_queue_head_t wait_has_data; + bool has_data; + bool power_down; + wait_queue_head_t wait_rst_state; + enum issei_rst_state rst_state; + u16 fw_protocol_ver; + u16 fw_version[4]; + /* reset flow */ + struct task_struct *process_thread; + u8 reset_count; + u8 all_reset_count; + /* clients */ + struct mutex client_lock; + struct list_head host_client_list; + u16 host_client_last_id; + u8 host_client_count; + struct kset *fw_clients; + struct list_head fw_client_list; + struct list_head write_queue; + ktime_t last_write_ts; + struct issei_dma dma; + const struct issei_hw_ops *ops; + char hw[]; +}; + +static inline void issei_poke_process_thread(struct issei_device *idev) +{ + WRITE_ONCE(idev->has_data, true); + wake_up_interruptible(&idev->wait_has_data); +} + +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); +#endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 index 000000000000..5987fb340250 --- /dev/null +++ b/drivers/misc/issei/main.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "fw_client.h" +#include "host_client.h" +#include "ham.h" +#include "issei_dev.h" + +static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state) +{ + idev->rst_state = state; + /* wake up the thread */ + if (waitqueue_active(&idev->wait_rst_state)) + wake_up(&idev->wait_rst_state); +} + +static int issei_reset(struct issei_device *idev) +{ + int ret; + + idev->ops->irq_clear(idev); + + issei_cl_all_disconnect(idev); + issei_fw_cl_remove_all(idev); + /* No need to check for overflow here, the counter is used only for info */ + idev->all_reset_count++; + ret = idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_setup(idev); + if (ret) { + dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); + return ret; + } + + if (idev->power_down) { + dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } + return 0; +} + +static int issei_process_read_msg(struct issei_device *idev) +{ + struct issei_dma_data data = {}; + int ret; + + ret = issei_dma_read(idev, &data); + if (ret) + return ret; + + dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, + data.status, data.length); + if (data.status != HAMS_SUCCESS) { + dev_err(&idev->dev, "Command failed with status 0x%02X", data.status); + kfree(data.buf); + ret = -EIO; + } else { + if (issei_is_ham_rsp(data.fw_id, data.host_id)) + ret = issei_ham_process_ham_rsp(idev, data.buf, data.length); + else + ret = issei_cl_read_buf(idev, data.fw_id, data.host_id, + data.buf, data.length); + } + idev->ops->irq_write_generate(idev); + return ret; +} + +static int issei_process_write_msg(struct issei_device *idev) +{ + if (idev->rst_state != ISSEI_RST_STATE_DONE) + return 0; + + return issei_cl_write_from_queue(idev); +} + +static int issei_process_thread(void *_dev) +{ + long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT; + struct issei_device *idev = _dev; + int ret; + + while (!kthread_should_stop()) { + dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { + if (!idev->power_down) + dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state = ISSEI_RST_STATE_INIT; + WRITE_ONCE(idev->has_data, false); + dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout = MAX_SCHEDULE_TIMEOUT; + ret = 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { + dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state = ISSEI_RST_STATE_INIT; + fallthrough; + + case ISSEI_RST_STATE_INIT: + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { + dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } + } + + ret = issei_reset(idev); + if (idev->power_down) { + dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_HW_READY; + timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_HW_READY: + if (!idev->ops->hw_is_ready(idev)) { + dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout = old_timeout; + break; + } + + dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret = idev->ops->setup_message_send(idev); + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_SETUP; + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_SETUP: + ret = idev->ops->setup_message_recv(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + ret = issei_ham_send_start_req(idev); + idev->rst_state = ISSEI_RST_STATE_START; + } + break; + + case ISSEI_RST_STATE_START: + ret = issei_process_read_msg(idev); + if (!ret) { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM; + } else if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + break; + + case ISSEI_RST_STATE_CLIENT_ENUM: + ret = issei_process_read_msg(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + idev->reset_count = 0; + idev->rst_state = ISSEI_RST_STATE_DONE; + dev_dbg(&idev->dev, "Reset finished successfully\n"); + } + break; + + case ISSEI_RST_STATE_DONE: + ret = issei_process_read_msg(idev); + if (ret != 0 && ret != -ENODATA) + break; + + ret = issei_process_write_msg(idev); + break; + } + + if (ret) { + dev_warn(&idev->dev, "Process failed ret = %d\n", ret); + idev->rst_state = ISSEI_RST_STATE_INIT; + continue; + } + + /* + * Every thread that has data to process sets the 'has_data' flag and + * triggers the wait queue. + * The processing thread, in each loop iteration, resets 'has_data' + * and processes all available data. + * + * After processing, the thread waits for 'has_data' to be set again. + * + * If the wait function times out but 'has_data' becomes 1 before + * the subsequent atomic read check, this is acceptable from a flow + * perspective - the thread will continue processing the data. + * + * The 'has_data' flag cannot become 0 between the wait function and + * the atomic read check, since only this thread is allowed to reset it to 0. + */ + + old_timeout = wait_event_interruptible_timeout(idev->wait_has_data, + READ_ONCE(idev->has_data), + timeout); + if (idev->rst_state == ISSEI_RST_STATE_DISABLED) + continue; + + if (!READ_ONCE(idev->has_data)) { + dev_warn(&idev->dev, "Timed out at state %d, resetting\n", + idev->rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + } + + return 0; +} + +/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * + * Return: 0 on success, < 0 on failure + */ +int issei_start(struct issei_device *idev) +{ + int ret; + + idev->power_down = false; + + ret = issei_dmam_setup(idev); + if (ret) + return ret; + + idev->ops->irq_clear(idev); + + ret = idev->ops->hw_config(idev); + if (ret) + return ret; + + idev->process_thread = kthread_run(issei_process_thread, idev, + "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->process_thread)) { + ret = PTR_ERR(idev->process_thread); + dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); + return ret; + } + + issei_poke_process_thread(idev); + return 0; +} +EXPORT_SYMBOL_GPL(issei_start); + +/** + * issei_stop - stop interrupts and processing thread. + * @idev: the device structure + */ +void issei_stop(struct issei_device *idev) +{ + idev->power_down = true; + + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + issei_poke_process_thread(idev); + + wait_event_timeout(idev->wait_rst_state, + (idev->rst_state == ISSEI_RST_STATE_DISABLED), + msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC)); + kthread_stop(idev->process_thread); +} +EXPORT_SYMBOL_GPL(issei_stop); diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 index 000000000000..86c567a4bf0f --- /dev/null +++ b/drivers/misc/issei/pci_heci.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "hw_heci.h" +#include "hw_heci_regs.h" + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; + char __iomem *registers; + int err; + + cfg = issei_heci_get_cfg(ent->driver_data); + if (!cfg) + return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + err = pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "failed to enable pci device.\n"); + + pci_set_master(pdev); + + registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(registers)) + return dev_err_probe(dev, PTR_ERR(registers), "failed to get pci region.\n"); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + return dev_err_probe(dev, err, "no usable DMA configuration.\n"); + + idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops()); + if (IS_ERR(idev)) + return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); + + issei_heci_dev_init(idev, registers, cfg); + + pci_set_drvdata(pdev, idev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); + goto deregister; + } + + hw = to_heci_hw(idev); + hw->irq = pci_irq_vector(pdev, 0); + + err = request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_SHARED, KBUILD_MODNAME, idev); + if (err) + goto release_irq; + + err = issei_start(idev); + if (err) { + dev_err_probe(dev, err, "init hw failure.\n"); + goto free_irq; + } + + return 0; + +free_irq: + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); +release_irq: + pci_free_irq_vectors(pdev); +deregister: + issei_deregister(idev); + return err; +} + +static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev = pci_get_drvdata(pdev); + struct issei_heci_hw *hw = to_heci_hw(idev); + + issei_stop(idev); + + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + +static void issei_heci_remove(struct pci_dev *pdev) +{ + issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} + +static int issei_heci_pm_suspend(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + issei_stop(idev); + idev->ops->irq_disable(idev); + + return 0; +} + +static int issei_heci_pm_resume(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return issei_start(idev); +} + +static const struct dev_pm_ops issei_heci_pm_ops = { + SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume) +}; + +static const struct pci_device_id heci_pci_tbl[] = { + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */ + {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ + {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */ + {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + {} +}; +MODULE_DEVICE_TABLE(pci, heci_pci_tbl); + +static struct pci_driver issei_heci_driver = { + .name = KBUILD_MODNAME, + .id_table = heci_pci_tbl, + .probe = issei_heci_probe, + .remove = issei_heci_remove, + .shutdown = issei_heci_shutdown, + .driver = { + .pm = &issei_heci_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_pci_driver(issei_heci_driver); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_SSEI"); diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 6c65fbf22e75..c2ca63e40814 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -887,7 +887,7 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) else return -ENODEV; - ret = pci_enable_device(pci_dev); + ret = pcim_enable_device(pci_dev); if (ret) return ret; pci_set_master(pci_dev); @@ -896,18 +896,15 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) startup.end = startup.start + cp500->devs->startup.size - 1; cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev, &startup); - if (IS_ERR(cp500->system_startup_addr)) { - ret = PTR_ERR(cp500->system_startup_addr); - goto out_disable; - } + if (IS_ERR(cp500->system_startup_addr)) + return PTR_ERR(cp500->system_startup_addr); cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI, CP500_NUM_MSIX, PCI_IRQ_MSIX); if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) { dev_err(&pci_dev->dev, "Hardware does not support enough MSI-X interrupts\n"); - ret = -ENODEV; - goto out_disable; + return -ENODEV; } cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG); @@ -937,9 +934,6 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); -out_disable: - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); return ret; } @@ -962,9 +956,6 @@ static void cp500_remove(struct pci_dev *pci_dev) pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); - - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); } static struct pci_device_id cp500_ids[] = { diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c index fc54afd1d05b..874dd50fac66 100644 --- a/drivers/misc/keba/lan9252.c +++ b/drivers/misc/keba/lan9252.c @@ -339,8 +339,8 @@ static int lan9252_probe(struct spi_device *spi) } static const struct spi_device_id lan9252_id[] = { - {"lan9252"}, - {} + { .name = "lan9252" }, + { } }; MODULE_DEVICE_TABLE(spi, lan9252_id); diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c index 0bb90c0943bf..b0949c653e5b 100644 --- a/drivers/misc/lan966x_pci.c +++ b/drivers/misc/lan966x_pci.c @@ -183,6 +183,7 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return 0; err_unload_overlay: + of_platform_depopulate(dev); lan966x_pci_unload_overlay(data); return ret; } diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 93949df3bcff..740b5cf53842 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi) } static const struct spi_device_id lattice_ecp3_id[] = { - { "ecp3-17", 0 }, - { "ecp3-35", 0 }, + { .name = "ecp3-17" }, + { .name = "ecp3-35" }, { } }; MODULE_DEVICE_TABLE(spi, lattice_ecp3_id); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 15119584473c..7c3ca6d604d1 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -95,7 +95,7 @@ static union axis_conversion lis3lv02d_axis_map = #ifdef CONFIG_OF static const struct of_device_id lis3lv02d_i2c_dt_ids[] = { { .compatible = "st,lis3lv02d" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids); #endif @@ -247,9 +247,9 @@ static int lis3_i2c_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct i2c_device_id lis3lv02d_id[] = { - {"lis3lv02d", LIS3LV02D}, - {"lis331dlh", LIS331DLH}, - {} + { .name = "lis3lv02d", .driver_data = LIS3LV02D }, + { .name = "lis331dlh", .driver_data = LIS331DLH }, + { } }; MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index 203a108b8883..5bf95176a00e 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -60,7 +60,7 @@ static union axis_conversion lis3lv02d_axis_normal = #ifdef CONFIG_OF static const struct of_device_id lis302dl_spi_dt_ids[] = { { .compatible = "st,lis302dl-spi" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); #endif diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index 48bea1117771..b0b678e6b28d 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -150,7 +150,7 @@ static const struct pci_device_id pci1xxxx_tbl[] = { { PCI_DEVICE(0x1055, 0xA035) }, { PCI_DEVICE(0x1055, 0xA045) }, { PCI_DEVICE(0x1055, 0xA055) }, - {0,} + { } }; MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 8eddbaa1fccd..82635db04dc5 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -513,8 +513,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_gpio"}, - {} + { .name = "mchp_pci1xxxx_gp.gp_gpio" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c index a2ed477e0370..09841c4f1a06 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c @@ -419,8 +419,8 @@ static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev) } static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"}, - {}, + { .name = "mchp_pci1xxxx_gp.gp_otp_e2p" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table); diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index 73d5beeb9c34..376840b2c354 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -101,10 +101,8 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev, mei_me_irq_quick_handler, mei_me_irq_thread_handler, IRQF_ONESHOT, KBUILD_MODNAME, dev); - if (ret) { - dev_err(device, "irq register failed %d\n", ret); + if (ret) goto err; - } } ret = mei_register(dev, device); diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c index f6a258c2b838..9fa69acf28d5 100644 --- a/drivers/misc/mei/mei_lb.c +++ b/drivers/misc/mei/mei_lb.c @@ -281,7 +281,7 @@ static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes, bytes, sizeof(rsp->rheader)); return -ENOMSG; } - if (rsp->rheader.header.command_id != MEI_LB2_CMD) { + if (rsp->rheader.header.command_id != cpu_to_le32(MEI_LB2_CMD)) { dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", rsp->rheader.header.command_id, MEI_LB2_CMD); return -EPROTO; @@ -327,7 +327,7 @@ static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *clde if (sent_data + chunk_size == payload_size) last_chunk = MEI_LB2_FLAG_LST_CHUNK; - req->header.command_id = MEI_LB2_CMD; + req->header.command_id = cpu_to_le32(MEI_LB2_CMD); req->type = cpu_to_le32(type); req->flags = cpu_to_le32(flags | first_chunk | last_chunk); req->reserved = 0; diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c index 70792bf9b3c0..6e1e8008f267 100644 --- a/drivers/misc/mei/pci-csc.c +++ b/drivers/misc/mei/pci-csc.c @@ -240,7 +240,7 @@ static const struct dev_pm_ops mei_csc_pm_ops = { static const struct pci_device_id mei_csc_pci_tbl[] = { { PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) }, - {} + { } }; MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 55e0b8a98827..790a4647811b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -27,114 +27,114 @@ /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index f5441bc5efe2..815ca72877ce 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -24,10 +24,10 @@ #include "hw-txe.h" static const struct pci_device_id mei_txe_pci_tbl[] = { - {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ - {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */ + { PCI_VDEVICE(INTEL, 0x0F18) }, /* Baytrail */ + { PCI_VDEVICE(INTEL, 0x2298) }, /* Cherrytrail */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 5100234ba5b6..ef6dcc043862 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -435,7 +435,7 @@ static int mei_vsc_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); static const struct platform_device_id mei_vsc_id_table[] = { - { MEI_VSC_DRV_NAME }, + { .name = MEI_VSC_DRV_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mei_vsc_id_table); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 5ecf99883996..432bc59f11f9 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -552,11 +552,11 @@ static void vsc_tp_remove(struct spi_device *spi) } static const struct acpi_device_id vsc_tp_acpi_ids[] = { - { "INTC1009" }, /* Raptor Lake */ - { "INTC1058" }, /* Tiger Lake */ - { "INTC1094" }, /* Alder Lake */ - { "INTC10D0" }, /* Meteor Lake */ - {} + { .id = "INTC1009" }, /* Raptor Lake */ + { .id = "INTC1058" }, /* Tiger Lake */ + { .id = "INTC1094" }, /* Alder Lake */ + { .id = "INTC10D0" }, /* Meteor Lake */ + { } }; MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c index 7d5433121ff6..73ff0f198ec3 100644 --- a/drivers/misc/mrvl_cn10k_dpi.c +++ b/drivers/misc/mrvl_cn10k_dpi.c @@ -470,10 +470,8 @@ static int dpi_irq_init(struct dpipf *dpi) ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX), dpi_mbox_intr_handler, 0, "dpi-mbox", dpi); - if (ret) { - dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret); + if (ret) return ret; - } dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0)); @@ -658,7 +656,7 @@ static void dpi_remove(struct pci_dev *pdev) static const struct pci_device_id dpi_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF, PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) }, - { 0, } /* end of table */ + { } /* end of table */ }; static struct pci_driver dpi_driver = { diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 3960506eb7ab..a3a9f9bfc6b7 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -61,7 +61,7 @@ struct nsm { /* NSM device ID */ static const struct virtio_device_id id_table[] = { { VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID }, - { 0 }, + { } }; static struct nsm *file_to_nsm(struct file *file) @@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm) goto cleanup; } - msg->resp.len = len; + msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data)); rc = 0; @@ -365,9 +365,8 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd, return -EINVAL; /* Copy user argument struct to kernel argument struct */ - r = -EFAULT; if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) - return r; + return -EFAULT; mutex_lock(&nsm->lock); diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 02c9d1192812..4a805919bb0c 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -875,6 +875,9 @@ static int setup_wait(struct ntsync_device *dev, if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME)) return -EINVAL; + if (!args->owner) + return -EINVAL; + if (size >= sizeof(fds)) return -EINVAL; diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c index cb920aa88d3a..0cf38bd482a9 100644 --- a/drivers/misc/ocxl/pci.c +++ b/drivers/misc/ocxl/pci.c @@ -9,7 +9,7 @@ * vendor/device ID to help differentiate devices. */ static const struct pci_device_id ocxl_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B) }, { } }; MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl); diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 45060fb4ea27..b3cf59954111 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2021 - Google LLC * Author: David Brazdil + * Author: Song Guo * * Driver for Open Profile for DICE. * @@ -19,6 +20,7 @@ * close(fd); */ +#include #include #include #include @@ -31,7 +33,8 @@ struct open_dice_drvdata { struct mutex lock; char name[16]; - struct reserved_mem *rmem; + phys_addr_t mem_base; + resource_size_t mem_size; struct miscdevice misc; }; @@ -45,14 +48,14 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) void *kaddr; mutex_lock(&drvdata->lock); - kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base, - drvdata->rmem->size, MEMREMAP_WC); + kaddr = devm_memremap(drvdata->misc.this_device, drvdata->mem_base, + drvdata->mem_size, MEMREMAP_WC); if (IS_ERR(kaddr)) { mutex_unlock(&drvdata->lock); return PTR_ERR(kaddr); } - memset(kaddr, 0, drvdata->rmem->size); + memset(kaddr, 0, drvdata->mem_size); devm_memunmap(drvdata->misc.this_device, kaddr); mutex_unlock(&drvdata->lock); return 0; @@ -64,7 +67,7 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len, loff_t *off) { - unsigned long val = to_open_dice_drvdata(filp)->rmem->size; + unsigned long val = to_open_dice_drvdata(filp)->mem_size; return simple_read_from_buffer(ptr, len, off, &val, sizeof(val)); } @@ -102,8 +105,8 @@ static int open_dice_mmap_prepare(struct vm_area_desc *desc) /* Create write-combine mapping so all clients observe a wipe. */ desc->page_prot = pgprot_writecombine(desc->page_prot); vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT); - mmap_action_simple_ioremap(desc, drvdata->rmem->base, - drvdata->rmem->size); + mmap_action_simple_ioremap(desc, drvdata->mem_base, + drvdata->mem_size); return 0; } @@ -118,22 +121,40 @@ static int __init open_dice_probe(struct platform_device *pdev) { static unsigned int dev_idx; struct device *dev = &pdev->dev; - struct reserved_mem *rmem; struct open_dice_drvdata *drvdata; + phys_addr_t mem_base; + resource_size_t mem_size; int ret; - rmem = of_reserved_mem_lookup(dev->of_node); - if (!rmem) { - dev_err(dev, "failed to lookup reserved memory\n"); + if (dev->of_node) { + struct reserved_mem *rmem = of_reserved_mem_lookup(dev->of_node); + + if (!rmem) { + dev_err(dev, "failed to lookup reserved memory\n"); + return -EINVAL; + } + mem_base = rmem->base; + mem_size = rmem->size; + } else if (is_acpi_node(dev->fwnode)) { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(dev, "failed to get MMIO resource\n"); + return -EINVAL; + } + mem_base = res->start; + mem_size = resource_size(res); + } else { + dev_err(dev, "device not supported (no DT or ACPI node)\n"); return -EINVAL; } - if (!rmem->size || (rmem->size > ULONG_MAX)) { + if (!mem_size || (mem_size > ULONG_MAX)) { dev_err(dev, "invalid memory region size\n"); return -EINVAL; } - if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) { + if (!PAGE_ALIGNED(mem_base) || !PAGE_ALIGNED(mem_size)) { dev_err(dev, "memory region must be page-aligned\n"); return -EINVAL; } @@ -143,7 +164,8 @@ static int __init open_dice_probe(struct platform_device *pdev) return -ENOMEM; *drvdata = (struct open_dice_drvdata){ - .rmem = rmem, + .mem_base = mem_base, + .mem_size = mem_size, .misc = (struct miscdevice){ .parent = dev, .name = drvdata->name, @@ -177,7 +199,7 @@ static void open_dice_remove(struct platform_device *pdev) static const struct of_device_id open_dice_of_match[] = { { .compatible = "google,open-dice" }, - {}, + { } }; static struct platform_driver open_dice_driver = { @@ -207,3 +229,4 @@ module_exit(open_dice_exit); MODULE_DESCRIPTION("Driver for Open Profile for DICE."); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Brazdil "); +MODULE_AUTHOR("Song Guo "); diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 19c4fa017f24..15785597da40 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -83,22 +83,16 @@ #define PCH_PHUB_OROM_SIZE 15360 +enum pch_phub_type { + PCH_EG20T, + PCH_ML7213, + PCH_ML7223M, + PCH_ML7223N, + PCH_ML7831, +}; + /** * struct pch_phub_reg - PHUB register structure - * @phub_id_reg: PHUB_ID register val - * @q_pri_val_reg: QUEUE_PRI_VAL register val - * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val - * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val - * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val - * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val - * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val - * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val - * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val - * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val - * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val - * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val - * @clkcfg_reg: CLK CFG register val - * @funcsel_reg: Function select register value * @pch_phub_base_address: Register base address * @pch_phub_extrom_base_address: external rom base address * @pch_mac_start_address: MAC address area start address @@ -107,25 +101,11 @@ * @pdev: pointer to pci device struct */ struct pch_phub_reg { - u32 phub_id_reg; - u32 q_pri_val_reg; - u32 rc_q_maxsize_reg; - u32 bri_q_maxsize_reg; - u32 comp_resp_timeout_reg; - u32 bus_slave_control_reg; - u32 deadlock_avoid_type_reg; - u32 intpin_reg_wpermit_reg0; - u32 intpin_reg_wpermit_reg1; - u32 intpin_reg_wpermit_reg2; - u32 intpin_reg_wpermit_reg3; - u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG]; - u32 clkcfg_reg; - u32 funcsel_reg; void __iomem *pch_phub_base_address; void __iomem *pch_phub_extrom_base_address; u32 pch_mac_start_address; u32 pch_opt_rom_start_address; - int ioh_type; + enum pch_phub_type ioh_type; struct pci_dev *pdev; }; @@ -344,10 +324,19 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) int retval; int i; - if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/ + switch (chip->ioh_type) { + case PCH_EG20T: + case PCH_ML7831: retval = pch_phub_gbe_serial_rom_conf(chip); - else /* ML7223 */ + break; + + case PCH_ML7213: + case PCH_ML7223M: + case PCH_ML7223N: retval = pch_phub_gbe_serial_rom_conf_mp(chip); + break; + } + if (retval) return retval; @@ -537,14 +526,6 @@ static const struct bin_attribute pch_bin_attr = { .write = pch_phub_bin_write, }; -enum { - PCH_EG20T, - PCH_ML7213, - PCH_ML7223M, - PCH_ML7223N, - PCH_ML7831, -}; - static int pch_phub_probe(struct pci_dev *pdev, const struct pci_device_id *id) { diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 7686d777a27e..6b18ea914689 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1419,18 +1419,18 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0), .driver_data = (kernel_ulong_t)&default_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),}, + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8) }, { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A), .driver_data = (kernel_ulong_t)&default_data, }, { PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) }, { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654), - .driver_data = (kernel_ulong_t)&am654_data + .driver_data = (kernel_ulong_t)&am654_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),}, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1) }, { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0), .driver_data = (kernel_ulong_t)&default_data, }, @@ -1449,8 +1449,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588), .driver_data = (kernel_ulong_t)&rk3588_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),}, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),}, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP) }, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 34a5054a6b40..331cae539290 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -481,10 +481,10 @@ static int __maybe_unused phantom_resume(struct device *dev_d) } static struct pci_device_id phantom_pci_tbl[] = { - { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index bedcda9b6ac5..724dfddf69e4 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -51,14 +51,14 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) } static const struct of_device_id pvpanic_mmio_match[] = { - { .compatible = "qemu,pvpanic-mmio", }, - {} + { .compatible = "qemu,pvpanic-mmio" }, + { } }; MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); static const struct acpi_device_id pvpanic_device_ids[] = { - { "QEMU0001", 0 }, - { "", 0 } + { .id = "QEMU0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index b21598a18f6d..572c6148a297 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -35,8 +35,8 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id pvpanic_pci_id_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, - {} + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC) }, + { } }; MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl); diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c index 3c57f7429147..b58bd34fc471 100644 --- a/drivers/misc/qcom-coincell.c +++ b/drivers/misc/qcom-coincell.c @@ -124,8 +124,8 @@ static int qcom_coincell_probe(struct platform_device *pdev) } static const struct of_device_id qcom_coincell_match_table[] = { - { .compatible = "qcom,pm8941-coincell", }, - {} + { .compatible = "qcom,pm8941-coincell" }, + { } }; MODULE_DEVICE_TABLE(of, qcom_coincell_match_table); diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index 81685e3f3296..207a676913a2 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -166,6 +166,9 @@ static void rp1_unregister_interrupts(struct pci_dev *pdev) struct rp1_dev *rp1 = pci_get_drvdata(pdev); int irq, i; + for (i = 0; i < RP1_INT_END; i++) + irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), NULL, NULL); + if (rp1->domain) { for (i = 0; i < RP1_INT_END; i++) { irq = irq_find_mapping(rp1->domain, i); @@ -191,13 +194,13 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!rp1_node) { dev_err(dev, "Missing of_node for device\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); if (!rp1) { err = -ENOMEM; - goto err_put_node; + goto err_out; } rp1->pdev = pdev; @@ -206,21 +209,21 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(&pdev->dev, "Not initialized - is the firmware running?\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } err = pcim_enable_device(pdev); if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Enabling PCI device has failed"); - goto err_put_node; + goto err_out; } rp1->bar1 = pcim_iomap(pdev, 1, 0); if (!rp1->bar1) { dev_err(&pdev->dev, "Cannot map PCI BAR\n"); err = -EIO; - goto err_put_node; + goto err_out; } pci_set_master(pdev); @@ -230,16 +233,16 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Failed to allocate MSI-X vectors\n"); - goto err_put_node; + goto err_out; } else if (err != RP1_INT_END) { dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } pci_set_drvdata(pdev, rp1); - rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, - &rp1_domain_ops, rp1); + rp1->domain = irq_domain_create_linear(of_fwnode_handle(rp1_node), RP1_INT_END, + &rp1_domain_ops, rp1); if (!rp1->domain) { dev_err(&pdev->dev, "Error creating IRQ domain\n"); err = -ENOMEM; @@ -267,15 +270,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_unregister_interrupts; } - of_node_put(rp1_node); - return 0; err_unregister_interrupts: rp1_unregister_interrupts(pdev); -err_put_node: - of_node_put(rp1_node); - +err_out: return err; } @@ -288,7 +287,7 @@ static void rp1_remove(struct pci_dev *pdev) } static const struct pci_device_id dev_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0) }, { } }; MODULE_DEVICE_TABLE(pci, dev_id_table); diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile deleted file mode 100644 index 8132116ec0f0..000000000000 --- a/drivers/misc/sgi-gru/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ccflags-$(CONFIG_SGI_GRU_DEBUG) := -DDEBUG - -obj-$(CONFIG_SGI_GRU) := gru.o -gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o - diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h deleted file mode 100644 index 6ae045037219..000000000000 --- a/drivers/misc/sgi-gru/gru.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_H__ -#define __GRU_H__ - -/* - * GRU architectural definitions - */ -#define GRU_CACHE_LINE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 -#define GRU_CB_BASE 0 -#define GRU_DS_BASE 0x20000 - -/* - * Size used to map GRU GSeg - */ -#if defined(CONFIG_X86_64) -#define GRU_GSEG_PAGESIZE (256 * 1024UL) /* ZZZ 2MB ??? */ -#else -#error "Unsupported architecture" -#endif - -/* - * Structure for obtaining GRU resource information - */ -struct gru_chiplet_info { - int node; - int chiplet; - int blade; - int total_dsr_bytes; - int total_cbr; - int total_user_dsr_bytes; - int total_user_cbr; - int free_user_dsr_bytes; - int free_user_cbr; -}; - -/* - * Statictics kept for each context. - */ -struct gru_gseg_statistics { - unsigned long fmm_tlbmiss; - unsigned long upm_tlbmiss; - unsigned long tlbdropin; - unsigned long context_stolen; - unsigned long reserved[10]; -}; - -/* Flags for GRU options on the gru_create_context() call */ -/* Select one of the follow 4 options to specify how TLB misses are handled */ -#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */ -#define GRU_OPT_MISS_USER_POLL 0x0001 /* User will poll CB for faults */ -#define GRU_OPT_MISS_FMM_INTR 0x0002 /* Send interrupt to cpu to - handle fault */ -#define GRU_OPT_MISS_FMM_POLL 0x0003 /* Use system polling thread */ -#define GRU_OPT_MISS_MASK 0x0003 /* Mask for TLB MISS option */ - - - -#endif /* __GRU_H__ */ diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h deleted file mode 100644 index da5eb9edf9ec..000000000000 --- a/drivers/misc/sgi-gru/gru_instructions.h +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_INSTRUCTIONS_H__ -#define __GRU_INSTRUCTIONS_H__ - -extern int gru_check_status_proc(void *cb); -extern int gru_wait_proc(void *cb); -extern void gru_wait_abort_proc(void *cb); - - - -/* - * Architecture dependent functions - */ - -#if defined(CONFIG_X86_64) -#include -#define __flush_cache(p) clflush(p) -#define gru_ordered_store_ulong(p, v) \ - do { \ - barrier(); \ - *(unsigned long *)p = v; \ - } while (0) -#else -#error "Unsupported architecture" -#endif - -/* - * Control block status and exception codes - */ -#define CBS_IDLE 0 -#define CBS_EXCEPTION 1 -#define CBS_ACTIVE 2 -#define CBS_CALL_OS 3 - -/* CB substatus bitmasks */ -#define CBSS_MSG_QUEUE_MASK 7 -#define CBSS_IMPLICIT_ABORT_ACTIVE_MASK 8 - -/* CB substatus message queue values (low 3 bits of substatus) */ -#define CBSS_NO_ERROR 0 -#define CBSS_LB_OVERFLOWED 1 -#define CBSS_QLIMIT_REACHED 2 -#define CBSS_PAGE_OVERFLOW 3 -#define CBSS_AMO_NACKED 4 -#define CBSS_PUT_NACKED 5 - -/* - * Structure used to fetch exception detail for CBs that terminate with - * CBS_EXCEPTION - */ -struct control_block_extended_exc_detail { - unsigned long cb; - int opc; - int ecause; - int exopc; - long exceptdet0; - int exceptdet1; - int cbrstate; - int cbrexecstatus; -}; - -/* - * Instruction formats - */ - -/* - * Generic instruction format. - * This definition has precise bit field definitions. - */ -struct gru_instruction_bits { - /* DW 0 - low */ - unsigned int icmd: 1; - unsigned char ima: 3; /* CB_DelRep, unmapped mode */ - unsigned char reserved0: 4; - unsigned int xtype: 3; - unsigned int iaa0: 2; - unsigned int iaa1: 2; - unsigned char reserved1: 1; - unsigned char opc: 8; /* opcode */ - unsigned char exopc: 8; /* extended opcode */ - /* DW 0 - high */ - unsigned int idef2: 22; /* TRi0 */ - unsigned char reserved2: 2; - unsigned char istatus: 2; - unsigned char isubstatus:4; - unsigned char reserved3: 1; - unsigned char tlb_fault_color: 1; - /* DW 1 */ - unsigned long idef4; /* 42 bits: TRi1, BufSize */ - /* DW 2-6 */ - unsigned long idef1; /* BAddr0 */ - unsigned long idef5; /* Nelem */ - unsigned long idef6; /* Stride, Operand1 */ - unsigned long idef3; /* BAddr1, Value, Operand2 */ - unsigned long reserved4; - /* DW 7 */ - unsigned long avalue; /* AValue */ -}; - -/* - * Generic instruction with friendlier names. This format is used - * for inline instructions. - */ -struct gru_instruction { - /* DW 0 */ - union { - unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */ - struct { - unsigned int op32; - unsigned int tri0; - }; - }; - unsigned long tri1_bufsize; /* DW 1 */ - unsigned long baddr0; /* DW 2 */ - unsigned long nelem; /* DW 3 */ - unsigned long op1_stride; /* DW 4 */ - unsigned long op2_value_baddr1; /* DW 5 */ - unsigned long reserved0; /* DW 6 */ - unsigned long avalue; /* DW 7 */ -}; - -/* Some shifts and masks for the low 64 bits of a GRU command */ -#define GRU_CB_ICMD_SHFT 0 -#define GRU_CB_ICMD_MASK 0x1 -#define GRU_CB_XTYPE_SHFT 8 -#define GRU_CB_XTYPE_MASK 0x7 -#define GRU_CB_IAA0_SHFT 11 -#define GRU_CB_IAA0_MASK 0x3 -#define GRU_CB_IAA1_SHFT 13 -#define GRU_CB_IAA1_MASK 0x3 -#define GRU_CB_IMA_SHFT 1 -#define GRU_CB_IMA_MASK 0x3 -#define GRU_CB_OPC_SHFT 16 -#define GRU_CB_OPC_MASK 0xff -#define GRU_CB_EXOPC_SHFT 24 -#define GRU_CB_EXOPC_MASK 0xff -#define GRU_IDEF2_SHFT 32 -#define GRU_IDEF2_MASK 0x3ffff -#define GRU_ISTATUS_SHFT 56 -#define GRU_ISTATUS_MASK 0x3 - -/* GRU instruction opcodes (opc field) */ -#define OP_NOP 0x00 -#define OP_BCOPY 0x01 -#define OP_VLOAD 0x02 -#define OP_IVLOAD 0x03 -#define OP_VSTORE 0x04 -#define OP_IVSTORE 0x05 -#define OP_VSET 0x06 -#define OP_IVSET 0x07 -#define OP_MESQ 0x08 -#define OP_GAMXR 0x09 -#define OP_GAMIR 0x0a -#define OP_GAMIRR 0x0b -#define OP_GAMER 0x0c -#define OP_GAMERR 0x0d -#define OP_BSTORE 0x0e -#define OP_VFLUSH 0x0f - - -/* Extended opcodes values (exopc field) */ - -/* GAMIR - AMOs with implicit operands */ -#define EOP_IR_FETCH 0x01 /* Plain fetch of memory */ -#define EOP_IR_CLR 0x02 /* Fetch and clear */ -#define EOP_IR_INC 0x05 /* Fetch and increment */ -#define EOP_IR_DEC 0x07 /* Fetch and decrement */ -#define EOP_IR_QCHK1 0x0d /* Queue check, 64 byte msg */ -#define EOP_IR_QCHK2 0x0e /* Queue check, 128 byte msg */ - -/* GAMIRR - Registered AMOs with implicit operands */ -#define EOP_IRR_FETCH 0x01 /* Registered fetch of memory */ -#define EOP_IRR_CLR 0x02 /* Registered fetch and clear */ -#define EOP_IRR_INC 0x05 /* Registered fetch and increment */ -#define EOP_IRR_DEC 0x07 /* Registered fetch and decrement */ -#define EOP_IRR_DECZ 0x0f /* Registered fetch and decrement, update on zero*/ - -/* GAMER - AMOs with explicit operands */ -#define EOP_ER_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ER_OR 0x01 /* Logical OR with memory */ -#define EOP_ER_AND 0x02 /* Logical AND with memory */ -#define EOP_ER_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ER_ADD 0x04 /* Add value to memory */ -#define EOP_ER_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ER_CADD 0x0c /* Queue check, operand1*64 byte msg */ - -/* GAMERR - Registered AMOs with explicit operands */ -#define EOP_ERR_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ERR_OR 0x01 /* Logical OR with memory */ -#define EOP_ERR_AND 0x02 /* Logical AND with memory */ -#define EOP_ERR_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ERR_ADD 0x04 /* Add value to memory */ -#define EOP_ERR_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ERR_EPOLL 0x09 /* Poll for equality */ -#define EOP_ERR_NPOLL 0x0a /* Poll for inequality */ - -/* GAMXR - SGI Arithmetic unit */ -#define EOP_XR_CSWAP 0x0b /* Masked compare exchange */ - - -/* Transfer types (xtype field) */ -#define XTYPE_B 0x0 /* byte */ -#define XTYPE_S 0x1 /* short (2-byte) */ -#define XTYPE_W 0x2 /* word (4-byte) */ -#define XTYPE_DW 0x3 /* doubleword (8-byte) */ -#define XTYPE_CL 0x6 /* cacheline (64-byte) */ - - -/* Instruction access attributes (iaa0, iaa1 fields) */ -#define IAA_RAM 0x0 /* normal cached RAM access */ -#define IAA_NCRAM 0x2 /* noncoherent RAM access */ -#define IAA_MMIO 0x1 /* noncoherent memory-mapped I/O space */ -#define IAA_REGISTER 0x3 /* memory-mapped registers, etc. */ - - -/* Instruction mode attributes (ima field) */ -#define IMA_MAPPED 0x0 /* Virtual mode */ -#define IMA_CB_DELAY 0x1 /* hold read responses until status changes */ -#define IMA_UNMAPPED 0x2 /* bypass the TLBs (OS only) */ -#define IMA_INTERRUPT 0x4 /* Interrupt when instruction completes */ - -/* CBE ecause bits */ -#define CBE_CAUSE_RI (1 << 0) -#define CBE_CAUSE_INVALID_INSTRUCTION (1 << 1) -#define CBE_CAUSE_UNMAPPED_MODE_FORBIDDEN (1 << 2) -#define CBE_CAUSE_PE_CHECK_DATA_ERROR (1 << 3) -#define CBE_CAUSE_IAA_GAA_MISMATCH (1 << 4) -#define CBE_CAUSE_DATA_SEGMENT_LIMIT_EXCEPTION (1 << 5) -#define CBE_CAUSE_OS_FATAL_TLB_FAULT (1 << 6) -#define CBE_CAUSE_EXECUTION_HW_ERROR (1 << 7) -#define CBE_CAUSE_TLBHW_ERROR (1 << 8) -#define CBE_CAUSE_RA_REQUEST_TIMEOUT (1 << 9) -#define CBE_CAUSE_HA_REQUEST_TIMEOUT (1 << 10) -#define CBE_CAUSE_RA_RESPONSE_FATAL (1 << 11) -#define CBE_CAUSE_RA_RESPONSE_NON_FATAL (1 << 12) -#define CBE_CAUSE_HA_RESPONSE_FATAL (1 << 13) -#define CBE_CAUSE_HA_RESPONSE_NON_FATAL (1 << 14) -#define CBE_CAUSE_ADDRESS_SPACE_DECODE_ERROR (1 << 15) -#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16) -#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17) -#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18) -#define CBE_CAUSE_FORCED_ERROR (1 << 19) - -/* CBE cbrexecstatus bits */ -#define CBR_EXS_ABORT_OCC_BIT 0 -#define CBR_EXS_INT_OCC_BIT 1 -#define CBR_EXS_PENDING_BIT 2 -#define CBR_EXS_QUEUED_BIT 3 -#define CBR_EXS_TLB_INVAL_BIT 4 -#define CBR_EXS_EXCEPTION_BIT 5 -#define CBR_EXS_CB_INT_PENDING_BIT 6 - -#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT) -#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT) -#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT) -#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT) -#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT) -#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT) -#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT) - -/* - * Exceptions are retried for the following cases. If any OTHER bits are set - * in ecause, the exception is not retryable. - */ -#define EXCEPTION_RETRY_BITS (CBE_CAUSE_EXECUTION_HW_ERROR | \ - CBE_CAUSE_TLBHW_ERROR | \ - CBE_CAUSE_RA_REQUEST_TIMEOUT | \ - CBE_CAUSE_RA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_HA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_RA_RESPONSE_DATA_ERROR | \ - CBE_CAUSE_HA_RESPONSE_DATA_ERROR \ - ) - -/* Message queue head structure */ -union gru_mesqhead { - unsigned long val; - struct { - unsigned int head; - unsigned int limit; - }; -}; - - -/* Generate the low word of a GRU instruction */ -static inline unsigned long -__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype, - unsigned char iaa0, unsigned char iaa1, - unsigned long idef2, unsigned char ima) -{ - return (1 << GRU_CB_ICMD_SHFT) | - ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) | - (idef2<< GRU_IDEF2_SHFT) | - (iaa0 << GRU_CB_IAA0_SHFT) | - (iaa1 << GRU_CB_IAA1_SHFT) | - (ima << GRU_CB_IMA_SHFT) | - (xtype << GRU_CB_XTYPE_SHFT) | - (opcode << GRU_CB_OPC_SHFT) | - (exopc << GRU_CB_EXOPC_SHFT); -} - -/* - * Architecture specific intrinsics - */ -static inline void gru_flush_cache(void *p) -{ - __flush_cache(p); -} - -/* - * Store the lower 64 bits of the command including the "start" bit. Then - * start the instruction executing. - */ -static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64) -{ - gru_ordered_store_ulong(ins, op64); - mb(); - gru_flush_cache(ins); -} - - -/* Convert "hints" to IMA */ -#define CB_IMA(h) ((h) | IMA_UNMAPPED) - -/* Convert data segment cache line index into TRI0 / TRI1 value */ -#define GRU_DINDEX(i) ((i) * GRU_CACHE_LINE_BYTES) - -/* Inline functions for GRU instructions. - * Note: - * - nelem and stride are in elements - * - tri0/tri1 is in bytes for the beginning of the data segment. - */ -static inline void gru_vload_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, - unsigned char xtype, unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_vset(void *cb, unsigned long mem_addr, - unsigned long value, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_ivset(void *cb, unsigned long mem_addr, - unsigned int tri1, unsigned long value, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_vflush(void *cb, unsigned long mem_addr, - unsigned long nelem, unsigned char xtype, unsigned long stride, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op1_stride = stride; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_nop(void *cb, int hints) -{ - struct gru_instruction *ins = (void *)cb; - - gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints))); -} - - -static inline void gru_bcopy(void *cb, const unsigned long src, - unsigned long dest, - unsigned int tri0, unsigned int xtype, unsigned long nelem, - unsigned int bufsize, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - ins->tri1_bufsize = bufsize; - gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM, - IAA_RAM, tri0, CB_IMA(hints))); -} - -static inline void gru_bstore(void *cb, const unsigned long src, - unsigned long dest, unsigned int tri0, unsigned int xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM, - tri0, CB_IMA(hints))); -} - -static inline void gru_gamir(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamirr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamer(void *cb, int exopc, unsigned long src, - unsigned int xtype, - unsigned long operand1, unsigned long operand2, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamerr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long operand1, - unsigned long operand2, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamxr(void *cb, unsigned long src, - unsigned int tri0, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->nelem = 4; - gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW, - IAA_RAM, 0, 0, CB_IMA(hints))); -} - -static inline void gru_mesq(void *cb, unsigned long queue, - unsigned long tri0, unsigned long nelem, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)queue; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline unsigned long gru_get_amo_value(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue; -} - -static inline int gru_get_amo_value_head(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue & 0xffffffff; -} - -static inline int gru_get_amo_value_limit(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue >> 32; -} - -static inline union gru_mesqhead gru_mesq_head(int head, int limit) -{ - union gru_mesqhead mqh; - - mqh.head = head; - mqh.limit = limit; - return mqh; -} - -/* - * Get struct control_block_extended_exc_detail for CB. - */ -extern int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet); - -#define GRU_EXC_STR_SIZE 256 - - -/* - * Control block definition for checking status - */ -struct gru_control_block_status { - unsigned int icmd :1; - unsigned int ima :3; - unsigned int reserved0 :4; - unsigned int unused1 :24; - unsigned int unused2 :24; - unsigned int istatus :2; - unsigned int isubstatus :4; - unsigned int unused3 :2; -}; - -/* Get CB status */ -static inline int gru_get_cb_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->istatus; -} - -/* Get CB message queue substatus */ -static inline int gru_get_cb_message_queue_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus & CBSS_MSG_QUEUE_MASK; -} - -/* Get CB substatus */ -static inline int gru_get_cb_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus; -} - -/* - * User interface to check an instruction status. UPM and exceptions - * are handled automatically. However, this function does NOT wait - * for an active instruction to complete. - * - */ -static inline int gru_check_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - int ret; - - ret = cbs->istatus; - if (ret != CBS_ACTIVE) - ret = gru_check_status_proc(cb); - return ret; -} - -/* - * User interface (via inline function) to wait for an instruction - * to complete. Completion status (IDLE or EXCEPTION is returned - * to the user. Exception due to hardware errors are automatically - * retried before returning an exception. - * - */ -static inline int gru_wait(void *cb) -{ - return gru_wait_proc(cb); -} - -/* - * Wait for CB to complete. Aborts program if error. (Note: error does NOT - * mean TLB mis - only fatal errors such as memory parity error or user - * bugs will cause termination. - */ -static inline void gru_wait_abort(void *cb) -{ - gru_wait_abort_proc(cb); -} - -/* - * Get a pointer to the start of a gseg - * p - Any valid pointer within the gseg - */ -static inline void *gru_get_gseg_pointer (void *p) -{ - return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1)); -} - -/* - * Get a pointer to a control block - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired CB - */ -static inline void *gru_get_cb_pointer(void *gseg, - int index) -{ - return gseg + GRU_CB_BASE + index * GRU_HANDLE_STRIDE; -} - -/* - * Get a pointer to a cacheline in the data segment portion of a GSeg - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired cache line - */ -static inline void *gru_get_data_pointer(void *gseg, int index) -{ - return gseg + GRU_DS_BASE + index * GRU_CACHE_LINE_BYTES; -} - -/* - * Convert a vaddr into the tri index within the GSEG - * vaddr - virtual address of within gseg - */ -static inline int gru_get_tri(void *vaddr) -{ - return ((unsigned long)vaddr & (GRU_GSEG_PAGESIZE - 1)) - GRU_DS_BASE; -} -#endif /* __GRU_INSTRUCTIONS_H__ */ diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c deleted file mode 100644 index 3557d78ee47a..000000000000 --- a/drivers/misc/sgi-gru/grufault.c +++ /dev/null @@ -1,903 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FAULT HANDLER FOR GRU DETECTED TLB MISSES - * - * This file contains code that handles TLB misses within the GRU. - * These misses are reported either via interrupts or user polling of - * the user CB. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include "grulib.h" -#include "gru_instructions.h" -#include - -/* Return codes for vtop functions */ -#define VTOP_SUCCESS 0 -#define VTOP_INVALID -1 -#define VTOP_RETRY -2 - - -/* - * Test if a physical address is a valid GRU GSEG address - */ -static inline int is_gru_paddr(unsigned long paddr) -{ - return paddr >= gru_start_paddr && paddr < gru_end_paddr; -} - -/* - * Find the vma of a GRU segment. Caller must hold mmap_lock. - */ -struct vm_area_struct *gru_find_vma(unsigned long vaddr) -{ - struct vm_area_struct *vma; - - vma = vma_lookup(current->mm, vaddr); - if (vma && vma->vm_ops == &gru_vm_ops) - return vma; - return NULL; -} - -/* - * Find and lock the gts that contains the specified user vaddr. - * - * Returns: - * - *gts with the mmap_lock locked for read and the GTS locked. - * - NULL if vaddr invalid OR is not a valid GSEG vaddr. - */ - -static struct gru_thread_state *gru_find_lock_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = NULL; - - mmap_read_lock(mm); - vma = gru_find_vma(vaddr); - if (vma) - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (gts) - mutex_lock(>s->ts_ctxlock); - else - mmap_read_unlock(mm); - return gts; -} - -static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = ERR_PTR(-EINVAL); - - mmap_write_lock(mm); - vma = gru_find_vma(vaddr); - if (!vma) - goto err; - - gts = gru_alloc_thread_state(vma, TSID(vaddr, vma)); - if (IS_ERR(gts)) - goto err; - mutex_lock(>s->ts_ctxlock); - mmap_write_downgrade(mm); - return gts; - -err: - mmap_write_unlock(mm); - return gts; -} - -/* - * Unlock a GTS that was previously locked with gru_find_lock_gts(). - */ -static void gru_unlock_gts(struct gru_thread_state *gts) -{ - mutex_unlock(>s->ts_ctxlock); - mmap_read_unlock(current->mm); -} - -/* - * Set a CB.istatus to active using a user virtual address. This must be done - * just prior to a TFH RESTART. The new cb.istatus is an in-cache status ONLY. - * If the line is evicted, the status may be lost. The in-cache update - * is necessary to prevent the user from seeing a stale cb.istatus that will - * change as soon as the TFH restart is complete. Races may cause an - * occasional failure to clear the cb.istatus, but that is ok. - */ -static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk) -{ - if (cbk) { - cbk->istatus = CBS_ACTIVE; - } -} - -/* - * Read & clear a TFM - * - * The GRU has an array of fault maps. A map is private to a cpu - * Only one cpu will be accessing a cpu's fault map. - * - * This function scans the cpu-private fault map & clears all bits that - * are set. The function returns a bitmap that indicates the bits that - * were cleared. Note that sense the maps may be updated asynchronously by - * the GRU, atomic operations must be used to clear bits. - */ -static void get_clear_fault_map(struct gru_state *gru, - struct gru_tlb_fault_map *imap, - struct gru_tlb_fault_map *dmap) -{ - unsigned long i, k; - struct gru_tlb_fault_map *tfm; - - tfm = get_tfm_for_cpu(gru, gru_cpu_fault_map_id()); - prefetchw(tfm); /* Helps on hardware, required for emulator */ - for (i = 0; i < BITS_TO_LONGS(GRU_NUM_CBE); i++) { - k = tfm->fault_bits[i]; - if (k) - k = xchg(&tfm->fault_bits[i], 0UL); - imap->fault_bits[i] = k; - k = tfm->done_bits[i]; - if (k) - k = xchg(&tfm->done_bits[i], 0UL); - dmap->fault_bits[i] = k; - } - - /* - * Not functionally required but helps performance. (Required - * on emulator) - */ - gru_flush_cache(tfm); -} - -/* - * Atomic (interrupt context) & non-atomic (user context) functions to - * convert a vaddr into a physical address. The size of the page - * is returned in pageshift. - * returns: - * 0 - successful - * < 0 - error code - * 1 - (atomic only) try again in non-atomic context - */ -static int non_atomic_pte_lookup(struct vm_area_struct *vma, - unsigned long vaddr, int write, - unsigned long *paddr, int *pageshift) -{ - struct page *page; - -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page) <= 0) - return -EFAULT; - *paddr = page_to_phys(page); - put_page(page); - return 0; -} - -/* - * atomic_pte_lookup - * - * Convert a user virtual address to a physical address - * Only supports Intel large pages (2MB only) on x86_64. - * ZZZ - hugepage support is incomplete - * - * NOTE: mmap_lock is already held on entry to this function. This - * guarantees existence of the page tables. - */ -static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, - int write, unsigned long *paddr, int *pageshift) -{ - pgd_t *pgdp; - p4d_t *p4dp; - pud_t *pudp; - pmd_t *pmdp; - pte_t pte; - - pgdp = pgd_offset(vma->vm_mm, vaddr); - if (unlikely(pgd_none(*pgdp))) - goto err; - - p4dp = p4d_offset(pgdp, vaddr); - if (unlikely(p4d_none(*p4dp))) - goto err; - - pudp = pud_offset(p4dp, vaddr); - if (unlikely(pud_none(*pudp))) - goto err; - - pmdp = pmd_offset(pudp, vaddr); - if (unlikely(pmd_none(*pmdp))) - goto err; -#ifdef CONFIG_X86_64 - if (unlikely(pmd_leaf(*pmdp))) - pte = ptep_get((pte_t *)pmdp); - else -#endif - pte = *pte_offset_kernel(pmdp, vaddr); - - if (unlikely(!pte_present(pte) || - (write && (!pte_write(pte) || !pte_dirty(pte))))) - return 1; - - *paddr = pte_pfn(pte) << PAGE_SHIFT; -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - return 0; - -err: - return 1; -} - -static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, - int write, int atomic, unsigned long *gpa, int *pageshift) -{ - struct mm_struct *mm = gts->ts_mm; - struct vm_area_struct *vma; - unsigned long paddr; - int ret, ps; - - vma = find_vma(mm, vaddr); - if (!vma) - goto inval; - - /* - * Atomic lookup is faster & usually works even if called in non-atomic - * context. - */ - rmb(); /* Must/check ms_range_active before loading PTEs */ - ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps); - if (ret) { - if (atomic) - goto upm; - if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) - goto inval; - } - if (is_gru_paddr(paddr)) - goto inval; - paddr = paddr & ~((1UL << ps) - 1); - *gpa = uv_soc_phys_ram_to_gpa(paddr); - *pageshift = ps; - return VTOP_SUCCESS; - -inval: - return VTOP_INVALID; -upm: - return VTOP_RETRY; -} - - -/* - * Flush a CBE from cache. The CBE is clean in the cache. Dirty the - * CBE cacheline so that the line will be written back to home agent. - * Otherwise the line may be silently dropped. This has no impact - * except on performance. - */ -static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) -{ - if (unlikely(cbe)) { - cbe->cbrexecstatus = 0; /* make CL dirty */ - gru_flush_cache(cbe); - } -} - -/* - * Preload the TLB with entries that may be required. Currently, preloading - * is implemented only for BCOPY. Preload pages OR to - * the end of the bcopy tranfer, whichever is smaller. - */ -static void gru_preload_tlb(struct gru_state *gru, - struct gru_thread_state *gts, int atomic, - unsigned long fault_vaddr, int asid, int write, - unsigned char tlb_preload_count, - struct gru_tlb_fault_handle *tfh, - struct gru_control_block_extended *cbe) -{ - unsigned long vaddr = 0, gpa; - int ret, pageshift; - - if (cbe->opccpy != OP_BCOPY) - return; - - if (fault_vaddr == cbe->cbe_baddr0) - vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1; - else if (fault_vaddr == cbe->cbe_baddr1) - vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1; - - fault_vaddr &= PAGE_MASK; - vaddr &= PAGE_MASK; - vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); - - while (vaddr > fault_vaddr) { - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); - if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift))) - return; - gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, - vaddr, asid, write, pageshift, gpa); - vaddr -= PAGE_SIZE; - STAT(tlb_preload_page); - } -} - -/* - * Drop a TLB entry into the GRU. The fault is described by info in an TFH. - * Input: - * cb Address of user CBR. Null if not running in user context - * Return: - * 0 = dropin, exception, or switch to UPM successful - * 1 = range invalidate active - * < 0 = error code - * - */ -static int gru_try_dropin(struct gru_state *gru, - struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - struct gru_instruction_bits *cbk) -{ - struct gru_control_block_extended *cbe = NULL; - unsigned char tlb_preload_count = gts->ts_tlb_preload_count; - int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; - unsigned long gpa = 0, vaddr = 0; - - /* - * NOTE: The GRU contains magic hardware that eliminates races between - * TLB invalidates and TLB dropins. If an invalidate occurs - * in the window between reading the TFH and the subsequent TLB dropin, - * the dropin is ignored. This eliminates the need for additional locks. - */ - - /* - * Prefetch the CBE if doing TLB preloading - */ - if (unlikely(tlb_preload_count)) { - cbe = gru_tfh_to_cbe(tfh); - prefetchw(cbe); - } - - /* - * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. - * Might be a hardware race OR a stupid user. Ignore FMM because FMM - * is a transient state. - */ - if (tfh->status != TFHSTATUS_EXCEPTION) { - gru_flush_cache(tfh); - sync_core(); - if (tfh->status != TFHSTATUS_EXCEPTION) - goto failnoexception; - STAT(tfh_stale_on_fault); - } - if (tfh->state == TFHSTATE_IDLE) - goto failidle; - if (tfh->state == TFHSTATE_MISS_FMM && cbk) - goto failfmm; - - write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; - vaddr = tfh->missvaddr; - asid = tfh->missasid; - indexway = tfh->indexway; - if (asid == 0) - goto failnoasid; - - rmb(); /* TFH must be cache resident before reading ms_range_active */ - - /* - * TFH is cache resident - at least briefly. Fail the dropin - * if a range invalidate is active. - */ - if (atomic_read(>s->ts_gms->ms_range_active)) - goto failactive; - - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); - if (ret == VTOP_INVALID) - goto failinval; - if (ret == VTOP_RETRY) - goto failupm; - - if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { - gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); - if (atomic || !gru_update_cch(gts)) { - gts->ts_force_cch_reload = 1; - goto failupm; - } - } - - if (unlikely(cbe) && pageshift == PAGE_SHIFT) { - gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe); - gru_flush_cache_cbe(cbe); - } - - gru_cb_set_istatus_active(cbk); - gts->ustats.tlbdropin++; - tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift)); - gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," - " rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid, - indexway, write, pageshift, gpa); - STAT(tlb_dropin); - return 0; - -failnoasid: - /* No asid (delayed unload). */ - STAT(tlb_dropin_fail_no_asid); - gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - return -EAGAIN; - -failupm: - /* Atomic failure switch CBR to UPM */ - tfh_user_polling_mode(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_upm); - gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return 1; - -failfmm: - /* FMM state on UPM call */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_fmm); - gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failnoexception: - /* TFH status did not show exception pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_no_exception); - gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", - tfh, tfh->status, tfh->state); - return 0; - -failidle: - /* TFH state was idle - no miss pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_idle); - gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failinval: - /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ - tfh_exception(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_invalid); - gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return -EFAULT; - -failactive: - /* Range invalidate active. Switch to UPM iff atomic */ - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_range_active); - gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", - tfh, vaddr); - return 1; -} - -/* - * Process an external interrupt from the GRU. This interrupt is - * caused by a TLB miss. - * Note that this is the interrupt handler that is registered with linux - * interrupt handlers. - */ -static irqreturn_t gru_intr(int chiplet, int blade) -{ - struct gru_state *gru; - struct gru_tlb_fault_map imap, dmap; - struct gru_thread_state *gts; - struct gru_tlb_fault_handle *tfh = NULL; - struct completion *cmp; - int cbrnum, ctxnum; - - STAT(intr); - - gru = &gru_base[blade]->bs_grus[chiplet]; - if (!gru) { - dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n", - raw_smp_processor_id(), chiplet); - return IRQ_NONE; - } - get_clear_fault_map(gru, &imap, &dmap); - gru_dbg(grudev, - "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n", - smp_processor_id(), chiplet, gru->gs_gid, - imap.fault_bits[0], imap.fault_bits[1], - dmap.fault_bits[0], dmap.fault_bits[1]); - - for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) { - STAT(intr_cbr); - cmp = gru->gs_blade->bs_async_wq; - if (cmp) - complete(cmp); - gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n", - gru->gs_gid, cbrnum, cmp ? cmp->done : -1); - } - - for_each_cbr_in_tfm(cbrnum, imap.fault_bits) { - STAT(intr_tfh); - tfh = get_tfh_by_index(gru, cbrnum); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - - /* - * When hardware sets a bit in the faultmap, it implicitly - * locks the GRU context so that it cannot be unloaded. - * The gts cannot change until a TFH start/writestart command - * is issued. - */ - ctxnum = tfh->ctxnum; - gts = gru->gs_gts[ctxnum]; - - /* Spurious interrupts can cause this. Ignore. */ - if (!gts) { - STAT(intr_spurious); - continue; - } - - /* - * This is running in interrupt context. Trylock the mmap_lock. - * If it fails, retry the fault in user context. - */ - gts->ustats.fmm_tlbmiss++; - if (!gts->ts_force_cch_reload && - mmap_read_trylock(gts->ts_mm)) { - gru_try_dropin(gru, gts, tfh, NULL); - mmap_read_unlock(gts->ts_mm); - } else { - tfh_user_polling_mode(tfh); - STAT(intr_mm_lock_failed); - } - } - return IRQ_HANDLED; -} - -irqreturn_t gru0_intr(int irq, void *dev_id) -{ - return gru_intr(0, uv_numa_blade_id()); -} - -irqreturn_t gru1_intr(int irq, void *dev_id) -{ - return gru_intr(1, uv_numa_blade_id()); -} - -irqreturn_t gru_intr_mblade(int irq, void *dev_id) -{ - int blade; - - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_intr(0, blade); - gru_intr(1, blade); - } - return IRQ_HANDLED; -} - - -static int gru_user_dropin(struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - void *cb) -{ - struct gru_mm_struct *gms = gts->ts_gms; - int ret; - - gts->ustats.upm_tlbmiss++; - while (1) { - wait_event(gms->ms_wait_queue, - atomic_read(&gms->ms_range_active) == 0); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb); - if (ret <= 0) - return ret; - STAT(call_os_wait_queue); - } -} - -/* - * This interface is called as a result of a user detecting a "call OS" bit - * in a user CB. Normally means that a TLB fault has occurred. - * cb - user virtual address of the CB - */ -int gru_handle_user_call_os(unsigned long cb) -{ - struct gru_tlb_fault_handle *tfh; - struct gru_thread_state *gts; - void *cbk; - int ucbnum, cbrnum, ret = -EINVAL; - - STAT(call_os); - - /* sanity check the cb pointer */ - ucbnum = get_cb_number((void *)cb); - if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) - return -EINVAL; - -again: - gts = gru_find_lock_gts(cb); - if (!gts) - return -EINVAL; - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) - goto exit; - - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - goto again; - } - - /* - * CCH may contain stale data if ts_force_cch_reload is set. - */ - if (gts->ts_gru && gts->ts_force_cch_reload) { - gts->ts_force_cch_reload = 0; - gru_update_cch(gts); - } - - ret = -EAGAIN; - cbrnum = thread_cbr_number(gts, ucbnum); - if (gts->ts_gru) { - tfh = get_tfh_by_index(gts->ts_gru, cbrnum); - cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr, - gts->ts_ctxnum, ucbnum); - ret = gru_user_dropin(gts, tfh, cbk); - } -exit: - gru_unlock_gts(gts); - return ret; -} - -/* - * Fetch the exception detail information for a CB that terminated with - * an exception. - */ -int gru_get_exception_detail(unsigned long arg) -{ - struct control_block_extended_exc_detail excdet; - struct gru_control_block_extended *cbe; - struct gru_thread_state *gts; - int ucbnum, cbrnum, ret; - - STAT(user_exception); - if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet))) - return -EFAULT; - - gts = gru_find_lock_gts(excdet.cb); - if (!gts) - return -EINVAL; - - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - ucbnum = get_cb_number((void *)excdet.cb); - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) { - ret = -EINVAL; - } else if (gts->ts_gru) { - cbrnum = thread_cbr_number(gts, ucbnum); - cbe = get_cbe_by_index(gts->ts_gru, cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); /* make sure we are have current data */ - excdet.opc = cbe->opccpy; - excdet.exopc = cbe->exopccpy; - excdet.ecause = cbe->ecause; - excdet.exceptdet0 = cbe->idef1upd; - excdet.exceptdet1 = cbe->idef3upd; - excdet.cbrstate = cbe->cbrstate; - excdet.cbrexecstatus = cbe->cbrexecstatus; - gru_flush_cache_cbe(cbe); - ret = 0; - } else { - ret = -EAGAIN; - } - gru_unlock_gts(gts); - - gru_dbg(grudev, - "cb 0x%lx, op %d, exopc %d, cbrstate %d, cbrexecstatus 0x%x, ecause 0x%x, " - "exdet0 0x%lx, exdet1 0x%x\n", - excdet.cb, excdet.opc, excdet.exopc, excdet.cbrstate, excdet.cbrexecstatus, - excdet.ecause, excdet.exceptdet0, excdet.exceptdet1); - if (!ret && copy_to_user((void __user *)arg, &excdet, sizeof(excdet))) - ret = -EFAULT; - return ret; -} - -/* - * User request to unload a context. Content is saved for possible reload. - */ -static int gru_unload_all_contexts(void) -{ - struct gru_thread_state *gts; - struct gru_state *gru; - int gid, ctxnum; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - foreach_gid(gid) { - gru = GID_TO_GRU(gid); - spin_lock(&gru->gs_lock); - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - gts = gru->gs_gts[ctxnum]; - if (gts && mutex_trylock(>s->ts_ctxlock)) { - spin_unlock(&gru->gs_lock); - gru_unload_context(gts, 1); - mutex_unlock(>s->ts_ctxlock); - spin_lock(&gru->gs_lock); - } - } - spin_unlock(&gru->gs_lock); - } - return 0; -} - -int gru_user_unload_context(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_unload_context_req req; - - STAT(user_unload_context); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx\n", req.gseg); - - if (!req.gseg) - return gru_unload_all_contexts(); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - if (gts->ts_gru) - gru_unload_context(gts, 1); - gru_unlock_gts(gts); - - return 0; -} - -/* - * User request to flush a range of virtual addresses from the GRU TLB - * (Mainly for testing). - */ -int gru_user_flush_tlb(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_flush_tlb_req req; - struct gru_mm_struct *gms; - - STAT(user_flush_tlb); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx, vaddr 0x%lx, len 0x%lx\n", req.gseg, - req.vaddr, req.len); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - gms = gts->ts_gms; - gru_unlock_gts(gts); - gru_flush_tlb_range(gms, req.vaddr, req.len); - - return 0; -} - -/* - * Fetch GSEG statisticss - */ -long gru_get_gseg_statistics(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_get_gseg_statistics_req req; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* - * The library creates arrays of contexts for threaded programs. - * If no gts exists in the array, the context has never been used & all - * statistics are implicitly 0. - */ - gts = gru_find_lock_gts(req.gseg); - if (gts) { - memcpy(&req.stats, >s->ustats, sizeof(gts->ustats)); - gru_unlock_gts(gts); - } else { - memset(&req.stats, 0, sizeof(gts->ustats)); - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - - return 0; -} - -/* - * Register the current task as the user of the GSEG slice. - * Needed for TLB fault interrupt targeting. - */ -int gru_set_context_option(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_set_context_option_req req; - int ret = 0; - - STAT(set_context_option); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) { - gts = gru_alloc_locked_gts(req.gseg); - if (IS_ERR(gts)) - return PTR_ERR(gts); - } - - switch (req.op) { - case sco_blade_chiplet: - /* Select blade/chiplet for GRU context */ - if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB || - req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || - (req.val1 >= 0 && !gru_base[req.val1])) { - ret = -EINVAL; - } else { - gts->ts_user_blade_id = req.val1; - gts->ts_user_chiplet_id = req.val0; - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - return ret; - } - } - break; - case sco_gseg_owner: - /* Register the current task as the GSEG owner */ - gts->ts_tgid_owner = current->tgid; - break; - case sco_cch_req_slice: - /* Set the CCH slice option */ - gts->ts_cch_req_slice = req.val1 & 3; - break; - default: - ret = -EINVAL; - } - gru_unlock_gts(gts); - - return ret; -} diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c deleted file mode 100644 index e755690c9805..000000000000 --- a/drivers/misc/sgi-gru/grufile.c +++ /dev/null @@ -1,540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FILE OPERATIONS & DRIVER INITIALIZATION - * - * This file supports the user system call for file open, close, mmap, etc. - * This also incudes the driver initialization code. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_X86_64 -#include -#endif -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#include -#include - -struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly; -unsigned long gru_start_paddr __read_mostly; -void *gru_start_vaddr __read_mostly; -unsigned long gru_end_paddr __read_mostly; -unsigned int gru_max_gids __read_mostly; -struct gru_stats_s gru_stats; - -/* Guaranteed user available resources on each node */ -static int max_user_cbrs, max_user_dsr_bytes; - -static struct miscdevice gru_miscdev; - -static int gru_supported(void) -{ - return is_uv_system() && - (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE); -} - -/* - * gru_vma_close - * - * Called when unmapping a device mapping. Frees all gru resources - * and tables belonging to the vma. - */ -static void gru_vma_close(struct vm_area_struct *vma) -{ - struct gru_vma_data *vdata; - struct gru_thread_state *gts; - struct list_head *entry, *next; - - if (!vma->vm_private_data) - return; - - vdata = vma->vm_private_data; - vma->vm_private_data = NULL; - gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file, - vdata); - list_for_each_safe(entry, next, &vdata->vd_head) { - gts = - list_entry(entry, struct gru_thread_state, ts_next); - list_del(>s->ts_next); - mutex_lock(>s->ts_ctxlock); - if (gts->ts_gru) - gru_unload_context(gts, 0); - mutex_unlock(>s->ts_ctxlock); - gts_drop(gts); - } - kfree(vdata); - STAT(vdata_free); -} - -/* - * gru_file_mmap - * - * Called when mmapping the device. Initializes the vma with a fault handler - * and private data structure necessary to allocate, track, and free the - * underlying pages. - */ -static int gru_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE)) - return -EPERM; - - if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) || - vma->vm_end & (GRU_GSEG_PAGESIZE - 1)) - return -EINVAL; - - vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_LOCKED | - VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP); - vma->vm_page_prot = PAGE_SHARED; - vma->vm_ops = &gru_vm_ops; - - vma->vm_private_data = gru_alloc_vma_data(vma, 0); - if (!vma->vm_private_data) - return -ENOMEM; - - gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n", - file, vma->vm_start, vma, vma->vm_private_data); - return 0; -} - -/* - * Create a new GRU context - */ -static int gru_create_new_context(unsigned long arg) -{ - struct gru_create_context_req req; - struct vm_area_struct *vma; - struct gru_vma_data *vdata; - int ret = -EINVAL; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - if (req.data_segment_bytes > max_user_dsr_bytes) - return -EINVAL; - if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count) - return -EINVAL; - - if (!(req.options & GRU_OPT_MISS_MASK)) - req.options |= GRU_OPT_MISS_FMM_INTR; - - mmap_write_lock(current->mm); - vma = gru_find_vma(req.gseg); - if (vma) { - vdata = vma->vm_private_data; - vdata->vd_user_options = req.options; - vdata->vd_dsr_au_count = - GRU_DS_BYTES_TO_AU(req.data_segment_bytes); - vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); - vdata->vd_tlb_preload_count = req.tlb_preload_count; - ret = 0; - } - mmap_write_unlock(current->mm); - - return ret; -} - -/* - * Get GRU configuration info (temp - for emulator testing) - */ -static long gru_get_config_info(unsigned long arg) -{ - struct gru_config_info info; - int nodesperblade; - - if (num_online_nodes() > 1 && - (uv_node_to_blade_id(1) == uv_node_to_blade_id(0))) - nodesperblade = 2; - else - nodesperblade = 1; - memset(&info, 0, sizeof(info)); - info.cpus = num_online_cpus(); - info.nodes = num_online_nodes(); - info.blades = info.nodes / nodesperblade; - info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades; - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -/* - * gru_file_unlocked_ioctl - * - * Called to update file attributes via IOCTL calls. - */ -static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, - unsigned long arg) -{ - int err = -EBADRQC; - - gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg); - - switch (req) { - case GRU_CREATE_CONTEXT: - err = gru_create_new_context(arg); - break; - case GRU_SET_CONTEXT_OPTION: - err = gru_set_context_option(arg); - break; - case GRU_USER_GET_EXCEPTION_DETAIL: - err = gru_get_exception_detail(arg); - break; - case GRU_USER_UNLOAD_CONTEXT: - err = gru_user_unload_context(arg); - break; - case GRU_USER_FLUSH_TLB: - err = gru_user_flush_tlb(arg); - break; - case GRU_USER_CALL_OS: - err = gru_handle_user_call_os(arg); - break; - case GRU_GET_GSEG_STATISTICS: - err = gru_get_gseg_statistics(arg); - break; - case GRU_KTEST: - err = gru_ktest(arg); - break; - case GRU_GET_CONFIG_INFO: - err = gru_get_config_info(arg); - break; - case GRU_DUMP_CHIPLET_STATE: - err = gru_dump_chiplet_request(arg); - break; - } - return err; -} - -/* - * Called at init time to build tables for all GRUs that are present in the - * system. - */ -static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, - void *vaddr, int blade_id, int chiplet_id) -{ - spin_lock_init(&gru->gs_lock); - spin_lock_init(&gru->gs_asid_lock); - gru->gs_gru_base_paddr = paddr; - gru->gs_gru_base_vaddr = vaddr; - gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id; - gru->gs_blade = gru_base[blade_id]; - gru->gs_blade_id = blade_id; - gru->gs_chiplet_id = chiplet_id; - gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; - gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; - gru->gs_asid_limit = MAX_ASID; - gru_tgh_flush_init(gru); - if (gru->gs_gid >= gru_max_gids) - gru_max_gids = gru->gs_gid + 1; - gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n", - blade_id, gru->gs_gid, gru->gs_gru_base_vaddr, - gru->gs_gru_base_paddr); -} - -static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) -{ - int pnode, nid, bid, chip; - int cbrs, dsrbytes, n; - int order = get_order(sizeof(struct gru_blade_state)); - struct page *page; - struct gru_state *gru; - unsigned long paddr; - void *vaddr; - - max_user_cbrs = GRU_NUM_CB; - max_user_dsr_bytes = GRU_NUM_DSR_BYTES; - for_each_possible_blade(bid) { - pnode = uv_blade_to_pnode(bid); - nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */ - page = alloc_pages_node(nid, GFP_KERNEL, order); - if (!page) - goto fail; - gru_base[bid] = page_address(page); - memset(gru_base[bid], 0, sizeof(struct gru_blade_state)); - gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0]; - spin_lock_init(&gru_base[bid]->bs_lock); - init_rwsem(&gru_base[bid]->bs_kgts_sema); - - dsrbytes = 0; - cbrs = 0; - for (gru = gru_base[bid]->bs_grus, chip = 0; - chip < GRU_CHIPLETS_PER_BLADE; - chip++, gru++) { - paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); - vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); - gru_init_chiplet(gru, paddr, vaddr, bid, chip); - n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - cbrs = max(cbrs, n); - n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - dsrbytes = max(dsrbytes, n); - } - max_user_cbrs = min(max_user_cbrs, cbrs); - max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes); - } - - return 0; - -fail: - for (bid--; bid >= 0; bid--) - free_pages((unsigned long)gru_base[bid], order); - return -ENOMEM; -} - -static void gru_free_tables(void) -{ - int bid; - int order = get_order(sizeof(struct gru_state) * - GRU_CHIPLETS_PER_BLADE); - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) - free_pages((unsigned long)gru_base[bid], order); -} - -static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep) -{ - unsigned long mmr = 0; - int core; - - /* - * We target the cores of a blade and not the hyperthreads themselves. - * There is a max of 8 cores per socket and 2 sockets per blade, - * making for a max total of 16 cores (i.e., 16 CPUs without - * hyperthreading and 32 CPUs with hyperthreading). - */ - core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu)) - return 0; - - if (chiplet == 0) { - mmr = UVH_GR0_TLB_INT0_CONFIG + - core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG); - } else if (chiplet == 1) { - mmr = UVH_GR1_TLB_INT0_CONFIG + - core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG); - } else { - BUG(); - } - - *corep = core; - return mmr; -} - -static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, - irq_handler_t irq_handler, int cpu, int blade) -{ - unsigned long mmr; - int irq, core; - int ret; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr == 0) - return 0; - - irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU); - if (irq < 0) { - printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -irq); - return irq; - } - - ret = request_irq(irq, irq_handler, 0, irq_name, NULL); - if (ret) { - uv_teardown_irq(irq); - printk(KERN_ERR "%s: request_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -ret); - return ret; - } - gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq; - return 0; -} - -static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) -{ - int irq, core; - unsigned long mmr; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr) { - irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core]; - if (irq) { - free_irq(irq, NULL); - uv_teardown_irq(irq); - } - } -} - -static void gru_teardown_tlb_irqs(void) -{ - int blade; - int cpu; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - gru_chiplet_teardown_tlb_irq(0, cpu, blade); - gru_chiplet_teardown_tlb_irq(1, cpu, blade); - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_chiplet_teardown_tlb_irq(0, 0, blade); - gru_chiplet_teardown_tlb_irq(1, 0, blade); - } -} - -static int gru_setup_tlb_irqs(void) -{ - int blade; - int cpu; - int ret; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade); - if (ret != 0) - goto exit1; - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - } - - return 0; - -exit1: - gru_teardown_tlb_irqs(); - return ret; -} - -/* - * gru_init - * - * Called at boot or module load time to initialize the GRUs. - */ -static int __init gru_init(void) -{ - int ret; - - if (!gru_supported()) - return 0; - - gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) & - 0x7fffffffffffUL; - gru_start_vaddr = __va(gru_start_paddr); - gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; - printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", - gru_start_paddr, gru_end_paddr); - ret = misc_register(&gru_miscdev); - if (ret) { - printk(KERN_ERR "%s: misc_register failed\n", - GRU_DRIVER_ID_STR); - goto exit0; - } - - ret = gru_proc_init(); - if (ret) { - printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); - goto exit1; - } - - ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); - if (ret) { - printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); - goto exit2; - } - - ret = gru_setup_tlb_irqs(); - if (ret != 0) - goto exit3; - - gru_kservices_init(); - - printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, - GRU_DRIVER_VERSION_STR); - return 0; - -exit3: - gru_free_tables(); -exit2: - gru_proc_exit(); -exit1: - misc_deregister(&gru_miscdev); -exit0: - return ret; - -} - -static void __exit gru_exit(void) -{ - if (!gru_supported()) - return; - - gru_teardown_tlb_irqs(); - gru_kservices_exit(); - gru_free_tables(); - misc_deregister(&gru_miscdev); - gru_proc_exit(); - mmu_notifier_synchronize(); -} - -static const struct file_operations gru_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = gru_file_unlocked_ioctl, - .mmap = gru_file_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice gru_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "gru", - .fops = &gru_fops, -}; - -const struct vm_operations_struct gru_vm_ops = { - .close = gru_vma_close, - .fault = gru_fault, -}; - -#ifndef MODULE -fs_initcall(gru_init); -#else -module_init(gru_init); -#endif -module_exit(gru_exit); - -module_param(gru_options, ulong, 0644); -MODULE_PARM_DESC(gru_options, "Various debug options"); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR); -MODULE_VERSION(GRU_DRIVER_VERSION_STR); - diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c deleted file mode 100644 index 695316a83b01..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * GRU KERNEL MCS INSTRUCTIONS - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -/* 10 sec */ -#include -#include -#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) -#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz) - -/* Extract the status field from a kernel handle */ -#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3) - -struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -static void update_mcs_stats(enum mcs_op op, unsigned long clks) -{ - unsigned long nsec; - - nsec = CLKS2NSEC(clks); - atomic_long_inc(&mcs_op_statistics[op].count); - atomic_long_add(nsec, &mcs_op_statistics[op].total); - if (mcs_op_statistics[op].max < nsec) - mcs_op_statistics[op].max = nsec; -} - -static void start_instruction(void *h) -{ - unsigned long *w0 = h; - - wmb(); /* setting CMD/STATUS bits must be last */ - *w0 = *w0 | 0x20001; - gru_flush_cache(h); -} - -static void report_instruction_timeout(void *h) -{ - unsigned long goff = GSEGPOFF((unsigned long)h); - char *id = "???"; - - if (TYPE_IS(CCH, goff)) - id = "CCH"; - else if (TYPE_IS(TGH, goff)) - id = "TGH"; - else if (TYPE_IS(TFH, goff)) - id = "TFH"; - - panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id); -} - -static int wait_instruction_complete(void *h, enum mcs_op opc) -{ - int status; - unsigned long start_time = get_cycles(); - - while (1) { - cpu_relax(); - status = GET_MSEG_HANDLE_STATUS(h); - if (status != CCHSTATUS_ACTIVE) - break; - if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) { - report_instruction_timeout(h); - start_time = get_cycles(); - } - } - if (gru_options & OPT_STATS) - update_mcs_stats(opc, get_cycles() - start_time); - return status; -} - -int cch_allocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_ALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_allocate); - - /* - * Stop speculation into the GSEG being mapped by the previous ALLOCATE. - * The GSEG memory does not exist until the ALLOCATE completes. - */ - sync_core(); - return ret; -} - -int cch_start(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_START; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_start); -} - -int cch_interrupt(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_INTERRUPT; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt); -} - -int cch_deallocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_DEALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_deallocate); - - /* - * Stop speculation into the GSEG being unmapped by the previous - * DEALLOCATE. - */ - sync_core(); - return ret; -} - -int cch_interrupt_sync(struct gru_context_configuration_handle - *cch) -{ - cch->opc = CCHOP_INTERRUPT_SYNC; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt_sync); -} - -int tgh_invalidate(struct gru_tlb_global_handle *tgh, - unsigned long vaddr, unsigned long vaddrmask, - int asid, int pagesize, int global, int n, - unsigned short ctxbitmap) -{ - tgh->vaddr = vaddr; - tgh->asid = asid; - tgh->pagesize = pagesize; - tgh->n = n; - tgh->global = global; - tgh->vaddrmask = vaddrmask; - tgh->ctxbitmap = ctxbitmap; - tgh->opc = TGHOP_TLBINV; - start_instruction(tgh); - return wait_instruction_complete(tgh, tghop_invalidate); -} - -int tfh_write_only(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_ONLY; - start_instruction(tfh); - return wait_instruction_complete(tfh, tfhop_write_only); -} - -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_RESTART; - start_instruction(tfh); -} - -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_USER_POLLING_MODE; - start_instruction(tfh); -} - -void tfh_exception(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_EXCEPTION; - start_instruction(tfh); -} - diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h deleted file mode 100644 index 5a498bf8d003..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.h +++ /dev/null @@ -1,517 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU HANDLE DEFINITION - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUHANDLES_H__ -#define __GRUHANDLES_H__ -#include "gru_instructions.h" - -/* - * Manifest constants for GRU Memory Map - */ -#define GRU_GSEG0_BASE 0 -#define GRU_MCS_BASE (64 * 1024 * 1024) -#define GRU_SIZE (128UL * 1024 * 1024) - -/* Handle & resource counts */ -#define GRU_NUM_CB 128 -#define GRU_NUM_DSR_BYTES (32 * 1024) -#define GRU_NUM_TFM 16 -#define GRU_NUM_TGH 24 -#define GRU_NUM_CBE 128 -#define GRU_NUM_TFH 128 -#define GRU_NUM_CCH 16 - -/* Maximum resource counts that can be reserved by user programs */ -#define GRU_NUM_USER_CBR GRU_NUM_CBE -#define GRU_NUM_USER_DSR_BYTES GRU_NUM_DSR_BYTES - -/* Bytes per handle & handle stride. Code assumes all cb, tfh, cbe handles - * are the same */ -#define GRU_HANDLE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 - -/* Base addresses of handles */ -#define GRU_TFM_BASE (GRU_MCS_BASE + 0x00000) -#define GRU_TGH_BASE (GRU_MCS_BASE + 0x08000) -#define GRU_CBE_BASE (GRU_MCS_BASE + 0x10000) -#define GRU_TFH_BASE (GRU_MCS_BASE + 0x18000) -#define GRU_CCH_BASE (GRU_MCS_BASE + 0x20000) - -/* User gseg constants */ -#define GRU_GSEG_STRIDE (4 * 1024 * 1024) -#define GSEG_BASE(a) ((a) & ~(GRU_GSEG_PAGESIZE - 1)) - -/* Data segment constants */ -#define GRU_DSR_AU_BYTES 1024 -#define GRU_DSR_CL (GRU_NUM_DSR_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU_CL (GRU_DSR_AU_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU (GRU_NUM_DSR_BYTES / GRU_DSR_AU_BYTES) - -/* Control block constants */ -#define GRU_CBR_AU_SIZE 2 -#define GRU_CBR_AU (GRU_NUM_CBE / GRU_CBR_AU_SIZE) - -/* Convert resource counts to the number of AU */ -#define GRU_DS_BYTES_TO_AU(n) DIV_ROUND_UP(n, GRU_DSR_AU_BYTES) -#define GRU_CB_COUNT_TO_AU(n) DIV_ROUND_UP(n, GRU_CBR_AU_SIZE) - -/* UV limits */ -#define GRU_CHIPLETS_PER_HUB 2 -#define GRU_HUBS_PER_BLADE 1 -#define GRU_CHIPLETS_PER_BLADE (GRU_HUBS_PER_BLADE * GRU_CHIPLETS_PER_HUB) - -/* User GRU Gseg offsets */ -#define GRU_CB_BASE 0 -#define GRU_CB_LIMIT (GRU_CB_BASE + GRU_HANDLE_STRIDE * GRU_NUM_CBE) -#define GRU_DS_BASE 0x20000 -#define GRU_DS_LIMIT (GRU_DS_BASE + GRU_NUM_DSR_BYTES) - -/* Convert a GRU physical address to the chiplet offset */ -#define GSEGPOFF(h) ((h) & (GRU_SIZE - 1)) - -/* Convert an arbitrary handle address to the beginning of the GRU segment */ -#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1))) - -/* Test a valid handle address to determine the type */ -#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \ - GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \ - (((h) & (GRU_HANDLE_STRIDE - 1)) == 0)) - - -/* General addressing macros. */ -static inline void *get_gseg_base_address(void *base, int ctxnum) -{ - return (void *)(base + GRU_GSEG0_BASE + GRU_GSEG_STRIDE * ctxnum); -} - -static inline void *get_gseg_base_address_cb(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + - GRU_CB_BASE + GRU_HANDLE_STRIDE * line); -} - -static inline void *get_gseg_base_address_ds(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + GRU_DS_BASE + - GRU_CACHE_LINE_BYTES * line); -} - -static inline struct gru_tlb_fault_map *get_tfm(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_map *)(base + GRU_TFM_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_global_handle *get_tgh(void *base, int ctxnum) -{ - return (struct gru_tlb_global_handle *)(base + GRU_TGH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_control_block_extended *get_cbe(void *base, int ctxnum) -{ - return (struct gru_control_block_extended *)(base + GRU_CBE_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_fault_handle *get_tfh(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_handle *)(base + GRU_TFH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_context_configuration_handle *get_cch(void *base, - int ctxnum) -{ - return (struct gru_context_configuration_handle *)(base + - GRU_CCH_BASE + ctxnum * GRU_HANDLE_STRIDE); -} - -static inline unsigned long get_cb_number(void *cb) -{ - return (((unsigned long)cb - GRU_CB_BASE) % GRU_GSEG_PAGESIZE) / - GRU_HANDLE_STRIDE; -} - -/* byte offset to a specific GRU chiplet. (p=pnode, c=chiplet (0 or 1)*/ -static inline unsigned long gru_chiplet_paddr(unsigned long paddr, int pnode, - int chiplet) -{ - return paddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet) -{ - return vaddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline struct gru_control_block_extended *gru_tfh_to_cbe( - struct gru_tlb_fault_handle *tfh) -{ - unsigned long cbe; - - cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE; - return (struct gru_control_block_extended*)cbe; -} - - - - -/* - * Global TLB Fault Map - * Bitmap of outstanding TLB misses needing interrupt/polling service. - * - */ -struct gru_tlb_fault_map { - unsigned long fault_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill0[2]; - unsigned long done_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill1[2]; -}; - -/* - * TGH - TLB Global Handle - * Used for TLB flushing. - * - */ -struct gru_tlb_global_handle { - unsigned int cmd:1; /* DW 0 */ - unsigned int delresp:1; - unsigned int opc:1; - unsigned int fill1:5; - - unsigned int fill2:8; - - unsigned int status:2; - unsigned long fill3:2; - unsigned int state:3; - unsigned long fill4:1; - - unsigned int cause:3; - unsigned long fill5:37; - - unsigned long vaddr:64; /* DW 1 */ - - unsigned int asid:24; /* DW 2 */ - unsigned int fill6:8; - - unsigned int pagesize:5; - unsigned int fill7:11; - - unsigned int global:1; - unsigned int fill8:15; - - unsigned long vaddrmask:39; /* DW 3 */ - unsigned int fill9:9; - unsigned int n:10; - unsigned int fill10:6; - - unsigned int ctxbitmap:16; /* DW4 */ - unsigned long fill11[3]; -}; - -enum gru_tgh_cmd { - TGHCMD_START -}; - -enum gru_tgh_opc { - TGHOP_TLBNOP, - TGHOP_TLBINV -}; - -enum gru_tgh_status { - TGHSTATUS_IDLE, - TGHSTATUS_EXCEPTION, - TGHSTATUS_ACTIVE -}; - -enum gru_tgh_state { - TGHSTATE_IDLE, - TGHSTATE_PE_INVAL, - TGHSTATE_INTERRUPT_INVAL, - TGHSTATE_WAITDONE, - TGHSTATE_RESTART_CTX, -}; - -enum gru_tgh_cause { - TGHCAUSE_RR_ECC, - TGHCAUSE_TLB_ECC, - TGHCAUSE_LRU_ECC, - TGHCAUSE_PS_ECC, - TGHCAUSE_MUL_ERR, - TGHCAUSE_DATA_ERR, - TGHCAUSE_SW_FORCE -}; - - -/* - * TFH - TLB Global Handle - * Used for TLB dropins into the GRU TLB. - * - */ -struct gru_tlb_fault_handle { - unsigned int cmd:1; /* DW 0 - low 32*/ - unsigned int delresp:1; - unsigned int fill0:2; - unsigned int opc:3; - unsigned int fill1:9; - - unsigned int status:2; - unsigned int fill2:2; - unsigned int state:3; - unsigned int fill3:1; - - unsigned int cause:6; - unsigned int cb_int:1; - unsigned int fill4:1; - - unsigned int indexway:12; /* DW 0 - high 32 */ - unsigned int fill5:4; - - unsigned int ctxnum:4; - unsigned int fill6:12; - - unsigned long missvaddr:64; /* DW 1 */ - - unsigned int missasid:24; /* DW 2 */ - unsigned int fill7:8; - unsigned int fillasid:24; - unsigned int dirty:1; - unsigned int gaa:2; - unsigned long fill8:5; - - unsigned long pfn:41; /* DW 3 */ - unsigned int fill9:7; - unsigned int pagesize:5; - unsigned int fill10:11; - - unsigned long fillvaddr:64; /* DW 4 */ - - unsigned long fill11[3]; -}; - -enum gru_tfh_opc { - TFHOP_NOOP, - TFHOP_RESTART, - TFHOP_WRITE_ONLY, - TFHOP_WRITE_RESTART, - TFHOP_EXCEPTION, - TFHOP_USER_POLLING_MODE = 7, -}; - -enum tfh_status { - TFHSTATUS_IDLE, - TFHSTATUS_EXCEPTION, - TFHSTATUS_ACTIVE, -}; - -enum tfh_state { - TFHSTATE_INACTIVE, - TFHSTATE_IDLE, - TFHSTATE_MISS_UPM, - TFHSTATE_MISS_FMM, - TFHSTATE_HW_ERR, - TFHSTATE_WRITE_TLB, - TFHSTATE_RESTART_CBR, -}; - -/* TFH cause bits */ -enum tfh_cause { - TFHCAUSE_NONE, - TFHCAUSE_TLB_MISS, - TFHCAUSE_TLB_MOD, - TFHCAUSE_HW_ERROR_RR, - TFHCAUSE_HW_ERROR_MAIN_ARRAY, - TFHCAUSE_HW_ERROR_VALID, - TFHCAUSE_HW_ERROR_PAGESIZE, - TFHCAUSE_INSTRUCTION_EXCEPTION, - TFHCAUSE_UNCORRECTIBLE_ERROR, -}; - -/* GAA values */ -#define GAA_RAM 0x0 -#define GAA_NCRAM 0x2 -#define GAA_MMIO 0x1 -#define GAA_REGISTER 0x3 - -/* GRU paddr shift for pfn. (NOTE: shift is NOT by actual pagesize) */ -#define GRU_PADDR_SHIFT 12 - -/* - * Context Configuration handle - * Used to allocate resources to a GSEG context. - * - */ -struct gru_context_configuration_handle { - unsigned int cmd:1; /* DW0 */ - unsigned int delresp:1; - unsigned int opc:3; - unsigned int unmap_enable:1; - unsigned int req_slice_set_enable:1; - unsigned int req_slice:2; - unsigned int cb_int_enable:1; - unsigned int tlb_int_enable:1; - unsigned int tfm_fault_bit_enable:1; - unsigned int tlb_int_select:4; - - unsigned int status:2; - unsigned int state:2; - unsigned int reserved2:4; - - unsigned int cause:4; - unsigned int tfm_done_bit_enable:1; - unsigned int unused:3; - - unsigned int dsr_allocation_map; - - unsigned long cbr_allocation_map; /* DW1 */ - - unsigned int asid[8]; /* DW 2 - 5 */ - unsigned short sizeavail[8]; /* DW 6 - 7 */ -} __attribute__ ((packed)); - -enum gru_cch_opc { - CCHOP_START = 1, - CCHOP_ALLOCATE, - CCHOP_INTERRUPT, - CCHOP_DEALLOCATE, - CCHOP_INTERRUPT_SYNC, -}; - -enum gru_cch_status { - CCHSTATUS_IDLE, - CCHSTATUS_EXCEPTION, - CCHSTATUS_ACTIVE, -}; - -enum gru_cch_state { - CCHSTATE_INACTIVE, - CCHSTATE_MAPPED, - CCHSTATE_ACTIVE, - CCHSTATE_INTERRUPTED, -}; - -/* CCH Exception cause */ -enum gru_cch_cause { - CCHCAUSE_REGION_REGISTER_WRITE_ERROR = 1, - CCHCAUSE_ILLEGAL_OPCODE = 2, - CCHCAUSE_INVALID_START_REQUEST = 3, - CCHCAUSE_INVALID_ALLOCATION_REQUEST = 4, - CCHCAUSE_INVALID_DEALLOCATION_REQUEST = 5, - CCHCAUSE_INVALID_INTERRUPT_REQUEST = 6, - CCHCAUSE_CCH_BUSY = 7, - CCHCAUSE_NO_CBRS_TO_ALLOCATE = 8, - CCHCAUSE_BAD_TFM_CONFIG = 9, - CCHCAUSE_CBR_RESOURCES_OVERSUBSCRIPED = 10, - CCHCAUSE_DSR_RESOURCES_OVERSUBSCRIPED = 11, - CCHCAUSE_CBR_DEALLOCATION_ERROR = 12, -}; -/* - * CBE - Control Block Extended - * Maintains internal GRU state for active CBs. - * - */ -struct gru_control_block_extended { - unsigned int reserved0:1; /* DW 0 - low */ - unsigned int imacpy:3; - unsigned int reserved1:4; - unsigned int xtypecpy:3; - unsigned int iaa0cpy:2; - unsigned int iaa1cpy:2; - unsigned int reserved2:1; - unsigned int opccpy:8; - unsigned int exopccpy:8; - - unsigned int idef2cpy:22; /* DW 0 - high */ - unsigned int reserved3:10; - - unsigned int idef4cpy:22; /* DW 1 */ - unsigned int reserved4:10; - unsigned int idef4upd:22; - unsigned int reserved5:10; - - unsigned long idef1upd:64; /* DW 2 */ - - unsigned long idef5cpy:64; /* DW 3 */ - - unsigned long idef6cpy:64; /* DW 4 */ - - unsigned long idef3upd:64; /* DW 5 */ - - unsigned long idef5upd:64; /* DW 6 */ - - unsigned int idef2upd:22; /* DW 7 */ - unsigned int reserved6:10; - - unsigned int ecause:20; - unsigned int cbrstate:4; - unsigned int cbrexecstatus:8; -}; - -/* CBE fields for active BCOPY instructions */ -#define cbe_baddr0 idef1upd -#define cbe_baddr1 idef3upd -#define cbe_src_cl idef6cpy -#define cbe_nelemcur idef5upd - -enum gru_cbr_state { - CBRSTATE_INACTIVE, - CBRSTATE_IDLE, - CBRSTATE_PE_CHECK, - CBRSTATE_QUEUED, - CBRSTATE_WAIT_RESPONSE, - CBRSTATE_INTERRUPTED, - CBRSTATE_INTERRUPTED_MISS_FMM, - CBRSTATE_BUSY_INTERRUPT_MISS_FMM, - CBRSTATE_INTERRUPTED_MISS_UPM, - CBRSTATE_BUSY_INTERRUPTED_MISS_UPM, - CBRSTATE_REQUEST_ISSUE, - CBRSTATE_BUSY_INTERRUPT, -}; - -/* CBE cbrexecstatus bits - defined in gru_instructions.h*/ -/* CBE ecause bits - defined in gru_instructions.h */ - -/* - * Convert a processor pagesize into the strange encoded pagesize used by the - * GRU. Processor pagesize is encoded as log of bytes per page. (or PAGE_SHIFT) - * pagesize log pagesize grupagesize - * 4k 12 0 - * 16k 14 1 - * 64k 16 2 - * 256k 18 3 - * 1m 20 4 - * 2m 21 5 - * 4m 22 6 - * 16m 24 7 - * 64m 26 8 - * ... - */ -#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6) -#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh)) - -/* minimum TLB purge count to ensure a full purge */ -#define GRUMAXINVAL 1024UL - -int cch_allocate(struct gru_context_configuration_handle *cch); -int cch_start(struct gru_context_configuration_handle *cch); -int cch_interrupt(struct gru_context_configuration_handle *cch); -int cch_deallocate(struct gru_context_configuration_handle *cch); -int cch_interrupt_sync(struct gru_context_configuration_handle *cch); -int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr, - unsigned long vaddrmask, int asid, int pagesize, int global, int n, - unsigned short ctxbitmap); -int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh); -void tfh_exception(struct gru_tlb_fault_handle *tfh); - -#endif /* __GRUHANDLES_H__ */ diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c deleted file mode 100644 index 9869f4f2f476..000000000000 --- a/drivers/misc/sgi-gru/grukdump.c +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * Dump GRU State - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" -#include "grulib.h" - -#define CCH_LOCK_ATTEMPTS 10 - -static int gru_user_copy_handle(void __user **dp, void *s) -{ - if (copy_to_user(*dp, s, GRU_HANDLE_BYTES)) - return -1; - *dp += GRU_HANDLE_BYTES; - return 0; -} - -static int gru_dump_context_data(void *grubase, - struct gru_context_configuration_handle *cch, - void __user *ubuf, int ctxnum, int dsrcnt, - int flush_cbrs) -{ - void *cb, *cbe, *tfh, *gseg; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - tfh = grubase + GRU_TFH_BASE; - - for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { - if (flush_cbrs) - gru_flush_cache(cb); - if (gru_user_copy_handle(&ubuf, cb)) - goto fail; - if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) - goto fail; - if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE)) - goto fail; - cb += GRU_HANDLE_STRIDE; - } - if (dsrcnt) - memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE); - return 0; - -fail: - return -EFAULT; -} - -static int gru_dump_tfm(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_fault_map *tfm; - int i; - - if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TFM; i++) { - tfm = get_tfm(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tfm)) - goto fail; - } - return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_tgh(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_global_handle *tgh; - int i; - - if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TGH; i++) { - tgh = get_tgh(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tgh)) - goto fail; - } - return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_context(struct gru_state *gru, int ctxnum, - void __user *ubuf, void __user *ubufend, char data_opt, - char lock_cch, char flush_cbrs) -{ - struct gru_dump_context_header hdr; - struct gru_dump_context_header __user *uhdr = ubuf; - struct gru_context_configuration_handle *cch, *ubufcch; - struct gru_thread_state *gts; - int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0; - void *grubase; - - memset(&hdr, 0, sizeof(hdr)); - grubase = gru->gs_gru_base_vaddr; - cch = get_cch(grubase, ctxnum); - for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) { - cch_locked = trylock_cch_handle(cch); - if (cch_locked) - break; - msleep(1); - } - - ubuf += sizeof(hdr); - ubufcch = ubuf; - if (gru_user_copy_handle(&ubuf, cch)) { - if (cch_locked) - unlock_cch_handle(cch); - return -EFAULT; - } - if (cch_locked) - ubufcch->delresp = 0; - bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES; - - if (cch_locked || !lock_cch) { - gts = gru->gs_gts[ctxnum]; - if (gts && gts->ts_vma) { - hdr.pid = gts->ts_tgid_owner; - hdr.vaddr = gts->ts_vma->vm_start; - } - if (cch->state != CCHSTATE_INACTIVE) { - cbrcnt = hweight64(cch->cbr_allocation_map) * - GRU_CBR_AU_SIZE; - dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) * - GRU_DSR_AU_CL : 0; - } - bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES; - if (bytes > ubufend - ubuf) - ret = -EFBIG; - else - ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, - dsrcnt, flush_cbrs); - } - if (cch_locked) - unlock_cch_handle(cch); - if (ret) - return ret; - - hdr.magic = GRU_DUMP_MAGIC; - hdr.gid = gru->gs_gid; - hdr.ctxnum = ctxnum; - hdr.cbrcnt = cbrcnt; - hdr.dsrcnt = dsrcnt; - hdr.cch_locked = cch_locked; - if (copy_to_user(uhdr, &hdr, sizeof(hdr))) - return -EFAULT; - - return bytes; -} - -int gru_dump_chiplet_request(unsigned long arg) -{ - struct gru_state *gru; - struct gru_dump_chiplet_state_req req; - void __user *ubuf; - void __user *ubufend; - int ctxnum, ret, cnt = 0; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* Currently, only dump by gid is implemented */ - if (req.gid >= gru_max_gids) - return -EINVAL; - req.gid = array_index_nospec(req.gid, gru_max_gids); - - gru = GID_TO_GRU(req.gid); - ubuf = req.buf; - ubufend = req.buf + req.buflen; - - ret = gru_dump_tfm(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - ret = gru_dump_tgh(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (req.ctxnum == ctxnum || req.ctxnum < 0) { - ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, - req.data_opt, req.lock_cch, - req.flush_cbrs); - if (ret < 0) - goto fail; - ubuf += ret; - cnt++; - } - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - return cnt; - -fail: - return ret; -} diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c deleted file mode 100644 index 205945ce9e86..000000000000 --- a/drivers/misc/sgi-gru/grukservices.c +++ /dev/null @@ -1,1157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * KERNEL SERVICES THAT USE THE GRU - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" -#include "grukservices.h" -#include "gru_instructions.h" -#include - -/* - * Kernel GRU Usage - * - * The following is an interim algorithm for management of kernel GRU - * resources. This will likely be replaced when we better understand the - * kernel/user requirements. - * - * Blade percpu resources reserved for kernel use. These resources are - * reserved whenever the kernel context for the blade is loaded. Note - * that the kernel context is not guaranteed to be always available. It is - * loaded on demand & can be stolen by a user if the user demand exceeds the - * kernel demand. The kernel can always reload the kernel context but - * a SLEEP may be required!!!. - * - * Async Overview: - * - * Each blade has one "kernel context" that owns GRU kernel resources - * located on the blade. Kernel drivers use GRU resources in this context - * for sending messages, zeroing memory, etc. - * - * The kernel context is dynamically loaded on demand. If it is not in - * use by the kernel, the kernel context can be unloaded & given to a user. - * The kernel context will be reloaded when needed. This may require that - * a context be stolen from a user. - * NOTE: frequent unloading/reloading of the kernel context is - * expensive. We are depending on batch schedulers, cpusets, sane - * drivers or some other mechanism to prevent the need for frequent - * stealing/reloading. - * - * The kernel context consists of two parts: - * - 1 CB & a few DSRs that are reserved for each cpu on the blade. - * Each cpu has it's own private resources & does not share them - * with other cpus. These resources are used serially, ie, - * locked, used & unlocked on each call to a function in - * grukservices. - * (Now that we have dynamic loading of kernel contexts, I - * may rethink this & allow sharing between cpus....) - * - * - Additional resources can be reserved long term & used directly - * by UV drivers located in the kernel. Drivers using these GRU - * resources can use asynchronous GRU instructions that send - * interrupts on completion. - * - these resources must be explicitly locked/unlocked - * - locked resources prevent (obviously) the kernel - * context from being unloaded. - * - drivers using these resource directly issue their own - * GRU instruction and must wait/check completion. - * - * When these resources are reserved, the caller can optionally - * associate a wait_queue with the resources and use asynchronous - * GRU instructions. When an async GRU instruction completes, the - * driver will do a wakeup on the event. - * - */ - - -#define ASYNC_HAN_TO_BID(h) ((h) - 1) -#define ASYNC_BID_TO_HAN(b) ((b) + 1) -#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)] - -#define GRU_NUM_KERNEL_CBR 1 -#define GRU_NUM_KERNEL_DSR_BYTES 256 -#define GRU_NUM_KERNEL_DSR_CL (GRU_NUM_KERNEL_DSR_BYTES / \ - GRU_CACHE_LINE_BYTES) - -/* GRU instruction attributes for all instructions */ -#define IMA IMA_CB_DELAY - -/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */ -#define __gru_cacheline_aligned__ \ - __attribute__((__aligned__(GRU_CACHE_LINE_BYTES))) - -#define MAGIC 0x1234567887654321UL - -/* Default retry count for GRU errors on kernel instructions */ -#define EXCEPTION_RETRY_LIMIT 3 - -/* Status of message queue sections */ -#define MQS_EMPTY 0 -#define MQS_FULL 1 -#define MQS_NOOP 2 - -/*----------------- RESOURCE MANAGEMENT -------------------------------------*/ -/* optimized for x86_64 */ -struct message_queue { - union gru_mesqhead head __gru_cacheline_aligned__; /* CL 0 */ - int qlines; /* DW 1 */ - long hstatus[2]; - void *next __gru_cacheline_aligned__;/* CL 1 */ - void *limit; - void *start; - void *start2; - char data ____cacheline_aligned; /* CL 2 */ -}; - -/* First word in every message - used by mesq interface */ -struct message_header { - char present; - char present2; - char lines; - char fill; -}; - -#define HSTATUS(mq, h) ((mq) + offsetof(struct message_queue, hstatus[h])) - -/* - * Reload the blade's kernel context into a GRU chiplet. Called holding - * the bs_kgts_sema for READ. Will steal user contexts if necessary. - */ -static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id) -{ - struct gru_state *gru; - struct gru_thread_state *kgts; - void *vaddr; - int ctxnum, ncpus; - - up_read(&bs->bs_kgts_sema); - down_write(&bs->bs_kgts_sema); - - if (!bs->bs_kgts) { - do { - bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0); - if (!IS_ERR(bs->bs_kgts)) - break; - msleep(1); - } while (true); - bs->bs_kgts->ts_user_blade_id = blade_id; - } - kgts = bs->bs_kgts; - - if (!kgts->ts_gru) { - STAT(load_kernel_context); - ncpus = uv_blade_nr_possible_cpus(blade_id); - kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU( - GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs); - kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU( - GRU_NUM_KERNEL_DSR_BYTES * ncpus + - bs->bs_async_dsr_bytes); - while (!gru_assign_gru_context(kgts)) { - msleep(1); - gru_steal_context(kgts); - } - gru_load_context(kgts); - gru = bs->bs_kgts->ts_gru; - vaddr = gru->gs_gru_base_vaddr; - ctxnum = kgts->ts_ctxnum; - bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0); - bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0); - } - downgrade_write(&bs->bs_kgts_sema); -} - -/* - * Free all kernel contexts that are not currently in use. - * Returns 0 if all freed, else number of inuse context. - */ -static int gru_free_kernel_contexts(void) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int bid, ret = 0; - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) { - bs = gru_base[bid]; - if (!bs) - continue; - - /* Ignore busy contexts. Don't want to block here. */ - if (down_write_trylock(&bs->bs_kgts_sema)) { - kgts = bs->bs_kgts; - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - bs->bs_kgts = NULL; - up_write(&bs->bs_kgts_sema); - kfree(kgts); - } else { - ret++; - } - } - return ret; -} - -/* - * Lock & load the kernel context for the specified blade. - */ -static struct gru_blade_state *gru_lock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - int bid; - - STAT(lock_kernel_context); -again: - bid = blade_id < 0 ? uv_numa_blade_id() : blade_id; - bs = gru_base[bid]; - - /* Handle the case where migration occurred while waiting for the sema */ - down_read(&bs->bs_kgts_sema); - if (blade_id < 0 && bid != uv_numa_blade_id()) { - up_read(&bs->bs_kgts_sema); - goto again; - } - if (!bs->bs_kgts || !bs->bs_kgts->ts_gru) - gru_load_kernel_context(bs, bid); - return bs; - -} - -/* - * Unlock the kernel context for the specified blade. Context is not - * unloaded but may be stolen before next use. - */ -static void gru_unlock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - - bs = gru_base[blade_id]; - up_read(&bs->bs_kgts_sema); - STAT(unlock_kernel_context); -} - -/* - * Reserve & get pointers to the DSR/CBRs reserved for the current cpu. - * - returns with preemption disabled - */ -static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr) -{ - struct gru_blade_state *bs; - int lcpu; - - BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES); - bs = gru_lock_kernel_context(-1); - lcpu = uv_blade_processor_id(); - *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE; - *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES; - return 0; -} - -/* - * Free the current cpus reserved DSR/CBR resources. - */ -static void gru_free_cpu_resources(void *cb, void *dsr) -{ - gru_unlock_kernel_context(uv_numa_blade_id()); -} - -/* - * Reserve GRU resources to be used asynchronously. - * Note: currently supports only 1 reservation per blade. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * output: - * handle to identify resource - * (0 = async resources already reserved) - */ -unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int ret = 0; - - bs = gru_base[blade_id]; - - down_write(&bs->bs_kgts_sema); - - /* Verify no resources already reserved */ - if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs) - goto done; - bs->bs_async_dsr_bytes = dsr_bytes; - bs->bs_async_cbrs = cbrs; - bs->bs_async_wq = cmp; - kgts = bs->bs_kgts; - - /* Resources changed. Unload context if already loaded */ - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - ret = ASYNC_BID_TO_HAN(blade_id); - -done: - up_write(&bs->bs_kgts_sema); - return ret; -} - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -void gru_release_async_resources(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - down_write(&bs->bs_kgts_sema); - bs->bs_async_dsr_bytes = 0; - bs->bs_async_cbrs = 0; - bs->bs_async_wq = NULL; - up_write(&bs->bs_kgts_sema); -} - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -void gru_wait_async_cbr(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - wait_for_completion(bs->bs_async_wq); - mb(); -} - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -void gru_lock_async_resource(unsigned long han, void **cb, void **dsr) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - int blade_id = ASYNC_HAN_TO_BID(han); - int ncpus; - - gru_lock_kernel_context(blade_id); - ncpus = uv_blade_nr_possible_cpus(blade_id); - if (cb) - *cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE; - if (dsr) - *dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES; -} - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -void gru_unlock_async_resource(unsigned long han) -{ - int blade_id = ASYNC_HAN_TO_BID(han); - - gru_unlock_kernel_context(blade_id); -} - -/*----------------------------------------------------------------------*/ -int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet) -{ - struct gru_control_block_extended *cbe; - struct gru_thread_state *kgts = NULL; - unsigned long off; - int cbrnum, bid; - - /* - * Locate kgts for cb. This algorithm is SLOW but - * this function is rarely called (ie., almost never). - * Performance does not matter. - */ - for_each_possible_blade(bid) { - if (!gru_base[bid]) - break; - kgts = gru_base[bid]->bs_kgts; - if (!kgts || !kgts->ts_gru) - continue; - off = cb - kgts->ts_gru->gs_gru_base_vaddr; - if (off < GRU_SIZE) - break; - kgts = NULL; - } - BUG_ON(!kgts); - cbrnum = thread_cbr_number(kgts, get_cb_number(cb)); - cbe = get_cbe(GRUBASE(cb), cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); - excdet->opc = cbe->opccpy; - excdet->exopc = cbe->exopccpy; - excdet->ecause = cbe->ecause; - excdet->exceptdet0 = cbe->idef1upd; - excdet->exceptdet1 = cbe->idef3upd; - gru_flush_cache(cbe); - return 0; -} - -static char *gru_get_cb_exception_detail_str(int ret, void *cb, - char *buf, int size) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - - if (ret > 0 && gen->istatus == CBS_EXCEPTION) { - gru_get_cb_exception_detail(cb, &excdet); - snprintf(buf, size, - "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x," - "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(), - gen, excdet.opc, excdet.exopc, excdet.ecause, - excdet.exceptdet0, excdet.exceptdet1); - } else { - snprintf(buf, size, "No exception"); - } - return buf; -} - -static int gru_wait_idle_or_exception(struct gru_control_block_status *gen) -{ - while (gen->istatus >= CBS_ACTIVE) { - cpu_relax(); - barrier(); - } - return gen->istatus; -} - -static int gru_retry_exception(void *cb) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - int retry = EXCEPTION_RETRY_LIMIT; - - while (1) { - if (gru_wait_idle_or_exception(gen) == CBS_IDLE) - return CBS_IDLE; - if (gru_get_cb_message_queue_substatus(cb)) - return CBS_EXCEPTION; - gru_get_cb_exception_detail(cb, &excdet); - if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) || - (excdet.cbrexecstatus & CBR_EXS_ABORT_OCC)) - break; - if (retry-- == 0) - break; - gen->icmd = 1; - gru_flush_cache(gen); - } - return CBS_EXCEPTION; -} - -int gru_check_status_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gen->istatus; - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; - -} - -int gru_wait_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gru_wait_idle_or_exception(gen); - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; -} - -static void gru_abort(int ret, void *cb, char *str) -{ - char buf[GRU_EXC_STR_SIZE]; - - panic("GRU FATAL ERROR: %s - %s\n", str, - gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf))); -} - -void gru_wait_abort_proc(void *cb) -{ - int ret; - - ret = gru_wait_proc(cb); - if (ret) - gru_abort(ret, cb, "gru_wait_abort"); -} - - -/*------------------------------ MESSAGE QUEUES -----------------------------*/ - -/* Internal status . These are NOT returned to the user. */ -#define MQIE_AGAIN -1 /* try again */ - - -/* - * Save/restore the "present" flag that is in the second line of 2-line - * messages - */ -static inline int get_present2(void *p) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - return mhdr->present; -} - -static inline void restore_present2(void *p, int val) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - mhdr->present = val; -} - -/* - * Create a message queue. - * qlines - message queue size in cache lines. Includes 2-line header. - */ -int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid) -{ - struct message_queue *mq = p; - unsigned int qlines; - - qlines = bytes / GRU_CACHE_LINE_BYTES - 2; - memset(mq, 0, bytes); - mq->start = &mq->data; - mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES; - mq->next = &mq->data; - mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES; - mq->qlines = qlines; - mq->hstatus[0] = 0; - mq->hstatus[1] = 1; - mq->head = gru_mesq_head(2, qlines / 2 + 1); - mqd->mq = mq; - mqd->mq_gpa = uv_gpa(mq); - mqd->qlines = qlines; - mqd->interrupt_pnode = nasid >> 1; - mqd->interrupt_vector = vector; - mqd->interrupt_apicid = apicid; - return 0; -} -EXPORT_SYMBOL_GPL(gru_create_message_queue); - -/* - * Send a NOOP message to a message queue - * Returns: - * 0 - if queue is full after the send. This is the normal case - * but various races can change this. - * -1 - if mesq sent successfully but queue not full - * >0 - unexpected error. MQE_xxx returned - */ -static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd, - void *mesg) -{ - const struct message_header noop_header = { - .present = MQS_NOOP, .lines = 1}; - unsigned long m; - int substatus, ret; - struct message_header save_mhdr, *mhdr = mesg; - - STAT(mesq_noop); - save_mhdr = *mhdr; - *mhdr = noop_header; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA); - ret = gru_wait(cb); - - if (ret) { - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_noop_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_noop_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_noop_qlimit_reached); - ret = 0; - break; - case CBSS_AMO_NACKED: - STAT(mesq_noop_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_noop_put_nacked); - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1, - IMA); - if (gru_wait(cb) == CBS_IDLE) - ret = MQIE_AGAIN; - else - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_noop_page_overflow); - fallthrough; - default: - BUG(); - } - } - *mhdr = save_mhdr; - return ret; -} - -/* - * Handle a gru_mesq full. - */ -static int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - union gru_mesqhead mqh; - unsigned int limit, head; - unsigned long avalue; - int half, qlines; - - /* Determine if switching to first/second half of q */ - avalue = gru_get_amo_value(cb); - head = gru_get_amo_value_head(cb); - limit = gru_get_amo_value_limit(cb); - - qlines = mqd->qlines; - half = (limit != qlines); - - if (half) - mqh = gru_mesq_head(qlines / 2 + 1, qlines); - else - mqh = gru_mesq_head(2, qlines / 2 + 1); - - /* Try to get lock for switching head pointer */ - gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - if (!gru_get_amo_value(cb)) { - STAT(mesq_qf_locked); - return MQE_QUEUE_FULL; - } - - /* Got the lock. Send optional NOP if queue not full, */ - if (head != limit) { - if (send_noop_message(cb, mqd, mesg)) { - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), - XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - STAT(mesq_qf_noop_not_full); - return MQIE_AGAIN; - } - avalue++; - } - - /* Then flip queuehead to other half of queue. */ - gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - - /* If not successfully in swapping queue head, clear the hstatus lock */ - if (gru_get_amo_value(cb) != avalue) { - STAT(mesq_qf_switch_head_failed); - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - } - return MQIE_AGAIN; -cberr: - STAT(mesq_qf_unexpected_error); - return MQE_UNEXPECTED_CB_ERR; -} - -/* - * Handle a PUT failure. Note: if message was a 2-line message, one of the - * lines might have successfully have been written. Before sending the - * message, "present" must be cleared in BOTH lines to prevent the receiver - * from prematurely seeing the full message. - */ -static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - unsigned long m; - int ret, loops = 200; /* experimentally determined */ - - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - if (lines == 2) { - gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - } - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - - if (!mqd->interrupt_vector) - return MQE_OK; - - /* - * Send a noop message in order to deliver a cross-partition interrupt - * to the SSI that contains the target message queue. Normally, the - * interrupt is automatically delivered by hardware following mesq - * operations, but some error conditions require explicit delivery. - * The noop message will trigger delivery. Otherwise partition failures - * could cause unrecovered errors. - */ - do { - ret = send_noop_message(cb, mqd, mesg); - } while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0)); - - if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) { - /* - * Don't indicate to the app to resend the message, as it's - * already been successfully sent. We simply send an OK - * (rather than fail the send with MQE_UNEXPECTED_CB_ERR), - * assuming that the other side is receiving enough - * interrupts to get this message processed anyway. - */ - ret = MQE_OK; - } - return ret; -} - -/* - * Handle a gru_mesq failure. Some of these failures are software recoverable - * or retryable. - */ -static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - int substatus, ret = 0; - - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_send_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_send_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_send_qlimit_reached); - ret = send_message_queue_full(cb, mqd, mesg, lines); - break; - case CBSS_AMO_NACKED: - STAT(mesq_send_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_send_put_nacked); - ret = send_message_put_nacked(cb, mqd, mesg, lines); - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_page_overflow); - fallthrough; - default: - BUG(); - } - return ret; -} - -/* - * Send a message to a message queue - * mqd message queue descriptor - * mesg message. ust be vaddr within a GSEG - * bytes message size (<= 2 CL) - */ -int gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg, - unsigned int bytes) -{ - struct message_header *mhdr; - void *cb; - void *dsr; - int istatus, clines, ret; - - STAT(mesq_send); - BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES); - - clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES); - if (gru_get_cpu_resources(bytes, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - memcpy(dsr, mesg, bytes); - mhdr = dsr; - mhdr->present = MQS_FULL; - mhdr->lines = clines; - if (clines == 2) { - mhdr->present2 = get_present2(mhdr); - restore_present2(mhdr, MQS_FULL); - } - - do { - ret = MQE_OK; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA); - istatus = gru_wait(cb); - if (istatus != CBS_IDLE) - ret = send_message_failure(cb, mqd, dsr, clines); - } while (ret == MQIE_AGAIN); - gru_free_cpu_resources(cb, dsr); - - if (ret) - STAT(mesq_send_failed); - return ret; -} -EXPORT_SYMBOL_GPL(gru_send_message_gpa); - -/* - * Advance the receive pointer for the queue to the next message. - */ -void gru_free_message(struct gru_message_queue_desc *mqd, void *mesg) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - void *next, *pnext; - int half = -1; - int lines = mhdr->lines; - - if (lines == 2) - restore_present2(mhdr, MQS_EMPTY); - mhdr->present = MQS_EMPTY; - - pnext = mq->next; - next = pnext + GRU_CACHE_LINE_BYTES * lines; - if (next == mq->limit) { - next = mq->start; - half = 1; - } else if (pnext < mq->start2 && next >= mq->start2) { - half = 0; - } - - if (half >= 0) - mq->hstatus[half] = 1; - mq->next = next; -} -EXPORT_SYMBOL_GPL(gru_free_message); - -/* - * Get next message from message queue. Return NULL if no message - * present. User must call next_message() to move to next message. - * rmq message queue - */ -void *gru_get_next_message(struct gru_message_queue_desc *mqd) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - int present = mhdr->present; - - /* skip NOOP messages */ - while (present == MQS_NOOP) { - gru_free_message(mqd, mhdr); - mhdr = mq->next; - present = mhdr->present; - } - - /* Wait for both halves of 2 line messages */ - if (present == MQS_FULL && mhdr->lines == 2 && - get_present2(mhdr) == MQS_EMPTY) - present = MQS_EMPTY; - - if (!present) { - STAT(mesq_receive_none); - return NULL; - } - - if (mhdr->lines == 2) - restore_present2(mhdr, mhdr->present2); - - STAT(mesq_receive); - return mhdr; -} -EXPORT_SYMBOL_GPL(gru_get_next_message); - -/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/ - -/* - * Load a DW from a global GPA. The GPA can be a memory or MMR address. - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa) -{ - void *cb; - void *dsr; - int ret, iaa; - - STAT(read_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - iaa = gpa >> 62; - gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA); - ret = gru_wait(cb); - if (ret == CBS_IDLE) - *value = *(unsigned long *)dsr; - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_read_gpa); - - -/* - * Copy a block of data using the GRU resources - */ -int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes) -{ - void *cb; - void *dsr; - int ret; - - STAT(copy_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr), - XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA); - ret = gru_wait(cb); - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_copy_gpa); - -/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/ -/* Temp - will delete after we gain confidence in the GRU */ - -static int quicktest0(unsigned long arg) -{ - unsigned long word0; - unsigned long word1; - void *cb; - void *dsr; - unsigned long *p; - int ret = -EIO; - - if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - p = dsr; - word0 = MAGIC; - word1 = 0; - - gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id()); - goto done; - } - - if (*p != MAGIC) { - printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p); - goto done; - } - gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id()); - goto done; - } - - if (word0 != word1 || word1 != MAGIC) { - printk(KERN_DEBUG - "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n", - smp_processor_id(), word1, MAGIC); - goto done; - } - ret = 0; - -done: - gru_free_cpu_resources(cb, dsr); - return ret; -} - -#define ALIGNUP(p, q) ((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1))) - -static int quicktest1(unsigned long arg) -{ - struct gru_message_queue_desc mqd; - void *p, *mq; - int i, ret = -EIO; - char mes[GRU_CACHE_LINE_BYTES], *m; - - /* Need 1K cacheline aligned that does not cross page boundary */ - p = kmalloc(4096, 0); - if (p == NULL) - return -ENOMEM; - mq = ALIGNUP(p, 1024); - memset(mes, 0xee, sizeof(mes)); - - gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0); - for (i = 0; i < 6; i++) { - mes[8] = i; - do { - ret = gru_send_message_gpa(&mqd, mes, sizeof(mes)); - } while (ret == MQE_CONGESTION); - if (ret) - break; - } - if (ret != MQE_QUEUE_FULL || i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n", - smp_processor_id(), ret, i); - goto done; - } - - for (i = 0; i < 6; i++) { - m = gru_get_next_message(&mqd); - if (!m || m[8] != i) - break; - gru_free_message(&mqd, m); - } - if (i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n", - smp_processor_id(), i, m, m ? m[8] : -1); - goto done; - } - ret = 0; - -done: - kfree(p); - return ret; -} - -static int quicktest2(unsigned long arg) -{ - static DECLARE_COMPLETION(cmp); - unsigned long han; - int blade_id = 0; - int numcb = 4; - int ret = 0; - unsigned long *buf; - void *cb0, *cb; - struct gru_control_block_status *gen; - int i, k, istatus, bytes; - - bytes = numcb * 4 * 8; - buf = kmalloc(bytes, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = -EBUSY; - han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp); - if (!han) - goto done; - - gru_lock_async_resource(han, &cb0, NULL); - memset(buf, 0xee, bytes); - for (i = 0; i < numcb; i++) - gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0, - XTYPE_DW, 4, 1, IMA_INTERRUPT); - - ret = 0; - k = numcb; - do { - gru_wait_async_cbr(han); - for (i = 0; i < numcb; i++) { - cb = cb0 + i * GRU_HANDLE_STRIDE; - istatus = gru_check_status(cb); - if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS) - break; - } - if (i == numcb) - continue; - if (istatus != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i); - ret = -EFAULT; - } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] || - buf[4 * i + 3]) { - printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", - smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]); - ret = -EIO; - } - k--; - gen = cb; - gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */ - } while (k); - BUG_ON(cmp.done); - - gru_unlock_async_resource(han); - gru_release_async_resources(han); -done: - kfree(buf); - return ret; -} - -#define BUFSIZE 200 -static int quicktest3(unsigned long arg) -{ - char buf1[BUFSIZE], buf2[BUFSIZE]; - int ret = 0; - - memset(buf2, 0, sizeof(buf2)); - memset(buf1, get_cycles() & 255, sizeof(buf1)); - gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE); - if (memcmp(buf1, buf2, BUFSIZE)) { - printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id()); - ret = -EIO; - } - return ret; -} - -/* - * Debugging only. User hook for various kernel tests - * of driver & gru. - */ -int gru_ktest(unsigned long arg) -{ - int ret = -EINVAL; - - switch (arg & 0xff) { - case 0: - ret = quicktest0(arg); - break; - case 1: - ret = quicktest1(arg); - break; - case 2: - ret = quicktest2(arg); - break; - case 3: - ret = quicktest3(arg); - break; - case 99: - ret = gru_free_kernel_contexts(); - break; - } - return ret; - -} - -int gru_kservices_init(void) -{ - return 0; -} - -void gru_kservices_exit(void) -{ - if (gru_free_kernel_contexts()) - BUG(); -} - diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h deleted file mode 100644 index 510e45e9737e..000000000000 --- a/drivers/misc/sgi-gru/grukservices.h +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ -#ifndef __GRU_KSERVICES_H_ -#define __GRU_KSERVICES_H_ - - -/* - * Message queues using the GRU to send/receive messages. - * - * These function allow the user to create a message queue for - * sending/receiving 1 or 2 cacheline messages using the GRU. - * - * Processes SENDING messages will use a kernel CBR/DSR to send - * the message. This is transparent to the caller. - * - * The receiver does not use any GRU resources. - * - * The functions support: - * - single receiver - * - multiple senders - * - cross partition message - * - * Missing features ZZZ: - * - user options for dealing with timeouts, queue full, etc. - * - gru_create_message_queue() needs interrupt vector info - */ - -struct gru_message_queue_desc { - void *mq; /* message queue vaddress */ - unsigned long mq_gpa; /* global address of mq */ - int qlines; /* queue size in CL */ - int interrupt_vector; /* interrupt vector */ - int interrupt_pnode; /* pnode for interrupt */ - int interrupt_apicid; /* lapicid for interrupt */ -}; - -/* - * Initialize a user allocated chunk of memory to be used as - * a message queue. The caller must ensure that the queue is - * in contiguous physical memory and is cacheline aligned. - * - * Message queue size is the total number of bytes allocated - * to the queue including a 2 cacheline header that is used - * to manage the queue. - * - * Input: - * mqd pointer to message queue descriptor - * p pointer to user allocated mesq memory. - * bytes size of message queue in bytes - * vector interrupt vector (zero if no interrupts) - * nasid nasid of blade where interrupt is delivered - * apicid apicid of cpu for interrupt - * - * Errors: - * 0 OK - * >0 error - */ -extern int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid); - -/* - * Send a message to a message queue. - * - * Note: The message queue transport mechanism uses the first 32 - * bits of the message. Users should avoid using these bits. - * - * - * Input: - * mqd pointer to message queue descriptor - * mesg pointer to message. Must be 64-bit aligned - * bytes size of message in bytes - * - * Output: - * 0 message sent - * >0 Send failure - see error codes below - * - */ -extern int gru_send_message_gpa(struct gru_message_queue_desc *mqd, - void *mesg, unsigned int bytes); - -/* Status values for gru_send_message() */ -#define MQE_OK 0 /* message sent successfully */ -#define MQE_CONGESTION 1 /* temporary congestion, try again */ -#define MQE_QUEUE_FULL 2 /* queue is full */ -#define MQE_UNEXPECTED_CB_ERR 3 /* unexpected CB error */ -#define MQE_PAGE_OVERFLOW 10 /* BUG - queue overflowed a page */ -#define MQE_BUG_NO_RESOURCES 11 /* BUG - could not alloc GRU cb/dsr */ - -/* - * Advance the receive pointer for the message queue to the next message. - * Note: current API requires messages to be gotten & freed in order. Future - * API extensions may allow for out-of-order freeing. - * - * Input - * mqd pointer to message queue descriptor - * mesq message being freed - */ -extern void gru_free_message(struct gru_message_queue_desc *mqd, - void *mesq); - -/* - * Get next message from message queue. Returns pointer to - * message OR NULL if no message present. - * User must call gru_free_message() after message is processed - * in order to move the queue pointers to next message. - * - * Input - * mqd pointer to message queue descriptor - * - * Output: - * p pointer to message - * NULL no message available - */ -extern void *gru_get_next_message(struct gru_message_queue_desc *mqd); - - -/* - * Read a GRU global GPA. Source can be located in a remote partition. - * - * Input: - * value memory address where MMR value is returned - * gpa source numalink physical address of GPA - * - * Output: - * 0 OK - * >0 error - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa); - - -/* - * Copy data using the GRU. Source or destination can be located in a remote - * partition. - * - * Input: - * dest_gpa destination global physical address - * src_gpa source global physical address - * bytes number of bytes to copy - * - * Output: - * 0 OK - * >0 error - */ -extern int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes); - -/* - * Reserve GRU resources to be used asynchronously. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * cmp - completion structure for waiting for - * async completions - * output: - * handle to identify resource - * (0 = no resources) - */ -extern unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp); - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -extern void gru_release_async_resources(unsigned long han); - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -extern void gru_wait_async_cbr(unsigned long han); - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -extern void gru_lock_async_resource(unsigned long han, void **cb, void **dsr); - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -extern void gru_unlock_async_resource(unsigned long han); - -#endif /* __GRU_KSERVICES_H_ */ diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h deleted file mode 100644 index 85c103923632..000000000000 --- a/drivers/misc/sgi-gru/grulib.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRULIB_H__ -#define __GRULIB_H__ - -#define GRU_BASENAME "gru" -#define GRU_FULLNAME "/dev/gru" -#define GRU_IOCTL_NUM 'G' - -/* - * Maximum number of GRU segments that a user can have open - * ZZZ temp - set high for testing. Revisit. - */ -#define GRU_MAX_OPEN_CONTEXTS 32 - -/* Set Number of Request Blocks */ -#define GRU_CREATE_CONTEXT _IOWR(GRU_IOCTL_NUM, 1, void *) - -/* Set Context Options */ -#define GRU_SET_CONTEXT_OPTION _IOWR(GRU_IOCTL_NUM, 4, void *) - -/* Fetch exception detail */ -#define GRU_USER_GET_EXCEPTION_DETAIL _IOWR(GRU_IOCTL_NUM, 6, void *) - -/* For user call_os handling - normally a TLB fault */ -#define GRU_USER_CALL_OS _IOWR(GRU_IOCTL_NUM, 8, void *) - -/* For user unload context */ -#define GRU_USER_UNLOAD_CONTEXT _IOWR(GRU_IOCTL_NUM, 9, void *) - -/* For dumpping GRU chiplet state */ -#define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *) - -/* For getting gseg statistics */ -#define GRU_GET_GSEG_STATISTICS _IOWR(GRU_IOCTL_NUM, 12, void *) - -/* For user TLB flushing (primarily for tests) */ -#define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *) - -/* Get some config options (primarily for tests & emulator) */ -#define GRU_GET_CONFIG_INFO _IOWR(GRU_IOCTL_NUM, 51, void *) - -/* Various kernel self-tests */ -#define GRU_KTEST _IOWR(GRU_IOCTL_NUM, 52, void *) - -#define CONTEXT_WINDOW_BYTES(th) (GRU_GSEG_PAGESIZE * (th)) -#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th)) -#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1))) - -struct gru_get_gseg_statistics_req { - unsigned long gseg; - struct gru_gseg_statistics stats; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_create_context_req { - unsigned long gseg; - unsigned int data_segment_bytes; - unsigned int control_blocks; - unsigned int maximum_thread_count; - unsigned int options; - unsigned char tlb_preload_count; -}; - -/* - * Structure used to pass unload context parameters to the driver - */ -struct gru_unload_context_req { - unsigned long gseg; -}; - -/* - * Structure used to set context options - */ -enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet}; -struct gru_set_context_option_req { - unsigned long gseg; - int op; - int val0; - long val1; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_flush_tlb_req { - unsigned long gseg; - unsigned long vaddr; - size_t len; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -enum {dcs_pid, dcs_gid}; -struct gru_dump_chiplet_state_req { - unsigned int op; - unsigned int gid; - int ctxnum; - char data_opt; - char lock_cch; - char flush_cbrs; - char fill[10]; - pid_t pid; - void *buf; - size_t buflen; - /* ---- output --- */ - unsigned int num_contexts; -}; - -#define GRU_DUMP_MAGIC 0x3474ab6c -struct gru_dump_context_header { - unsigned int magic; - unsigned int gid; - unsigned char ctxnum; - unsigned char cbrcnt; - unsigned char dsrcnt; - pid_t pid; - unsigned long vaddr; - int cch_locked; - unsigned long data[]; -}; - -/* - * GRU configuration info (temp - for testing) - */ -struct gru_config_info { - int cpus; - int blades; - int nodes; - int chiplets; - int fill[16]; -}; - -#endif /* __GRULIB_H__ */ diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c deleted file mode 100644 index 278b76cbd281..000000000000 --- a/drivers/misc/sgi-gru/grumain.c +++ /dev/null @@ -1,969 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * DRIVER TABLE MANAGER + GRU CONTEXT LOAD/UNLOAD - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" - -unsigned long gru_options __read_mostly; - -static struct device_driver gru_driver = { - .name = "gru" -}; - -static struct device gru_device = { - .init_name = "", - .driver = &gru_driver, -}; - -struct device *grudev = &gru_device; - -/* - * Select a gru fault map to be used by the current cpu. Note that - * multiple cpus may be using the same map. - * ZZZ should be inline but did not work on emulator - */ -int gru_cpu_fault_map_id(void) -{ - int cpu = smp_processor_id(); - int id, core; - - core = uv_cpu_core_number(cpu); - id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - return id; -} - -/*--------- ASID Management ------------------------------------------- - * - * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID. - * Once MAX is reached, flush the TLB & start over. However, - * some asids may still be in use. There won't be many (percentage wise) still - * in use. Search active contexts & determine the value of the first - * asid in use ("x"s below). Set "limit" to this value. - * This defines a block of assignable asids. - * - * When "limit" is reached, search forward from limit+1 and determine the - * next block of assignable asids. - * - * Repeat until MAX_ASID is reached, then start over again. - * - * Each time MAX_ASID is reached, increment the asid generation. Since - * the search for in-use asids only checks contexts with GRUs currently - * assigned, asids in some contexts will be missed. Prior to loading - * a context, the asid generation of the GTS asid is rechecked. If it - * doesn't match the current generation, a new asid will be assigned. - * - * 0---------------x------------x---------------------x----| - * ^-next ^-limit ^-MAX_ASID - * - * All asid manipulation & context loading/unloading is protected by the - * gs_lock. - */ - -/* Hit the asid limit. Start over */ -static int gru_wrap_asid(struct gru_state *gru) -{ - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - STAT(asid_wrap); - gru->gs_asid_gen++; - return MIN_ASID; -} - -/* Find the next chunk of unused asids */ -static int gru_reset_asid_limit(struct gru_state *gru, int asid) -{ - int i, gid, inuse_asid, limit; - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - STAT(asid_next); - limit = MAX_ASID; - if (asid >= limit) - asid = gru_wrap_asid(gru); - gru_flush_all_tlb(gru); - gid = gru->gs_gid; -again: - for (i = 0; i < GRU_NUM_CCH; i++) { - if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i])) - continue; - inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid; - gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n", - gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms, - inuse_asid, i); - if (inuse_asid == asid) { - asid += ASID_INC; - if (asid >= limit) { - /* - * empty range: reset the range limit and - * start over - */ - limit = MAX_ASID; - if (asid >= MAX_ASID) - asid = gru_wrap_asid(gru); - goto again; - } - } - - if ((inuse_asid > asid) && (inuse_asid < limit)) - limit = inuse_asid; - } - gru->gs_asid_limit = limit; - gru->gs_asid = asid; - gru_dbg(grudev, "gid %d, new asid 0x%x, new_limit 0x%x\n", gru->gs_gid, - asid, limit); - return asid; -} - -/* Assign a new ASID to a thread context. */ -static int gru_assign_asid(struct gru_state *gru) -{ - int asid; - - gru->gs_asid += ASID_INC; - asid = gru->gs_asid; - if (asid >= gru->gs_asid_limit) - asid = gru_reset_asid_limit(gru, asid); - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - return asid; -} - -/* - * Clear n bits in a word. Return a word indicating the bits that were cleared. - * Optionally, build an array of chars that contain the bit numbers allocated. - */ -static unsigned long reserve_resources(unsigned long *p, int n, int mmax, - signed char *idx) -{ - unsigned long bits = 0; - int i; - - while (n--) { - i = find_first_bit(p, mmax); - if (i == mmax) - BUG(); - __clear_bit(i, p); - __set_bit(i, &bits); - if (idx) - *idx++ = i; - } - return bits; -} - -unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count, - signed char *cbmap) -{ - return reserve_resources(&gru->gs_cbr_map, cbr_au_count, GRU_CBR_AU, - cbmap); -} - -unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count, - signed char *dsmap) -{ - return reserve_resources(&gru->gs_dsr_map, dsr_au_count, GRU_DSR_AU, - dsmap); -} - -static void reserve_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts++; - gts->ts_cbr_map = - gru_reserve_cb_resources(gru, gts->ts_cbr_au_count, - gts->ts_cbr_idx); - gts->ts_dsr_map = - gru_reserve_ds_resources(gru, gts->ts_dsr_au_count, NULL); -} - -static void free_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts--; - gru->gs_cbr_map |= gts->ts_cbr_map; - gru->gs_dsr_map |= gts->ts_dsr_map; -} - -/* - * Check if a GRU has sufficient free resources to satisfy an allocation - * request. Note: GRU locks may or may not be held when this is called. If - * not held, recheck after acquiring the appropriate locks. - * - * Returns 1 if sufficient resources, 0 if not - */ -static int check_gru_resources(struct gru_state *gru, int cbr_au_count, - int dsr_au_count, int max_active_contexts) -{ - return hweight64(gru->gs_cbr_map) >= cbr_au_count - && hweight64(gru->gs_dsr_map) >= dsr_au_count - && gru->gs_active_contexts < max_active_contexts; -} - -/* - * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG - * context. - */ -static int gru_load_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid]; - unsigned short ctxbitmap = (1 << gts->ts_ctxnum); - int asid; - - spin_lock(&gms->ms_asid_lock); - asid = asids->mt_asid; - - spin_lock(&gru->gs_asid_lock); - if (asid == 0 || (asids->mt_ctxbitmap == 0 && asids->mt_asid_gen != - gru->gs_asid_gen)) { - asid = gru_assign_asid(gru); - asids->mt_asid = asid; - asids->mt_asid_gen = gru->gs_asid_gen; - STAT(asid_new); - } else { - STAT(asid_reuse); - } - spin_unlock(&gru->gs_asid_lock); - - BUG_ON(asids->mt_ctxbitmap & ctxbitmap); - asids->mt_ctxbitmap |= ctxbitmap; - if (!test_bit(gru->gs_gid, gms->ms_asidmap)) - __set_bit(gru->gs_gid, gms->ms_asidmap); - spin_unlock(&gms->ms_asid_lock); - - gru_dbg(grudev, - "gid %d, gts %p, gms %p, ctxnum %d, asid 0x%x, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, asid, - gms->ms_asidmap[0]); - return asid; -} - -static void gru_unload_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids; - unsigned short ctxbitmap; - - asids = &gms->ms_asids[gru->gs_gid]; - ctxbitmap = (1 << gts->ts_ctxnum); - spin_lock(&gms->ms_asid_lock); - spin_lock(&gru->gs_asid_lock); - BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap); - asids->mt_ctxbitmap ^= ctxbitmap; - gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]); - spin_unlock(&gru->gs_asid_lock); - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Decrement the reference count on a GTS structure. Free the structure - * if the reference count goes to zero. - */ -void gts_drop(struct gru_thread_state *gts) -{ - if (gts && refcount_dec_and_test(>s->ts_refcnt)) { - if (gts->ts_gms) - gru_drop_mmu_notifier(gts->ts_gms); - kfree(gts); - STAT(gts_free); - } -} - -/* - * Locate the GTS structure for the current thread. - */ -static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data - *vdata, int tsid) -{ - struct gru_thread_state *gts; - - list_for_each_entry(gts, &vdata->vd_head, ts_next) - if (gts->ts_tsid == tsid) - return gts; - return NULL; -} - -/* - * Allocate a thread state structure. - */ -struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid) -{ - struct gru_thread_state *gts; - struct gru_mm_struct *gms; - int bytes; - - bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count); - bytes += sizeof(struct gru_thread_state); - gts = kmalloc(bytes, GFP_KERNEL); - if (!gts) - return ERR_PTR(-ENOMEM); - - STAT(gts_alloc); - memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */ - refcount_set(>s->ts_refcnt, 1); - mutex_init(>s->ts_ctxlock); - gts->ts_cbr_au_count = cbr_au_count; - gts->ts_dsr_au_count = dsr_au_count; - gts->ts_tlb_preload_count = tlb_preload_count; - gts->ts_user_options = options; - gts->ts_user_blade_id = -1; - gts->ts_user_chiplet_id = -1; - gts->ts_tsid = tsid; - gts->ts_ctxnum = NULLCTX; - gts->ts_tlb_int_select = -1; - gts->ts_cch_req_slice = -1; - gts->ts_sizeavail = GRU_SIZEAVAIL(PAGE_SHIFT); - if (vma) { - gts->ts_mm = current->mm; - gts->ts_vma = vma; - gms = gru_register_mmu_notifier(); - if (IS_ERR(gms)) - goto err; - gts->ts_gms = gms; - } - - gru_dbg(grudev, "alloc gts %p\n", gts); - return gts; - -err: - gts_drop(gts); - return ERR_CAST(gms); -} - -/* - * Allocate a vma private data structure. - */ -struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid) -{ - struct gru_vma_data *vdata = NULL; - - vdata = kmalloc_obj(*vdata); - if (!vdata) - return NULL; - - STAT(vdata_alloc); - INIT_LIST_HEAD(&vdata->vd_head); - spin_lock_init(&vdata->vd_lock); - gru_dbg(grudev, "alloc vdata %p\n", vdata); - return vdata; -} - -/* - * Find the thread state structure for the current thread. - */ -struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts; - - spin_lock(&vdata->vd_lock); - gts = gru_find_current_gts_nolock(vdata, tsid); - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Allocate a new thread state for a GSEG. Note that races may allow - * another thread to race to create a gts. - */ -struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts, *ngts; - - gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, - vdata->vd_dsr_au_count, - vdata->vd_tlb_preload_count, - vdata->vd_user_options, tsid); - if (IS_ERR(gts)) - return gts; - - spin_lock(&vdata->vd_lock); - ngts = gru_find_current_gts_nolock(vdata, tsid); - if (ngts) { - gts_drop(gts); - gts = ngts; - STAT(gts_double_allocate); - } else { - list_add(>s->ts_next, &vdata->vd_head); - } - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Free the GRU context assigned to the thread state. - */ -static void gru_free_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru; - - gru = gts->ts_gru; - gru_dbg(grudev, "gts %p, gid %d\n", gts, gru->gs_gid); - - spin_lock(&gru->gs_lock); - gru->gs_gts[gts->ts_ctxnum] = NULL; - free_gru_resources(gru, gts); - BUG_ON(test_bit(gts->ts_ctxnum, &gru->gs_context_map) == 0); - __clear_bit(gts->ts_ctxnum, &gru->gs_context_map); - gts->ts_ctxnum = NULLCTX; - gts->ts_gru = NULL; - gts->ts_blade = -1; - spin_unlock(&gru->gs_lock); - - gts_drop(gts); - STAT(free_context); -} - -/* - * Prefetching cachelines help hardware performance. - * (Strictly a performance enhancement. Not functionally required). - */ -static void prefetch_data(void *p, int num, int stride) -{ - while (num-- > 0) { - prefetchw(p); - p += stride; - } -} - -static inline long gru_copy_handle(void *d, void *s) -{ - memcpy(d, s, GRU_HANDLE_BYTES); - return GRU_HANDLE_BYTES; -} - -static void gru_prefetch_context(void *gseg, void *cb, void *cbe, - unsigned long cbrmap, unsigned long length) -{ - int i, scr; - - prefetch_data(gseg + GRU_DS_BASE, length / GRU_CACHE_LINE_BYTES, - GRU_CACHE_LINE_BYTES); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - prefetch_data(cb, 1, GRU_CACHE_LINE_BYTES); - prefetch_data(cbe + i * GRU_HANDLE_STRIDE, 1, - GRU_CACHE_LINE_BYTES); - cb += GRU_HANDLE_STRIDE; - } -} - -static void gru_load_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap, - int data_valid) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - if (data_valid) { - save += gru_copy_handle(cb, save); - save += gru_copy_handle(cbe + i * GRU_HANDLE_STRIDE, - save); - } else { - memset(cb, 0, GRU_CACHE_LINE_BYTES); - memset(cbe + i * GRU_HANDLE_STRIDE, 0, - GRU_CACHE_LINE_BYTES); - } - /* Flush CBE to hide race in context restart */ - mb(); - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - - if (data_valid) - memcpy(gseg + GRU_DS_BASE, save, length); - else - memset(gseg + GRU_DS_BASE, 0, length); -} - -static void gru_unload_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - - /* CBEs may not be coherent. Flush them from cache */ - for_each_cbr_in_allocation_map(i, &cbrmap, scr) - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - mb(); /* Let the CL flush complete */ - - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - save += gru_copy_handle(save, cb); - save += gru_copy_handle(save, cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - memcpy(save, gseg + GRU_DS_BASE, length); -} - -void gru_unload_context(struct gru_thread_state *gts, int savestate) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int ctxnum = gts->ts_ctxnum; - - if (!is_kernel_context(gts)) - zap_special_vma_range(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE); - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n", - gts, gts->ts_cbr_map, gts->ts_dsr_map); - lock_cch_handle(cch); - if (cch_interrupt_sync(cch)) - BUG(); - - if (!is_kernel_context(gts)) - gru_unload_mm_tracker(gru, gts); - if (savestate) { - gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, - ctxnum, gts->ts_cbr_map, - gts->ts_dsr_map); - gts->ts_data_valid = 1; - } - - if (cch_deallocate(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_free_gru_context(gts); -} - -/* - * Load a GRU context by copying it from the thread data structure in memory - * to the GRU. - */ -void gru_load_context(struct gru_thread_state *gts) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int i, err, asid, ctxnum = gts->ts_ctxnum; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - lock_cch_handle(cch); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - cch->tlb_int_enable = (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch->tlb_int_enable) { - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gts->ts_tlb_int_select; - } - if (gts->ts_cch_req_slice >= 0) { - cch->req_slice_set_enable = 1; - cch->req_slice = gts->ts_cch_req_slice; - } else { - cch->req_slice_set_enable =0; - } - cch->tfm_done_bit_enable = 0; - cch->dsr_allocation_map = gts->ts_dsr_map; - cch->cbr_allocation_map = gts->ts_cbr_map; - - if (is_kernel_context(gts)) { - cch->unmap_enable = 1; - cch->tfm_done_bit_enable = 1; - cch->cb_int_enable = 1; - cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */ - } else { - cch->unmap_enable = 0; - cch->tfm_done_bit_enable = 0; - cch->cb_int_enable = 0; - asid = gru_load_mm_tracker(gru, gts); - for (i = 0; i < 8; i++) { - cch->asid[i] = asid + i; - cch->sizeavail[i] = gts->ts_sizeavail; - } - } - - err = cch_allocate(cch); - if (err) { - gru_dbg(grudev, - "err %d: cch %p, gts %p, cbr 0x%lx, dsr 0x%lx\n", - err, cch, gts, gts->ts_cbr_map, gts->ts_dsr_map); - BUG(); - } - - gru_load_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, ctxnum, - gts->ts_cbr_map, gts->ts_dsr_map, gts->ts_data_valid); - - if (cch_start(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n", - gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map, - (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select); -} - -/* - * Update fields in an active CCH: - * - retarget interrupts on local blade - * - update sizeavail mask - */ -int gru_update_cch(struct gru_thread_state *gts) -{ - struct gru_context_configuration_handle *cch; - struct gru_state *gru = gts->ts_gru; - int i, ctxnum = gts->ts_ctxnum, ret = 0; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - lock_cch_handle(cch); - if (cch->state == CCHSTATE_ACTIVE) { - if (gru->gs_gts[gts->ts_ctxnum] != gts) - goto exit; - if (cch_interrupt(cch)) - BUG(); - for (i = 0; i < 8; i++) - cch->sizeavail[i] = gts->ts_sizeavail; - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gru_cpu_fault_map_id(); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch_start(cch)) - BUG(); - ret = 1; - } -exit: - unlock_cch_handle(cch); - return ret; -} - -/* - * Update CCH tlb interrupt select. Required when all the following is true: - * - task's GRU context is loaded into a GRU - * - task is using interrupt notification for TLB faults - * - task has migrated to a different cpu on the same blade where - * it was previously running. - */ -static int gru_retarget_intr(struct gru_thread_state *gts) -{ - if (gts->ts_tlb_int_select < 0 - || gts->ts_tlb_int_select == gru_cpu_fault_map_id()) - return 0; - - gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select, - gru_cpu_fault_map_id()); - return gru_update_cch(gts); -} - -/* - * Check if a GRU context is allowed to use a specific chiplet. By default - * a context is assigned to any blade-local chiplet. However, users can - * override this. - * Returns 1 if assignment allowed, 0 otherwise - */ -static int gru_check_chiplet_assignment(struct gru_state *gru, - struct gru_thread_state *gts) -{ - int blade_id; - int chiplet_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - - chiplet_id = gts->ts_user_chiplet_id; - return gru->gs_blade_id == blade_id && - (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id); -} - -/* - * Unload the gru context if it is not assigned to the correct blade or - * chiplet. Misassignment can occur if the process migrates to a different - * blade or if the user changes the selected blade/chiplet. - */ -int gru_check_context_placement(struct gru_thread_state *gts) -{ - struct gru_state *gru; - int ret = 0; - - /* - * If the current task is the context owner, verify that the - * context is correctly placed. This test is skipped for non-owner - * references. Pthread apps use non-owner references to the CBRs. - */ - gru = gts->ts_gru; - /* - * If gru or gts->ts_tgid_owner isn't initialized properly, return - * success to indicate that the caller does not need to unload the - * gru context.The caller is responsible for their inspection and - * reinitialization if needed. - */ - if (!gru || gts->ts_tgid_owner != current->tgid) - return ret; - - if (!gru_check_chiplet_assignment(gru, gts)) { - STAT(check_context_unload); - ret = -EINVAL; - } else if (gru_retarget_intr(gts)) { - STAT(check_context_retarget_intr); - } - - return ret; -} - - -/* - * Insufficient GRU resources available on the local blade. Steal a context from - * a process. This is a hack until a _real_ resource scheduler is written.... - */ -#define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0) -#define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \ - ((g)+1) : &(b)->bs_grus[0]) - -static int is_gts_stealable(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) - return down_write_trylock(&bs->bs_kgts_sema); - else - return mutex_trylock(>s->ts_ctxlock); -} - -static void gts_stolen(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) { - up_write(&bs->bs_kgts_sema); - STAT(steal_kernel_context); - } else { - mutex_unlock(>s->ts_ctxlock); - STAT(steal_user_context); - } -} - -void gru_steal_context(struct gru_thread_state *gts) -{ - struct gru_blade_state *blade; - struct gru_state *gru, *gru0; - struct gru_thread_state *ngts = NULL; - int ctxnum, ctxnum0, flag = 0, cbr, dsr; - int blade_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - cbr = gts->ts_cbr_au_count; - dsr = gts->ts_dsr_au_count; - - blade = gru_base[blade_id]; - spin_lock(&blade->bs_lock); - - ctxnum = next_ctxnum(blade->bs_lru_ctxnum); - gru = blade->bs_lru_gru; - if (ctxnum == 0) - gru = next_gru(blade, gru); - blade->bs_lru_gru = gru; - blade->bs_lru_ctxnum = ctxnum; - ctxnum0 = ctxnum; - gru0 = gru; - while (1) { - if (gru_check_chiplet_assignment(gru, gts)) { - if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH)) - break; - spin_lock(&gru->gs_lock); - for (; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (flag && gru == gru0 && ctxnum == ctxnum0) - break; - ngts = gru->gs_gts[ctxnum]; - /* - * We are grabbing locks out of order, so trylock is - * needed. GTSs are usually not locked, so the odds of - * success are high. If trylock fails, try to steal a - * different GSEG. - */ - if (ngts && is_gts_stealable(ngts, blade)) - break; - ngts = NULL; - } - spin_unlock(&gru->gs_lock); - if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0)) - break; - } - if (flag && gru == gru0) - break; - flag = 1; - ctxnum = 0; - gru = next_gru(blade, gru); - } - spin_unlock(&blade->bs_lock); - - if (ngts) { - gts->ustats.context_stolen++; - ngts->ts_steal_jiffies = jiffies; - gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1); - gts_stolen(ngts, blade); - } else { - STAT(steal_context_failed); - } - gru_dbg(grudev, - "stole gid %d, ctxnum %d from gts %p. Need cb %d, ds %d;" - " avail cb %ld, ds %ld\n", - gru->gs_gid, ctxnum, ngts, cbr, dsr, hweight64(gru->gs_cbr_map), - hweight64(gru->gs_dsr_map)); -} - -/* - * Assign a gru context. - */ -static int gru_assign_context_number(struct gru_state *gru) -{ - int ctxnum; - - ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH); - __set_bit(ctxnum, &gru->gs_context_map); - return ctxnum; -} - -/* - * Scan the GRUs on the local blade & assign a GRU context. - */ -struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru, *grux; - int i, max_active_contexts; - int blade_id = gts->ts_user_blade_id; - - if (blade_id < 0) - blade_id = uv_numa_blade_id(); -again: - gru = NULL; - max_active_contexts = GRU_NUM_CCH; - for_each_gru_on_blade(grux, blade_id, i) { - if (!gru_check_chiplet_assignment(grux, gts)) - continue; - if (check_gru_resources(grux, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, - max_active_contexts)) { - gru = grux; - max_active_contexts = grux->gs_active_contexts; - if (max_active_contexts == 0) - break; - } - } - - if (gru) { - spin_lock(&gru->gs_lock); - if (!check_gru_resources(gru, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, GRU_NUM_CCH)) { - spin_unlock(&gru->gs_lock); - goto again; - } - reserve_gru_resources(gru, gts); - gts->ts_gru = gru; - gts->ts_blade = gru->gs_blade_id; - gts->ts_ctxnum = gru_assign_context_number(gru); - refcount_inc(>s->ts_refcnt); - gru->gs_gts[gts->ts_ctxnum] = gts; - spin_unlock(&gru->gs_lock); - - STAT(assign_context); - gru_dbg(grudev, - "gseg %p, gts %p, gid %d, ctx %d, cbr %d, dsr %d\n", - gseg_virtual_address(gts->ts_gru, gts->ts_ctxnum), gts, - gts->ts_gru->gs_gid, gts->ts_ctxnum, - gts->ts_cbr_au_count, gts->ts_dsr_au_count); - } else { - gru_dbg(grudev, "failed to allocate a GTS %s\n", ""); - STAT(assign_context_failed); - } - - return gru; -} - -/* - * gru_nopage - * - * Map the user's GRU segment - * - * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. - */ -vm_fault_t gru_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct gru_thread_state *gts; - unsigned long paddr, vaddr; - unsigned long expires; - - vaddr = vmf->address; - gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", - vma, vaddr, GSEG_BASE(vaddr)); - STAT(nopfn); - - /* The following check ensures vaddr is a valid address in the VMA */ - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (!gts) - return VM_FAULT_SIGBUS; - -again: - mutex_lock(>s->ts_ctxlock); - - if (gru_check_context_placement(gts)) { - mutex_unlock(>s->ts_ctxlock); - gru_unload_context(gts, 1); - return VM_FAULT_NOPAGE; - } - - if (!gts->ts_gru) { - STAT(load_user_context); - if (!gru_assign_gru_context(gts)) { - mutex_unlock(>s->ts_ctxlock); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */ - expires = gts->ts_steal_jiffies + GRU_STEAL_DELAY; - if (time_before(expires, jiffies)) - gru_steal_context(gts); - goto again; - } - gru_load_context(gts); - paddr = gseg_physical_address(gts->ts_gru, gts->ts_ctxnum); - remap_pfn_range(vma, vaddr & ~(GRU_GSEG_PAGESIZE - 1), - paddr >> PAGE_SHIFT, GRU_GSEG_PAGESIZE, - vma->vm_page_prot); - } - - mutex_unlock(>s->ts_ctxlock); - - return VM_FAULT_NOPAGE; -} - diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c deleted file mode 100644 index 97b8b38ab47d..000000000000 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ /dev/null @@ -1,308 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * PROC INTERFACES - * - * This file supports the /proc interfaces for the GRU driver - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#define printstat(s, f) printstat_val(s, &gru_stats.f, #f) - -static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id) -{ - unsigned long val = atomic_long_read(v); - - seq_printf(s, "%16lu %s\n", val, id); -} - -static int statistics_show(struct seq_file *s, void *p) -{ - printstat(s, vdata_alloc); - printstat(s, vdata_free); - printstat(s, gts_alloc); - printstat(s, gts_free); - printstat(s, gms_alloc); - printstat(s, gms_free); - printstat(s, gts_double_allocate); - printstat(s, assign_context); - printstat(s, assign_context_failed); - printstat(s, free_context); - printstat(s, load_user_context); - printstat(s, load_kernel_context); - printstat(s, lock_kernel_context); - printstat(s, unlock_kernel_context); - printstat(s, steal_user_context); - printstat(s, steal_kernel_context); - printstat(s, steal_context_failed); - printstat(s, nopfn); - printstat(s, asid_new); - printstat(s, asid_next); - printstat(s, asid_wrap); - printstat(s, asid_reuse); - printstat(s, intr); - printstat(s, intr_cbr); - printstat(s, intr_tfh); - printstat(s, intr_spurious); - printstat(s, intr_mm_lock_failed); - printstat(s, call_os); - printstat(s, call_os_wait_queue); - printstat(s, user_flush_tlb); - printstat(s, user_unload_context); - printstat(s, user_exception); - printstat(s, set_context_option); - printstat(s, check_context_retarget_intr); - printstat(s, check_context_unload); - printstat(s, tlb_dropin); - printstat(s, tlb_preload_page); - printstat(s, tlb_dropin_fail_no_asid); - printstat(s, tlb_dropin_fail_upm); - printstat(s, tlb_dropin_fail_invalid); - printstat(s, tlb_dropin_fail_range_active); - printstat(s, tlb_dropin_fail_idle); - printstat(s, tlb_dropin_fail_fmm); - printstat(s, tlb_dropin_fail_no_exception); - printstat(s, tfh_stale_on_fault); - printstat(s, mmu_invalidate_range); - printstat(s, mmu_invalidate_page); - printstat(s, flush_tlb); - printstat(s, flush_tlb_gru); - printstat(s, flush_tlb_gru_tgh); - printstat(s, flush_tlb_gru_zero_asid); - printstat(s, copy_gpa); - printstat(s, read_gpa); - printstat(s, mesq_receive); - printstat(s, mesq_receive_none); - printstat(s, mesq_send); - printstat(s, mesq_send_failed); - printstat(s, mesq_noop); - printstat(s, mesq_send_unexpected_error); - printstat(s, mesq_send_lb_overflow); - printstat(s, mesq_send_qlimit_reached); - printstat(s, mesq_send_amo_nacked); - printstat(s, mesq_send_put_nacked); - printstat(s, mesq_qf_locked); - printstat(s, mesq_qf_noop_not_full); - printstat(s, mesq_qf_switch_head_failed); - printstat(s, mesq_qf_unexpected_error); - printstat(s, mesq_noop_unexpected_error); - printstat(s, mesq_noop_lb_overflow); - printstat(s, mesq_noop_qlimit_reached); - printstat(s, mesq_noop_amo_nacked); - printstat(s, mesq_noop_put_nacked); - printstat(s, mesq_noop_page_overflow); - return 0; -} - -static ssize_t statistics_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - memset(&gru_stats, 0, sizeof(gru_stats)); - return count; -} - -static int mcs_statistics_show(struct seq_file *s, void *p) -{ - int op; - unsigned long total, count, max; - static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt", - "cch_interrupt_sync", "cch_deallocate", "tfh_write_only", - "tfh_write_restart", "tgh_invalidate"}; - - seq_puts(s, "#id count aver-clks max-clks\n"); - for (op = 0; op < mcsop_last; op++) { - count = atomic_long_read(&mcs_op_statistics[op].count); - total = atomic_long_read(&mcs_op_statistics[op].total); - max = mcs_op_statistics[op].max; - seq_printf(s, "%-20s%12ld%12ld%12ld\n", id[op], count, - count ? total / count : 0, max); - } - return 0; -} - -static ssize_t mcs_statistics_write(struct file *file, - const char __user *userbuf, size_t count, loff_t *data) -{ - memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics)); - return count; -} - -static int options_show(struct seq_file *s, void *p) -{ - seq_printf(s, "#bitmask: 1=trace, 2=statistics\n"); - seq_printf(s, "0x%lx\n", gru_options); - return 0; -} - -static ssize_t options_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - int ret; - - ret = kstrtoul_from_user(userbuf, count, 0, &gru_options); - if (ret) - return ret; - - return count; -} - -static int cch_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data; - int i; - struct gru_state *gru = GID_TO_GRU(gid); - struct gru_thread_state *ts; - const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; - - if (gid == 0) - seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n"); - if (gru) - for (i = 0; i < GRU_NUM_CCH; i++) { - ts = gru->gs_gts[i]; - if (!ts) - continue; - seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n", - gru->gs_gid, gru->gs_blade_id, i, - is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid, - is_kernel_context(ts) ? 0 : ts->ts_tgid_owner, - ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, - ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, - mode[ts->ts_user_options & - GRU_OPT_MISS_MASK]); - } - - return 0; -} - -static int gru_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data, ctxfree, cbrfree, dsrfree; - struct gru_state *gru = GID_TO_GRU(gid); - - if (gid == 0) { - seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n"); - seq_puts(file, "# busy busy busy free free free\n"); - } - if (gru) { - ctxfree = GRU_NUM_CCH - gru->gs_active_contexts; - cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - seq_printf(file, " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n", - gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree, - GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree, - ctxfree, cbrfree, dsrfree); - } - - return 0; -} - -static void seq_stop(struct seq_file *file, void *data) -{ -} - -static void *seq_start(struct seq_file *file, loff_t *gid) -{ - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static void *seq_next(struct seq_file *file, void *data, loff_t *gid) -{ - (*gid)++; - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static const struct seq_operations cch_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = cch_seq_show -}; - -static const struct seq_operations gru_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = gru_seq_show -}; - -static int statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, statistics_show, NULL); -} - -static int mcs_statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, mcs_statistics_show, NULL); -} - -static int options_open(struct inode *inode, struct file *file) -{ - return single_open(file, options_show, NULL); -} - -/* *INDENT-OFF* */ -static const struct proc_ops statistics_proc_ops = { - .proc_open = statistics_open, - .proc_read = seq_read, - .proc_write = statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops mcs_statistics_proc_ops = { - .proc_open = mcs_statistics_open, - .proc_read = seq_read, - .proc_write = mcs_statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops options_proc_ops = { - .proc_open = options_open, - .proc_read = seq_read, - .proc_write = options_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static struct proc_dir_entry *proc_gru __read_mostly; - -int gru_proc_init(void) -{ - proc_gru = proc_mkdir("sgi_uv/gru", NULL); - if (!proc_gru) - return -1; - if (!proc_create("statistics", 0644, proc_gru, &statistics_proc_ops)) - goto err; - if (!proc_create("mcs_statistics", 0644, proc_gru, &mcs_statistics_proc_ops)) - goto err; - if (!proc_create("debug_options", 0644, proc_gru, &options_proc_ops)) - goto err; - if (!proc_create_seq("cch_status", 0444, proc_gru, &cch_seq_ops)) - goto err; - if (!proc_create_seq("gru_status", 0444, proc_gru, &gru_seq_ops)) - goto err; - return 0; -err: - remove_proc_subtree("sgi_uv/gru", NULL); - return -1; -} - -void gru_proc_exit(void) -{ - remove_proc_subtree("sgi_uv/gru", NULL); -} diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h deleted file mode 100644 index 640daf1994df..000000000000 --- a/drivers/misc/sgi-gru/grutables.h +++ /dev/null @@ -1,659 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU DRIVER TABLES, MACROS, externs, etc - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUTABLES_H__ -#define __GRUTABLES_H__ - -/* - * GRU Chiplet: - * The GRU is a user addressible memory accelerator. It provides - * several forms of load, store, memset, bcopy instructions. In addition, it - * contains special instructions for AMOs, sending messages to message - * queues, etc. - * - * The GRU is an integral part of the node controller. It connects - * directly to the cpu socket. In its current implementation, there are 2 - * GRU chiplets in the node controller on each blade (~node). - * - * The entire GRU memory space is fully coherent and cacheable by the cpus. - * - * Each GRU chiplet has a physical memory map that looks like the following: - * - * +-----------------+ - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * +-----------------+ - * | system control | - * +-----------------+ _______ +-------------+ - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / | instructions| - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / |-------------| - * |/////////////////| / | | - * +-----------------+ | | - * | context 15 | | data | - * +-----------------+ | | - * | ...... | \ | | - * +-----------------+ \____________ +-------------+ - * | context 1 | - * +-----------------+ - * | context 0 | - * +-----------------+ - * - * Each of the "contexts" is a chunk of memory that can be mmaped into user - * space. The context consists of 2 parts: - * - * - an instruction space that can be directly accessed by the user - * to issue GRU instructions and to check instruction status. - * - * - a data area that acts as normal RAM. - * - * User instructions contain virtual addresses of data to be accessed by the - * GRU. The GRU contains a TLB that is used to convert these user virtual - * addresses to physical addresses. - * - * The "system control" area of the GRU chiplet is used by the kernel driver - * to manage user contexts and to perform functions such as TLB dropin and - * purging. - * - * One context may be reserved for the kernel and used for cross-partition - * communication. The GRU will also be used to asynchronously zero out - * large blocks of memory (not currently implemented). - * - * - * Tables: - * - * VDATA-VMA Data - Holds a few parameters. Head of linked list of - * GTS tables for threads using the GSEG - * GTS - Gru Thread State - contains info for managing a GSEG context. A - * GTS is allocated for each thread accessing a - * GSEG. - * GTD - GRU Thread Data - contains shadow copy of GRU data when GSEG is - * not loaded into a GRU - * GMS - GRU Memory Struct - Used to manage TLB shootdowns. Tracks GRUs - * where a GSEG has been loaded. Similar to - * an mm_struct but for GRU. - * - * GS - GRU State - Used to manage the state of a GRU chiplet - * BS - Blade State - Used to manage state of all GRU chiplets - * on a blade - * - * - * Normal task tables for task using GRU. - * - 2 threads in process - * - 2 GSEGs open in process - * - GSEG1 is being used by both threads - * - GSEG2 is used only by thread 2 - * - * task -->| - * task ---+---> mm ->------ (notifier) -------+-> gms - * | | - * |--> vma -> vdata ---> gts--->| GSEG1 (thread1) - * | | | - * | +-> gts--->| GSEG1 (thread2) - * | | - * |--> vma -> vdata ---> gts--->| GSEG2 (thread2) - * . - * . - * - * GSEGs are marked DONTCOPY on fork - * - * At open - * file.private_data -> NULL - * - * At mmap, - * vma -> vdata - * - * After gseg reference - * vma -> vdata ->gts - * - * After fork - * parent - * vma -> vdata -> gts - * child - * (vma is not copied) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "gruhandles.h" - -extern struct gru_stats_s gru_stats; -extern struct gru_blade_state *gru_base[]; -extern unsigned long gru_start_paddr, gru_end_paddr; -extern void *gru_start_vaddr; -extern unsigned int gru_max_gids; - -#define GRU_MAX_BLADES MAX_NUMNODES -#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE) - -#define GRU_DRIVER_ID_STR "SGI GRU Device Driver" -#define GRU_DRIVER_VERSION_STR "0.85" - -/* - * GRU statistics. - */ -struct gru_stats_s { - atomic_long_t vdata_alloc; - atomic_long_t vdata_free; - atomic_long_t gts_alloc; - atomic_long_t gts_free; - atomic_long_t gms_alloc; - atomic_long_t gms_free; - atomic_long_t gts_double_allocate; - atomic_long_t assign_context; - atomic_long_t assign_context_failed; - atomic_long_t free_context; - atomic_long_t load_user_context; - atomic_long_t load_kernel_context; - atomic_long_t lock_kernel_context; - atomic_long_t unlock_kernel_context; - atomic_long_t steal_user_context; - atomic_long_t steal_kernel_context; - atomic_long_t steal_context_failed; - atomic_long_t nopfn; - atomic_long_t asid_new; - atomic_long_t asid_next; - atomic_long_t asid_wrap; - atomic_long_t asid_reuse; - atomic_long_t intr; - atomic_long_t intr_cbr; - atomic_long_t intr_tfh; - atomic_long_t intr_spurious; - atomic_long_t intr_mm_lock_failed; - atomic_long_t call_os; - atomic_long_t call_os_wait_queue; - atomic_long_t user_flush_tlb; - atomic_long_t user_unload_context; - atomic_long_t user_exception; - atomic_long_t set_context_option; - atomic_long_t check_context_retarget_intr; - atomic_long_t check_context_unload; - atomic_long_t tlb_dropin; - atomic_long_t tlb_preload_page; - atomic_long_t tlb_dropin_fail_no_asid; - atomic_long_t tlb_dropin_fail_upm; - atomic_long_t tlb_dropin_fail_invalid; - atomic_long_t tlb_dropin_fail_range_active; - atomic_long_t tlb_dropin_fail_idle; - atomic_long_t tlb_dropin_fail_fmm; - atomic_long_t tlb_dropin_fail_no_exception; - atomic_long_t tfh_stale_on_fault; - atomic_long_t mmu_invalidate_range; - atomic_long_t mmu_invalidate_page; - atomic_long_t flush_tlb; - atomic_long_t flush_tlb_gru; - atomic_long_t flush_tlb_gru_tgh; - atomic_long_t flush_tlb_gru_zero_asid; - - atomic_long_t copy_gpa; - atomic_long_t read_gpa; - - atomic_long_t mesq_receive; - atomic_long_t mesq_receive_none; - atomic_long_t mesq_send; - atomic_long_t mesq_send_failed; - atomic_long_t mesq_noop; - atomic_long_t mesq_send_unexpected_error; - atomic_long_t mesq_send_lb_overflow; - atomic_long_t mesq_send_qlimit_reached; - atomic_long_t mesq_send_amo_nacked; - atomic_long_t mesq_send_put_nacked; - atomic_long_t mesq_page_overflow; - atomic_long_t mesq_qf_locked; - atomic_long_t mesq_qf_noop_not_full; - atomic_long_t mesq_qf_switch_head_failed; - atomic_long_t mesq_qf_unexpected_error; - atomic_long_t mesq_noop_unexpected_error; - atomic_long_t mesq_noop_lb_overflow; - atomic_long_t mesq_noop_qlimit_reached; - atomic_long_t mesq_noop_amo_nacked; - atomic_long_t mesq_noop_put_nacked; - atomic_long_t mesq_noop_page_overflow; - -}; - -enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync, - cchop_deallocate, tfhop_write_only, tfhop_write_restart, - tghop_invalidate, mcsop_last}; - -struct mcs_op_statistic { - atomic_long_t count; - atomic_long_t total; - unsigned long max; -}; - -extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -#define OPT_DPRINT 1 -#define OPT_STATS 2 - - -#define IRQ_GRU 110 /* Starting IRQ number for interrupts */ - -/* Delay in jiffies between attempts to assign a GRU context */ -#define GRU_ASSIGN_DELAY ((HZ * 20) / 1000) - -/* - * If a process has it's context stolen, min delay in jiffies before trying to - * steal a context from another process. - */ -#define GRU_STEAL_DELAY ((HZ * 200) / 1000) - -#define STAT(id) do { \ - if (gru_options & OPT_STATS) \ - atomic_long_inc(&gru_stats.id); \ - } while (0) - -#ifdef CONFIG_SGI_GRU_DEBUG -#define gru_dbg(dev, fmt, x...) \ - do { \ - if (gru_options & OPT_DPRINT) \ - printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\ - } while (0) -#else -#define gru_dbg(x...) -#endif - -/*----------------------------------------------------------------------------- - * ASID management - */ -#define MAX_ASID 0xfffff0 -#define MIN_ASID 8 -#define ASID_INC 8 /* number of regions */ - -/* Generate a GRU asid value from a GRU base asid & a virtual address. */ -#define VADDR_HI_BIT 64 -#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3) -#define GRUASID(asid, addr) ((asid) + GRUREGION(addr)) - -/*------------------------------------------------------------------------------ - * File & VMS Tables - */ - -struct gru_state; - -/* - * This structure is pointed to from the mmstruct via the notifier pointer. - * There is one of these per address space. - */ -struct gru_mm_tracker { /* pack to reduce size */ - unsigned int mt_asid_gen:24; /* ASID wrap count */ - unsigned int mt_asid:24; /* current base ASID for gru */ - unsigned short mt_ctxbitmap:16;/* bitmap of contexts using - asid */ -} __attribute__ ((packed)); - -struct gru_mm_struct { - struct mmu_notifier ms_notifier; - spinlock_t ms_asid_lock; /* protects ASID assignment */ - atomic_t ms_range_active;/* num range_invals active */ - wait_queue_head_t ms_wait_queue; - DECLARE_BITMAP(ms_asidmap, GRU_MAX_GRUS); - struct gru_mm_tracker ms_asids[GRU_MAX_GRUS]; -}; - -/* - * One of these structures is allocated when a GSEG is mmaped. The - * structure is pointed to by the vma->vm_private_data field in the vma struct. - */ -struct gru_vma_data { - spinlock_t vd_lock; /* Serialize access to vma */ - struct list_head vd_head; /* head of linked list of gts */ - long vd_user_options;/* misc user option flags */ - int vd_cbr_au_count; - int vd_dsr_au_count; - unsigned char vd_tlb_preload_count; -}; - -/* - * One of these is allocated for each thread accessing a mmaped GRU. A linked - * list of these structure is hung off the struct gru_vma_data in the mm_struct. - */ -struct gru_thread_state { - struct list_head ts_next; /* list - head at vma-private */ - struct mutex ts_ctxlock; /* load/unload CTX lock */ - struct mm_struct *ts_mm; /* mm currently mapped to - context */ - struct vm_area_struct *ts_vma; /* vma of GRU context */ - struct gru_state *ts_gru; /* GRU where the context is - loaded */ - struct gru_mm_struct *ts_gms; /* asid & ioproc struct */ - unsigned char ts_tlb_preload_count; /* TLB preload pages */ - unsigned long ts_cbr_map; /* map of allocated CBRs */ - unsigned long ts_dsr_map; /* map of allocated DATA - resources */ - unsigned long ts_steal_jiffies;/* jiffies when context last - stolen */ - long ts_user_options;/* misc user option flags */ - pid_t ts_tgid_owner; /* task that is using the - context - for migration */ - short ts_user_blade_id;/* user selected blade */ - signed char ts_user_chiplet_id;/* user selected chiplet */ - unsigned short ts_sizeavail; /* Pagesizes in use */ - int ts_tsid; /* thread that owns the - structure */ - int ts_tlb_int_select;/* target cpu if interrupts - enabled */ - int ts_ctxnum; /* context number where the - context is loaded */ - refcount_t ts_refcnt; /* reference count GTS */ - unsigned char ts_dsr_au_count;/* Number of DSR resources - required for contest */ - unsigned char ts_cbr_au_count;/* Number of CBR resources - required for contest */ - signed char ts_cch_req_slice;/* CCH packet slice */ - signed char ts_blade; /* If >= 0, migrate context if - ref from different blade */ - signed char ts_force_cch_reload; - signed char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each - allocated CB */ - int ts_data_valid; /* Indicates if ts_gdata has - valid data */ - struct gru_gseg_statistics ustats; /* User statistics */ - unsigned long ts_gdata[]; /* save area for GRU data (CB, - DS, CBE) */ -}; - -/* - * Threaded programs actually allocate an array of GSEGs when a context is - * created. Each thread uses a separate GSEG. TSID is the index into the GSEG - * array. - */ -#define TSID(a, v) (((a) - (v)->vm_start) / GRU_GSEG_PAGESIZE) -#define UGRUADDR(gts) ((gts)->ts_vma->vm_start + \ - (gts)->ts_tsid * GRU_GSEG_PAGESIZE) - -#define NULLCTX (-1) /* if context not loaded into GRU */ - -/*----------------------------------------------------------------------------- - * GRU State Tables - */ - -/* - * One of these exists for each GRU chiplet. - */ -struct gru_state { - struct gru_blade_state *gs_blade; /* GRU state for entire - blade */ - unsigned long gs_gru_base_paddr; /* Physical address of - gru segments (64) */ - void *gs_gru_base_vaddr; /* Virtual address of - gru segments (64) */ - unsigned short gs_gid; /* unique GRU number */ - unsigned short gs_blade_id; /* blade of GRU */ - unsigned char gs_chiplet_id; /* blade chiplet of GRU */ - unsigned char gs_tgh_local_shift; /* used to pick TGH for - local flush */ - unsigned char gs_tgh_first_remote; /* starting TGH# for - remote flush */ - spinlock_t gs_asid_lock; /* lock used for - assigning asids */ - spinlock_t gs_lock; /* lock used for - assigning contexts */ - - /* -- the following are protected by the gs_asid_lock spinlock ---- */ - unsigned int gs_asid; /* Next availe ASID */ - unsigned int gs_asid_limit; /* Limit of available - ASIDs */ - unsigned int gs_asid_gen; /* asid generation. - Inc on wrap */ - - /* --- the following fields are protected by the gs_lock spinlock --- */ - unsigned long gs_context_map; /* bitmap to manage - contexts in use */ - unsigned long gs_cbr_map; /* bitmap to manage CB - resources */ - unsigned long gs_dsr_map; /* bitmap used to manage - DATA resources */ - unsigned int gs_reserved_cbrs; /* Number of kernel- - reserved cbrs */ - unsigned int gs_reserved_dsr_bytes; /* Bytes of kernel- - reserved dsrs */ - unsigned short gs_active_contexts; /* number of contexts - in use */ - struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using - the context */ - int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */ -}; - -/* - * This structure contains the GRU state for all the GRUs on a blade. - */ -struct gru_blade_state { - void *kernel_cb; /* First kernel - reserved cb */ - void *kernel_dsr; /* First kernel - reserved DSR */ - struct rw_semaphore bs_kgts_sema; /* lock for kgts */ - struct gru_thread_state *bs_kgts; /* GTS for kernel use */ - - /* ---- the following are used for managing kernel async GRU CBRs --- */ - int bs_async_dsr_bytes; /* DSRs for async */ - int bs_async_cbrs; /* CBRs AU for async */ - struct completion *bs_async_wq; - - /* ---- the following are protected by the bs_lock spinlock ---- */ - spinlock_t bs_lock; /* lock used for - stealing contexts */ - int bs_lru_ctxnum; /* STEAL - last context - stolen */ - struct gru_state *bs_lru_gru; /* STEAL - last gru - stolen */ - - struct gru_state bs_grus[GRU_CHIPLETS_PER_BLADE]; -}; - -/*----------------------------------------------------------------------------- - * Address Primitives - */ -#define get_tfm_for_cpu(g, c) \ - ((struct gru_tlb_fault_map *)get_tfm((g)->gs_gru_base_vaddr, (c))) -#define get_tfh_by_index(g, i) \ - ((struct gru_tlb_fault_handle *)get_tfh((g)->gs_gru_base_vaddr, (i))) -#define get_tgh_by_index(g, i) \ - ((struct gru_tlb_global_handle *)get_tgh((g)->gs_gru_base_vaddr, (i))) -#define get_cbe_by_index(g, i) \ - ((struct gru_control_block_extended *)get_cbe((g)->gs_gru_base_vaddr,\ - (i))) - -/*----------------------------------------------------------------------------- - * Useful Macros - */ - -/* Given a blade# & chiplet#, get a pointer to the GRU */ -#define get_gru(b, c) (&gru_base[b]->bs_grus[c]) - -/* Number of bytes to save/restore when unloading/loading GRU contexts */ -#define DSR_BYTES(dsr) ((dsr) * GRU_DSR_AU_BYTES) -#define CBR_BYTES(cbr) ((cbr) * GRU_HANDLE_BYTES * GRU_CBR_AU_SIZE * 2) - -/* Convert a user CB number to the actual CBRNUM */ -#define thread_cbr_number(gts, n) ((gts)->ts_cbr_idx[(n) / GRU_CBR_AU_SIZE] \ - * GRU_CBR_AU_SIZE + (n) % GRU_CBR_AU_SIZE) - -/* Convert a gid to a pointer to the GRU */ -#define GID_TO_GRU(gid) \ - (gru_base[(gid) / GRU_CHIPLETS_PER_BLADE] ? \ - (&gru_base[(gid) / GRU_CHIPLETS_PER_BLADE]-> \ - bs_grus[(gid) % GRU_CHIPLETS_PER_BLADE]) : \ - NULL) - -/* Scan all active GRUs in a GRU bitmap */ -#define for_each_gru_in_bitmap(gid, map) \ - for_each_set_bit((gid), (map), GRU_MAX_GRUS) - -/* Scan all active GRUs on a specific blade */ -#define for_each_gru_on_blade(gru, nid, i) \ - for ((gru) = gru_base[nid]->bs_grus, (i) = 0; \ - (i) < GRU_CHIPLETS_PER_BLADE; \ - (i)++, (gru)++) - -/* Scan all GRUs */ -#define foreach_gid(gid) \ - for ((gid) = 0; (gid) < gru_max_gids; (gid)++) - -/* Scan all active GTSs on a gru. Note: must hold ss_lock to use this macro. */ -#define for_each_gts_on_gru(gts, gru, ctxnum) \ - for ((ctxnum) = 0; (ctxnum) < GRU_NUM_CCH; (ctxnum)++) \ - if (((gts) = (gru)->gs_gts[ctxnum])) - -/* Scan each CBR whose bit is set in a TFM (or copy of) */ -#define for_each_cbr_in_tfm(i, map) \ - for_each_set_bit((i), (map), GRU_NUM_CBE) - -/* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ -#define for_each_cbr_in_allocation_map(i, map, k) \ - for_each_set_bit((k), (map), GRU_CBR_AU) \ - for ((i) = (k)*GRU_CBR_AU_SIZE; \ - (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) - -#define gseg_physical_address(gru, ctxnum) \ - ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE) -#define gseg_virtual_address(gru, ctxnum) \ - ((gru)->gs_gru_base_vaddr + ctxnum * GRU_GSEG_STRIDE) - -/*----------------------------------------------------------------------------- - * Lock / Unlock GRU handles - * Use the "delresp" bit in the handle as a "lock" bit. - */ - -/* Lock hierarchy checking enabled only in emulator */ - -/* 0 = lock failed, 1 = locked */ -static inline int __trylock_handle(void *h) -{ - return !test_and_set_bit(1, h); -} - -static inline void __lock_handle(void *h) -{ - while (test_and_set_bit(1, h)) - cpu_relax(); -} - -static inline void __unlock_handle(void *h) -{ - clear_bit(1, h); -} - -static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch) -{ - return __trylock_handle(cch); -} - -static inline void lock_cch_handle(struct gru_context_configuration_handle *cch) -{ - __lock_handle(cch); -} - -static inline void unlock_cch_handle(struct gru_context_configuration_handle - *cch) -{ - __unlock_handle(cch); -} - -static inline void lock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __lock_handle(tgh); -} - -static inline void unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __unlock_handle(tgh); -} - -static inline int is_kernel_context(struct gru_thread_state *gts) -{ - return !gts->ts_mm; -} - -/* - * The following are for Nehelem-EX. A more general scheme is needed for - * future processors. - */ -#define UV_MAX_INT_CORES 8 -#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1) -#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1) -#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \ - ((cpu_physical_id(p) >> 1) & 3)) -/*----------------------------------------------------------------------------- - * Function prototypes & externs - */ -struct gru_unload_context_req; - -extern const struct vm_operations_struct gru_vm_ops; -extern struct device *grudev; - -extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, - int tsid); -extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts); -extern void gru_load_context(struct gru_thread_state *gts); -extern void gru_steal_context(struct gru_thread_state *gts); -extern void gru_unload_context(struct gru_thread_state *gts, int savestate); -extern int gru_update_cch(struct gru_thread_state *gts); -extern void gts_drop(struct gru_thread_state *gts); -extern void gru_tgh_flush_init(struct gru_state *gru); -extern int gru_kservices_init(void); -extern void gru_kservices_exit(void); -extern irqreturn_t gru0_intr(int irq, void *dev_id); -extern irqreturn_t gru1_intr(int irq, void *dev_id); -extern irqreturn_t gru_intr_mblade(int irq, void *dev_id); -extern int gru_dump_chiplet_request(unsigned long arg); -extern long gru_get_gseg_statistics(unsigned long arg); -extern int gru_handle_user_call_os(unsigned long address); -extern int gru_user_flush_tlb(unsigned long arg); -extern int gru_user_unload_context(unsigned long arg); -extern int gru_get_exception_detail(unsigned long arg); -extern int gru_set_context_option(unsigned long address); -extern int gru_check_context_placement(struct gru_thread_state *gts); -extern int gru_cpu_fault_map_id(void); -extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); -extern void gru_flush_all_tlb(struct gru_state *gru); -extern int gru_proc_init(void); -extern void gru_proc_exit(void); - -extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid); -extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, - int cbr_au_count, signed char *cbmap); -extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, - int dsr_au_count, signed char *dsmap); -extern vm_fault_t gru_fault(struct vm_fault *vmf); -extern struct gru_mm_struct *gru_register_mmu_notifier(void); -extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); - -extern int gru_ktest(unsigned long arg); -extern void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len); - -extern unsigned long gru_options; - -#endif /* __GRUTABLES_H__ */ diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c deleted file mode 100644 index a8121d40be68..000000000000 --- a/drivers/misc/sgi-gru/grutlbpurge.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * MMUOPS callbacks + TLB flushing - * - * This file handles emu notifier callbacks from the core kernel. The callbacks - * are used to update the TLB in the GRU as a result of changes in the - * state of a process address space. This file also handles TLB invalidates - * from the GRU driver. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include - -#define gru_random() get_cycles() - -/* ---------------------------------- TLB Invalidation functions -------- - * get_tgh_handle - * - * Find a TGH to use for issuing a TLB invalidate. For GRUs that are on the - * local blade, use a fixed TGH that is a function of the blade-local cpu - * number. Normally, this TGH is private to the cpu & no contention occurs for - * the TGH. For offblade GRUs, select a random TGH in the range above the - * private TGHs. A spinlock is required to access this TGH & the lock must be - * released when the invalidate is completes. This sucks, but it is the best we - * can do. - * - * Note that the spinlock is IN the TGH handle so locking does not involve - * additional cache lines. - * - */ -static inline int get_off_blade_tgh(struct gru_state *gru) -{ - int n; - - n = GRU_NUM_TGH - gru->gs_tgh_first_remote; - n = gru_random() % n; - n += gru->gs_tgh_first_remote; - return n; -} - -static inline int get_on_blade_tgh(struct gru_state *gru) -{ - return uv_blade_processor_id() >> gru->gs_tgh_local_shift; -} - -static struct gru_tlb_global_handle *get_lock_tgh_handle(struct gru_state - *gru) -{ - struct gru_tlb_global_handle *tgh; - int n; - - if (uv_numa_blade_id() == gru->gs_blade_id) - n = get_on_blade_tgh(gru); - else - n = get_off_blade_tgh(gru); - tgh = get_tgh_by_index(gru, n); - lock_tgh_handle(tgh); - - return tgh; -} - -static void get_unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - unlock_tgh_handle(tgh); -} - -/* - * gru_flush_tlb_range - * - * General purpose TLB invalidation function. This function scans every GRU in - * the ENTIRE system (partition) looking for GRUs where the specified MM has - * been accessed by the GRU. For each GRU found, the TLB must be invalidated OR - * the ASID invalidated. Invalidating an ASID causes a new ASID to be assigned - * on the next fault. This effectively flushes the ENTIRE TLB for the MM at the - * cost of (possibly) a large number of future TLBmisses. - * - * The current algorithm is optimized based on the following (somewhat true) - * assumptions: - * - GRU contexts are not loaded into a GRU unless a reference is made to - * the data segment or control block (this is true, not an assumption). - * If a DS/CB is referenced, the user will also issue instructions that - * cause TLBmisses. It is not necessary to optimize for the case where - * contexts are loaded but no instructions cause TLB misses. (I know - * this will happen but I'm not optimizing for it). - * - GRU instructions to invalidate TLB entries are SLOOOOWWW - normally - * a few usec but in unusual cases, it could be longer. Avoid if - * possible. - * - intrablade process migration between cpus is not frequent but is - * common. - * - a GRU context is not typically migrated to a different GRU on the - * blade because of intrablade migration - * - interblade migration is rare. Processes migrate their GRU context to - * the new blade. - * - if interblade migration occurs, migration back to the original blade - * is very very rare (ie., no optimization for this case) - * - most GRU instruction operate on a subset of the user REGIONS. Code - * & shared library regions are not likely targets of GRU instructions. - * - * To help improve the efficiency of TLB invalidation, the GMS data - * structure is maintained for EACH address space (MM struct). The GMS is - * also the structure that contains the pointer to the mmu callout - * functions. This structure is linked to the mm_struct for the address space - * using the mmu "register" function. The mmu interfaces are used to - * provide the callbacks for TLB invalidation. The GMS contains: - * - * - asid[maxgrus] array. ASIDs are assigned to a GRU when a context is - * loaded into the GRU. - * - asidmap[maxgrus]. bitmap to make it easier to find non-zero asids in - * the above array - * - ctxbitmap[maxgrus]. Indicates the contexts that are currently active - * in the GRU for the address space. This bitmap must be passed to the - * GRU to do an invalidate. - * - * The current algorithm for invalidating TLBs is: - * - scan the asidmap for GRUs where the context has been loaded, ie, - * asid is non-zero. - * - for each gru found: - * - if the ctxtmap is non-zero, there are active contexts in the - * GRU. TLB invalidate instructions must be issued to the GRU. - * - if the ctxtmap is zero, no context is active. Set the ASID to - * zero to force a full TLB invalidation. This is fast but will - * cause a lot of TLB misses if the context is reloaded onto the - * GRU - * - */ - -void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len) -{ - struct gru_state *gru; - struct gru_mm_tracker *asids; - struct gru_tlb_global_handle *tgh; - unsigned long num; - int grupagesize, pagesize, pageshift, gid, asid; - - /* ZZZ TODO - handle huge pages */ - pageshift = PAGE_SHIFT; - pagesize = (1UL << pageshift); - grupagesize = GRU_PAGESIZE(pageshift); - num = min(((len + pagesize - 1) >> pageshift), GRUMAXINVAL); - - STAT(flush_tlb); - gru_dbg(grudev, "gms %p, start 0x%lx, len 0x%lx, asidmap 0x%lx\n", gms, - start, len, gms->ms_asidmap[0]); - - spin_lock(&gms->ms_asid_lock); - for_each_gru_in_bitmap(gid, gms->ms_asidmap) { - STAT(flush_tlb_gru); - gru = GID_TO_GRU(gid); - asids = gms->ms_asids + gid; - asid = asids->mt_asid; - if (asids->mt_ctxbitmap && asid) { - STAT(flush_tlb_gru_tgh); - asid = GRUASID(asid, start); - gru_dbg(grudev, - " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n", - gid, asid, start, grupagesize, num, asids->mt_ctxbitmap); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0, - num - 1, asids->mt_ctxbitmap); - get_unlock_tgh_handle(tgh); - } else { - STAT(flush_tlb_gru_zero_asid); - asids->mt_asid = 0; - __clear_bit(gru->gs_gid, gms->ms_asidmap); - gru_dbg(grudev, - " CLEARASID gruid %d, asid 0x%x, cbtmap 0x%x, asidmap 0x%lx\n", - gid, asid, asids->mt_ctxbitmap, - gms->ms_asidmap[0]); - } - } - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Flush the entire TLB on a chiplet. - */ -void gru_flush_all_tlb(struct gru_state *gru) -{ - struct gru_tlb_global_handle *tgh; - - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, 0, ~0, 0, 1, 1, GRUMAXINVAL - 1, 0xffff); - get_unlock_tgh_handle(tgh); -} - -/* - * MMUOPS notifier callout functions - */ -static int gru_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - STAT(mmu_invalidate_range); - atomic_inc(&gms->ms_range_active); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms, - range->start, range->end, atomic_read(&gms->ms_range_active)); - gru_flush_tlb_range(gms, range->start, range->end - range->start); - - return 0; -} - -static void gru_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - /* ..._and_test() provides needed barrier */ - (void)atomic_dec_and_test(&gms->ms_range_active); - - wake_up_all(&gms->ms_wait_queue); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n", - gms, range->start, range->end); -} - -static struct mmu_notifier *gru_alloc_notifier(struct mm_struct *mm) -{ - struct gru_mm_struct *gms; - - gms = kzalloc_obj(*gms); - if (!gms) - return ERR_PTR(-ENOMEM); - STAT(gms_alloc); - spin_lock_init(&gms->ms_asid_lock); - init_waitqueue_head(&gms->ms_wait_queue); - - return &gms->ms_notifier; -} - -static void gru_free_notifier(struct mmu_notifier *mn) -{ - kfree(container_of(mn, struct gru_mm_struct, ms_notifier)); - STAT(gms_free); -} - -static const struct mmu_notifier_ops gru_mmuops = { - .invalidate_range_start = gru_invalidate_range_start, - .invalidate_range_end = gru_invalidate_range_end, - .alloc_notifier = gru_alloc_notifier, - .free_notifier = gru_free_notifier, -}; - -struct gru_mm_struct *gru_register_mmu_notifier(void) -{ - struct mmu_notifier *mn; - - mn = mmu_notifier_get_locked(&gru_mmuops, current->mm); - if (IS_ERR(mn)) - return ERR_CAST(mn); - - return container_of(mn, struct gru_mm_struct, ms_notifier); -} - -void gru_drop_mmu_notifier(struct gru_mm_struct *gms) -{ - mmu_notifier_put(&gms->ms_notifier); -} - -/* - * Setup TGH parameters. There are: - * - 24 TGH handles per GRU chiplet - * - a portion (MAX_LOCAL_TGH) of the handles are reserved for - * use by blade-local cpus - * - the rest are used by off-blade cpus. This usage is - * less frequent than blade-local usage. - * - * For now, use 16 handles for local flushes, 8 for remote flushes. If the blade - * has less tan or equal to 16 cpus, each cpu has a unique handle that it can - * use. - */ -#define MAX_LOCAL_TGH 16 - -void gru_tgh_flush_init(struct gru_state *gru) -{ - int cpus, shift = 0, n; - - cpus = uv_blade_nr_possible_cpus(gru->gs_blade_id); - - /* n = cpus rounded up to next power of 2 */ - if (cpus) { - n = 1 << fls(cpus - 1); - - /* - * shift count for converting local cpu# to TGH index - * 0 if cpus <= MAX_LOCAL_TGH, - * 1 if cpus <= 2*MAX_LOCAL_TGH, - * etc - */ - shift = max(0, fls(n - 1) - fls(MAX_LOCAL_TGH - 1)); - } - gru->gs_tgh_local_shift = shift; - - /* first starting TGH index to use for remote purges */ - gru->gs_tgh_first_remote = (cpus + (1 << shift) - 1) >> shift; - -} diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile deleted file mode 100644 index 34c55a4045af..000000000000 --- a/drivers/misc/sgi-xp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for SGI's XP devices. -# - -obj-$(CONFIG_SGI_XP) += xp.o -xp-y := xp_main.o xp_uv.o - -obj-$(CONFIG_SGI_XP) += xpc.o -xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \ - xpc_uv.o - -obj-$(CONFIG_SGI_XP) += xpnet.o diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h deleted file mode 100644 index 3185711beb07..000000000000 --- a/drivers/misc/sgi-xp/xp.h +++ /dev/null @@ -1,341 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * External Cross Partition (XP) structures and defines. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XP_H -#define _DRIVERS_MISC_SGIXP_XP_H - -#include - -#if defined CONFIG_X86_UV -#include -#endif - -#ifdef USE_DBUG_ON -#define DBUG_ON(condition) BUG_ON(condition) -#else -#define DBUG_ON(condition) -#endif - -/* - * Define the maximum number of partitions the system can possibly support. - * It is based on the maximum number of hardware partitionable regions. The - * term 'region' in this context refers to the minimum number of nodes that - * can comprise an access protection grouping. The access protection is in - * regards to memory, IPI and IOI. - * - * The maximum number of hardware partitionable regions is equal to the - * maximum number of nodes in the entire system divided by the minimum number - * of nodes that comprise an access protection grouping. - */ -#define XP_MAX_NPARTITIONS_SN2 64 -#define XP_MAX_NPARTITIONS_UV 256 - -/* - * XPC establishes channel connections between the local partition and any - * other partition that is currently up. Over these channels, kernel-level - * `users' can communicate with their counterparts on the other partitions. - * - * If the need for additional channels arises, one can simply increase - * XPC_MAX_NCHANNELS accordingly. If the day should come where that number - * exceeds the absolute MAXIMUM number of channels possible (eight), then one - * will need to make changes to the XPC code to accommodate for this. - * - * The absolute maximum number of channels possible is limited to eight for - * performance reasons on sn2 hardware. The internal cross partition structures - * require sixteen bytes per channel, and eight allows all of this - * interface-shared info to fit in one 128-byte cacheline. - */ -#define XPC_MEM_CHANNEL 0 /* memory channel number */ -#define XPC_NET_CHANNEL 1 /* network channel number */ - -#define XPC_MAX_NCHANNELS 2 /* max #of channels allowed */ - -#if XPC_MAX_NCHANNELS > 8 -#error XPC_MAX_NCHANNELS exceeds absolute MAXIMUM possible. -#endif - -/* - * Define macro, XPC_MSG_SIZE(), is provided for the user - * that wants to fit as many msg entries as possible in a given memory size - * (e.g. a memory page). - */ -#define XPC_MSG_MAX_SIZE 128 -#define XPC_MSG_HDR_MAX_SIZE 16 -#define XPC_MSG_PAYLOAD_MAX_SIZE (XPC_MSG_MAX_SIZE - XPC_MSG_HDR_MAX_SIZE) - -#define XPC_MSG_SIZE(_payload_size) \ - ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \ - is_uv_system() ? 64 : 128) - - -/* - * Define the return values and values passed to user's callout functions. - * (It is important to add new value codes at the end just preceding - * xpUnknownReason, which must have the highest numerical value.) - */ -enum xp_retval { - xpSuccess = 0, - - xpNotConnected, /* 1: channel is not connected */ - xpConnected, /* 2: channel connected (opened) */ - xpRETIRED1, /* 3: (formerly xpDisconnected) */ - - xpMsgReceived, /* 4: message received */ - xpMsgDelivered, /* 5: message delivered and acknowledged */ - - xpRETIRED2, /* 6: (formerly xpTransferFailed) */ - - xpNoWait, /* 7: operation would require wait */ - xpRetry, /* 8: retry operation */ - xpTimeout, /* 9: timeout in xpc_allocate_msg_wait() */ - xpInterrupted, /* 10: interrupted wait */ - - xpUnequalMsgSizes, /* 11: message size disparity between sides */ - xpInvalidAddress, /* 12: invalid address */ - - xpNoMemory, /* 13: no memory available for XPC structures */ - xpLackOfResources, /* 14: insufficient resources for operation */ - xpUnregistered, /* 15: channel is not registered */ - xpAlreadyRegistered, /* 16: channel is already registered */ - - xpPartitionDown, /* 17: remote partition is down */ - xpNotLoaded, /* 18: XPC module is not loaded */ - xpUnloading, /* 19: this side is unloading XPC module */ - - xpBadMagic, /* 20: XPC MAGIC string not found */ - - xpReactivating, /* 21: remote partition was reactivated */ - - xpUnregistering, /* 22: this side is unregistering channel */ - xpOtherUnregistering, /* 23: other side is unregistering channel */ - - xpCloneKThread, /* 24: cloning kernel thread */ - xpCloneKThreadFailed, /* 25: cloning kernel thread failed */ - - xpNoHeartbeat, /* 26: remote partition has no heartbeat */ - - xpPioReadError, /* 27: PIO read error */ - xpPhysAddrRegFailed, /* 28: registration of phys addr range failed */ - - xpRETIRED3, /* 29: (formerly xpBteDirectoryError) */ - xpRETIRED4, /* 30: (formerly xpBtePoisonError) */ - xpRETIRED5, /* 31: (formerly xpBteWriteError) */ - xpRETIRED6, /* 32: (formerly xpBteAccessError) */ - xpRETIRED7, /* 33: (formerly xpBtePWriteError) */ - xpRETIRED8, /* 34: (formerly xpBtePReadError) */ - xpRETIRED9, /* 35: (formerly xpBteTimeOutError) */ - xpRETIRED10, /* 36: (formerly xpBteXtalkError) */ - xpRETIRED11, /* 37: (formerly xpBteNotAvailable) */ - xpRETIRED12, /* 38: (formerly xpBteUnmappedError) */ - - xpBadVersion, /* 39: bad version number */ - xpVarsNotSet, /* 40: the XPC variables are not set up */ - xpNoRsvdPageAddr, /* 41: unable to get rsvd page's phys addr */ - xpInvalidPartid, /* 42: invalid partition ID */ - xpLocalPartid, /* 43: local partition ID */ - - xpOtherGoingDown, /* 44: other side going down, reason unknown */ - xpSystemGoingDown, /* 45: system is going down, reason unknown */ - xpSystemHalt, /* 46: system is being halted */ - xpSystemReboot, /* 47: system is being rebooted */ - xpSystemPoweroff, /* 48: system is being powered off */ - - xpDisconnecting, /* 49: channel disconnecting (closing) */ - - xpOpenCloseError, /* 50: channel open/close protocol error */ - - xpDisconnected, /* 51: channel disconnected (closed) */ - - xpBteCopyError, /* 52: bte_copy() returned error */ - xpSalError, /* 53: sn SAL error */ - xpRsvdPageNotSet, /* 54: the reserved page is not set up */ - xpPayloadTooBig, /* 55: payload too large for message slot */ - - xpUnsupported, /* 56: unsupported functionality or resource */ - xpNeedMoreInfo, /* 57: more info is needed by SAL */ - - xpGruCopyError, /* 58: gru_copy_gru() returned error */ - xpGruSendMqError, /* 59: gru send message queue related error */ - - xpBadChannelNumber, /* 60: invalid channel number */ - xpBadMsgType, /* 61: invalid message type */ - xpBiosError, /* 62: BIOS error */ - - xpUnknownReason /* 63: unknown reason - must be last in enum */ -}; - -/* - * Define the callout function type used by XPC to update the user on - * connection activity and state changes via the user function registered - * by xpc_connect(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * data - pointer to optional data. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_connect(). - * - * A reason code of xpConnected indicates that a connection has been - * established to the specified partition on the specified channel. The data - * argument indicates the max number of entries allowed in the message queue. - * - * A reason code of xpMsgReceived indicates that a XPC message arrived from - * the specified partition on the specified channel. The data argument - * specifies the address of the message's payload. The user must call - * xpc_received() when finished with the payload. - * - * All other reason codes indicate failure. The data argmument is NULL. - * When a failure reason code is received, one can assume that the channel - * is not connected. - */ -typedef void (*xpc_channel_func) (enum xp_retval reason, short partid, - int ch_number, void *data, void *key); - -/* - * Define the callout function type used by XPC to notify the user of - * messages received and delivered via the user function registered by - * xpc_send_notify(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_send_notify(). - * - * A reason code of xpMsgDelivered indicates that the message was delivered - * to the intended recipient and that they have acknowledged its receipt by - * calling xpc_received(). - * - * All other reason codes indicate failure. - * - * NOTE: The user defined function must be callable by an interrupt handler - * and thus cannot block. - */ -typedef void (*xpc_notify_func) (enum xp_retval reason, short partid, - int ch_number, void *key); - -/* - * The following is a registration entry. There is a global array of these, - * one per channel. It is used to record the connection registration made - * by the users of XPC. As long as a registration entry exists, for any - * partition that comes up, XPC will attempt to establish a connection on - * that channel. Notification that a connection has been made will occur via - * the xpc_channel_func function. - * - * The 'func' field points to the function to call when aynchronous - * notification is required for such events as: a connection established/lost, - * or an incoming message received, or an error condition encountered. A - * non-NULL 'func' field indicates that there is an active registration for - * the channel. - */ -struct xpc_registration { - struct mutex mutex; - xpc_channel_func func; /* function to call */ - void *key; /* pointer to user's key */ - u16 nentries; /* #of msg entries in local msg queue */ - u16 entry_size; /* message queue's message entry size */ - u32 assigned_limit; /* limit on #of assigned kthreads */ - u32 idle_limit; /* limit on #of idle kthreads */ -} ____cacheline_aligned; - -#define XPC_CHANNEL_REGISTERED(_c) (xpc_registrations[_c].func != NULL) - -/* the following are valid xpc_send() or xpc_send_notify() flags */ -#define XPC_WAIT 0 /* wait flag */ -#define XPC_NOWAIT 1 /* no wait flag */ - -struct xpc_interface { - void (*connect) (int); - void (*disconnect) (int); - enum xp_retval (*send) (short, int, u32, void *, u16); - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *); - void (*received) (short, int, void *); - enum xp_retval (*partid_to_nasids) (short, void *); -}; - -extern struct xpc_interface xpc_interface; - -extern void xpc_set_interface(void (*)(int), - void (*)(int), - enum xp_retval (*)(short, int, u32, void *, u16), - enum xp_retval (*)(short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*)(short, int, void *), - enum xp_retval (*)(short, void *)); -extern void xpc_clear_interface(void); - -extern enum xp_retval xpc_connect(int, xpc_channel_func, void *, u16, - u16, u32, u32); -extern void xpc_disconnect(int); - -static inline enum xp_retval -xpc_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - if (!xpc_interface.send) - return xpNotLoaded; - - return xpc_interface.send(partid, ch_number, flags, payload, - payload_size); -} - -static inline enum xp_retval -xpc_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - if (!xpc_interface.send_notify) - return xpNotLoaded; - - return xpc_interface.send_notify(partid, ch_number, flags, payload, - payload_size, func, key); -} - -static inline void -xpc_received(short partid, int ch_number, void *payload) -{ - if (xpc_interface.received) - xpc_interface.received(partid, ch_number, payload); -} - -static inline enum xp_retval -xpc_partid_to_nasids(short partid, void *nasids) -{ - if (!xpc_interface.partid_to_nasids) - return xpNotLoaded; - - return xpc_interface.partid_to_nasids(partid, nasids); -} - -extern short xp_max_npartitions; -extern short xp_partition_id; -extern u8 xp_region_size; - -extern unsigned long (*xp_pa) (void *); -extern unsigned long (*xp_socket_pa) (unsigned long); -extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long, - size_t); -extern int (*xp_cpu_to_nasid) (int); -extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long); -extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long); - -extern struct device *xp; -extern enum xp_retval xp_init_uv(void); -extern void xp_exit_uv(void); - -#endif /* _DRIVERS_MISC_SGIXP_XP_H */ diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c deleted file mode 100644 index 87d156c15f35..000000000000 --- a/drivers/misc/sgi-xp/xp_main.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) base. - * - * XP provides a base from which its users can interact - * with XPC, yet not be dependent on XPC. - * - */ - -#include -#include -#include "xp.h" - -/* define the XP debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xp_dbg_name = { - .name = "xp" -}; - -static struct device xp_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xp_dbg_name -}; - -struct device *xp = &xp_dbg_subname; - -/* max #of partitions possible */ -short xp_max_npartitions; -EXPORT_SYMBOL_GPL(xp_max_npartitions); - -short xp_partition_id; -EXPORT_SYMBOL_GPL(xp_partition_id); - -u8 xp_region_size; -EXPORT_SYMBOL_GPL(xp_region_size); - -unsigned long (*xp_pa) (void *addr); -EXPORT_SYMBOL_GPL(xp_pa); - -unsigned long (*xp_socket_pa) (unsigned long gpa); -EXPORT_SYMBOL_GPL(xp_socket_pa); - -enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa, - const unsigned long src_gpa, size_t len); -EXPORT_SYMBOL_GPL(xp_remote_memcpy); - -int (*xp_cpu_to_nasid) (int cpuid); -EXPORT_SYMBOL_GPL(xp_cpu_to_nasid); - -enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_expand_memprotect); -enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_restrict_memprotect); - -/* - * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level - * users of XPC. - */ -struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS]; -EXPORT_SYMBOL_GPL(xpc_registrations); - -/* - * Initialize the XPC interface to NULL to indicate that XPC isn't loaded. - */ -struct xpc_interface xpc_interface = { }; -EXPORT_SYMBOL_GPL(xpc_interface); - -/* - * XPC calls this when it (the XPC module) has been loaded. - */ -void -xpc_set_interface(void (*connect) (int), - void (*disconnect) (int), - enum xp_retval (*send) (short, int, u32, void *, u16), - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*received) (short, int, void *), - enum xp_retval (*partid_to_nasids) (short, void *)) -{ - xpc_interface.connect = connect; - xpc_interface.disconnect = disconnect; - xpc_interface.send = send; - xpc_interface.send_notify = send_notify; - xpc_interface.received = received; - xpc_interface.partid_to_nasids = partid_to_nasids; -} -EXPORT_SYMBOL_GPL(xpc_set_interface); - -/* - * XPC calls this when it (the XPC module) is being unloaded. - */ -void -xpc_clear_interface(void) -{ - memset(&xpc_interface, 0, sizeof(xpc_interface)); -} -EXPORT_SYMBOL_GPL(xpc_clear_interface); - -/* - * Register for automatic establishment of a channel connection whenever - * a partition comes up. - * - * Arguments: - * - * ch_number - channel # to register for connection. - * func - function to call for asynchronous notification of channel - * state changes (i.e., connection, disconnection, error) and - * the arrival of incoming messages. - * key - pointer to optional user-defined value that gets passed back - * to the user on any callouts made to func. - * payload_size - size in bytes of the XPC message's payload area which - * contains a user-defined message. The user should make - * this large enough to hold their largest message. - * nentries - max #of XPC message entries a message queue can contain. - * The actual number, which is determined when a connection - * is established and may be less then requested, will be - * passed to the user via the xpConnected callout. - * assigned_limit - max number of kthreads allowed to be processing - * messages (per connection) at any given instant. - * idle_limit - max number of kthreads allowed to be idle at any given - * instant. - */ -enum xp_retval -xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size, - u16 nentries, u32 assigned_limit, u32 idle_limit) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - DBUG_ON(payload_size == 0 || nentries == 0); - DBUG_ON(func == NULL); - DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit); - - if (XPC_MSG_SIZE(payload_size) > XPC_MSG_MAX_SIZE) - return xpPayloadTooBig; - - registration = &xpc_registrations[ch_number]; - - if (mutex_lock_interruptible(®istration->mutex) != 0) - return xpInterrupted; - - /* if XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func != NULL) { - mutex_unlock(®istration->mutex); - return xpAlreadyRegistered; - } - - /* register the channel for connection */ - registration->entry_size = XPC_MSG_SIZE(payload_size); - registration->nentries = nentries; - registration->assigned_limit = assigned_limit; - registration->idle_limit = idle_limit; - registration->key = key; - registration->func = func; - - mutex_unlock(®istration->mutex); - - if (xpc_interface.connect) - xpc_interface.connect(ch_number); - - return xpSuccess; -} -EXPORT_SYMBOL_GPL(xpc_connect); - -/* - * Remove the registration for automatic connection of the specified channel - * when a partition comes up. - * - * Before returning this xpc_disconnect() will wait for all connections on the - * specified channel have been closed/torndown. So the caller can be assured - * that they will not be receiving any more callouts from XPC to their - * function registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_disconnect(int ch_number) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - registration = &xpc_registrations[ch_number]; - - /* - * We've decided not to make this a down_interruptible(), since we - * figured XPC's users will just turn around and call xpc_disconnect() - * again anyways, so we might as well wait, if need be. - */ - mutex_lock(®istration->mutex); - - /* if !XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func == NULL) { - mutex_unlock(®istration->mutex); - return; - } - - /* remove the connection registration for the specified channel */ - registration->func = NULL; - registration->key = NULL; - registration->nentries = 0; - registration->entry_size = 0; - registration->assigned_limit = 0; - registration->idle_limit = 0; - - if (xpc_interface.disconnect) - xpc_interface.disconnect(ch_number); - - mutex_unlock(®istration->mutex); - - return; -} -EXPORT_SYMBOL_GPL(xpc_disconnect); - -static int __init -xp_init(void) -{ - enum xp_retval ret; - int ch_number; - - /* initialize the connection registration mutex */ - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) - mutex_init(&xpc_registrations[ch_number].mutex); - - if (is_uv_system()) - ret = xp_init_uv(); - else - ret = 0; - - if (ret != xpSuccess) - return ret; - - return 0; -} - -module_init(xp_init); - -static void __exit -xp_exit(void) -{ - if (is_uv_system()) - xp_exit_uv(); -} - -module_exit(xp_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition (XP) base"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c deleted file mode 100644 index 3faa7eadf679..000000000000 --- a/drivers/misc/sgi-xp/xp_uv.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include -#include -#if defined CONFIG_X86_64 -#include -#endif -#include "../sgi-gru/grukservices.h" -#include "xp.h" - -/* - * Convert a virtual memory address to a physical memory address. - */ -static unsigned long -xp_pa_uv(void *addr) -{ - return uv_gpa(addr); -} - -/* - * Convert a global physical to socket physical address. - */ -static unsigned long -xp_socket_pa_uv(unsigned long gpa) -{ - return uv_gpa_to_soc_phys_ram(gpa); -} - -static enum xp_retval -xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa)); - - BUG_ON(!uv_gpa_in_mmr_space(src_gpa)); - BUG_ON(len != 8); - - ret = gru_read_gpa(dst_va, src_gpa); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - - -static enum xp_retval -xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - - if (uv_gpa_in_mmr_space(src_gpa)) - return xp_remote_mmr_read(dst_gpa, src_gpa, len); - - ret = gru_copy_gpa(dst_gpa, src_gpa, len); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_copy_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - -static int -xp_cpu_to_nasid_uv(int cpuid) -{ - /* ??? Is this same as sn2 nasid in mach/part bitmaps set up by SAL? */ - return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid)); -} - -static enum xp_retval -xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -static enum xp_retval -xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, - UV_MEMPROT_RESTRICT_ACCESS); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -enum xp_retval -xp_init_uv(void) -{ - WARN_ON(!is_uv_system()); - if (!is_uv_system()) - return xpUnsupported; - - xp_max_npartitions = XP_MAX_NPARTITIONS_UV; -#ifdef CONFIG_X86 - xp_partition_id = sn_partition_id; - xp_region_size = sn_region_size; -#endif - xp_pa = xp_pa_uv; - xp_socket_pa = xp_socket_pa_uv; - xp_remote_memcpy = xp_remote_memcpy_uv; - xp_cpu_to_nasid = xp_cpu_to_nasid_uv; - xp_expand_memprotect = xp_expand_memprotect_uv; - xp_restrict_memprotect = xp_restrict_memprotect_uv; - - return xpSuccess; -} - -void -xp_exit_uv(void) -{ - WARN_ON(!is_uv_system()); -} diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h deleted file mode 100644 index 225f2bb84e39..000000000000 --- a/drivers/misc/sgi-xp/xpc.h +++ /dev/null @@ -1,732 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) structures and macros. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XPC_H -#define _DRIVERS_MISC_SGIXP_XPC_H - -#include -#include -#include -#include -#include "xp.h" - -/* - * XPC Version numbers consist of a major and minor number. XPC can always - * talk to versions with same major #, and never talk to versions with a - * different major #. - */ -#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf)) -#define XPC_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPC_VERSION_MINOR(_v) ((_v) & 0xf) - -/* define frequency of the heartbeat and frequency how often it's checked */ -#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */ -#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */ - -/* define the process name of HB checker and the CPU it is pinned to */ -#define XPC_HB_CHECK_THREAD_NAME "xpc_hb" -#define XPC_HB_CHECK_CPU 0 - -/* define the process name of the discovery thread */ -#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery" - -/* - * the reserved page - * - * SAL reserves one page of memory per partition for XPC. Though a full page - * in length (16384 bytes), its starting address is not page aligned, but it - * is cacheline aligned. The reserved page consists of the following: - * - * reserved page header - * - * The first two 64-byte cachelines of the reserved page contain the - * header (struct xpc_rsvd_page). Before SAL initialization has completed, - * SAL has set up the following fields of the reserved page header: - * SAL_signature, SAL_version, SAL_partid, and SAL_nasids_size. The - * other fields are set up by XPC. (xpc_rsvd_page points to the local - * partition's reserved page.) - * - * part_nasids mask - * mach_nasids mask - * - * SAL also sets up two bitmaps (or masks), one that reflects the actual - * nasids in this partition (part_nasids), and the other that reflects - * the actual nasids in the entire machine (mach_nasids). We're only - * interested in the even numbered nasids (which contain the processors - * and/or memory), so we only need half as many bits to represent the - * nasids. When mapping nasid to bit in a mask (or bit to nasid) be sure - * to either divide or multiply by 2. The part_nasids mask is located - * starting at the first cacheline following the reserved page header. The - * mach_nasids mask follows right after the part_nasids mask. The size in - * bytes of each mask is reflected by the reserved page header field - * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids - * and xpc_mach_nasids.) - * - * Immediately following the mach_nasids mask are the XPC variables - * required by other partitions. First are those that are generic to all - * partitions (vars), followed on the next available cacheline by those - * which are partition specific (vars part). These are setup by XPC. - * - * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been - * initialized. - */ -struct xpc_rsvd_page { - u64 SAL_signature; /* SAL: unique signature */ - u64 SAL_version; /* SAL: version */ - short SAL_partid; /* SAL: partition ID */ - short max_npartitions; /* value of XPC_MAX_PARTITIONS */ - u8 version; - u8 pad1[3]; /* align to next u64 in 1st 64-byte cacheline */ - unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */ - union { - struct { - unsigned long heartbeat_gpa; /* phys addr */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr */ - } uv; - } sn; - u64 pad2[9]; /* align to last u64 in 2nd 64-byte cacheline */ - u64 SAL_nasids_size; /* SAL: size of each nasid mask in bytes */ -}; - -#define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */ - -/* the reserved page sizes and offsets */ - -#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)) - -#define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \ - XPC_RP_HEADER_SIZE)) -#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \ - xpc_nasid_mask_nlongs) - - -/* - * The following structure describes the partition's heartbeat info which - * will be periodically read by other partitions to determine whether this - * XPC is still 'alive'. - */ -struct xpc_heartbeat_uv { - unsigned long value; - unsigned long offline; /* if 0, heartbeat should be changing */ -}; - -/* - * Info pertinent to a GRU message queue using a watch list for irq generation. - */ -struct xpc_gru_mq_uv { - void *address; /* address of GRU message queue */ - unsigned int order; /* size of GRU message queue as a power of 2 */ - int irq; /* irq raised when message is received in mq */ - int mmr_blade; /* blade where watchlist was allocated from */ - unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */ - unsigned long mmr_value; /* value of irq mmr located on mmr_blade */ - int watchlist_num; /* number of watchlist allocatd by BIOS */ - void *gru_mq_desc; /* opaque structure used by the GRU driver */ -}; - -/* - * The activate_mq is used to send/receive GRU messages that affect XPC's - * partition active state and channel state. This is uv only. - */ -struct xpc_activate_mq_msghdr_uv { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - short partid; /* sender's partid */ - u8 act_state; /* sender's act_state at time msg sent */ - u8 type; /* message's type */ - unsigned long rp_ts_jiffies; /* timestamp of sender's rp setup by XPC */ -}; - -/* activate_mq defined message types */ -#define XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV 0 - -#define XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV 1 -#define XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV 2 - -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV 3 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV 4 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV 5 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV 6 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV 7 - -#define XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV 8 -#define XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV 9 - -struct xpc_activate_mq_msg_uv { - struct xpc_activate_mq_msghdr_uv hdr; -}; - -struct xpc_activate_mq_msg_activate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - unsigned long rp_gpa; - unsigned long heartbeat_gpa; - unsigned long activate_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_deactivate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closerequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closereply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -struct xpc_activate_mq_msg_chctl_openrequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short entry_size; /* size of notify_mq's GRU messages */ - short local_nentries; /* ??? Is this needed? What is? */ -}; - -struct xpc_activate_mq_msg_chctl_openreply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short remote_nentries; /* ??? Is this needed? What is? */ - short local_nentries; /* ??? Is this needed? What is? */ - unsigned long notify_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_chctl_opencomplete_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -/* - * Functions registered by add_timer() or called by kernel_thread() only - * allow for a single 64-bit argument. The following macros can be used to - * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from - * the passed argument. - */ -#define XPC_PACK_ARGS(_arg1, _arg2) \ - ((((u64)_arg1) & 0xffffffff) | \ - ((((u64)_arg2) & 0xffffffff) << 32)) - -#define XPC_UNPACK_ARG1(_args) (((u64)_args) & 0xffffffff) -#define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff) - -/* - * Define a structure that contains arguments associated with opening and - * closing a channel. - */ -struct xpc_openclose_args { - u16 reason; /* reason why channel is closing */ - u16 entry_size; /* sizeof each message entry */ - u16 remote_nentries; /* #of message entries in remote msg queue */ - u16 local_nentries; /* #of message entries in local msg queue */ - unsigned long local_msgqueue_pa; /* phys addr of local message queue */ -}; - -#define XPC_OPENCLOSE_ARGS_SIZE \ - L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * \ - XPC_MAX_NCHANNELS) - - -/* - * Structures to define a fifo singly-linked list. - */ - -struct xpc_fifo_entry_uv { - struct xpc_fifo_entry_uv *next; -}; - -struct xpc_fifo_head_uv { - struct xpc_fifo_entry_uv *first; - struct xpc_fifo_entry_uv *last; - spinlock_t lock; - int n_entries; -}; - -/* - * The format of a uv XPC notify_mq GRU message is as follows: - * - * A user-defined message resides in the payload area. The max size of the - * payload is defined by the user via xpc_connect(). - * - * The size of a message (payload and header) sent via the GRU must be either 1 - * or 2 GRU_CACHE_LINE_BYTES in length. - */ - -struct xpc_notify_mq_msghdr_uv { - union { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - struct xpc_fifo_entry_uv next; /* FOR XPC INTERNAL USE ONLY */ - } u; - short partid; /* FOR XPC INTERNAL USE ONLY */ - u8 ch_number; /* FOR XPC INTERNAL USE ONLY */ - u8 size; /* FOR XPC INTERNAL USE ONLY */ - unsigned int msg_slot_number; /* FOR XPC INTERNAL USE ONLY */ -}; - -struct xpc_notify_mq_msg_uv { - struct xpc_notify_mq_msghdr_uv hdr; - unsigned long payload; -}; - -/* struct xpc_notify_sn2 type of notification */ - -#define XPC_N_CALL 0x01 /* notify function provided by user */ - -/* - * Define uv's version of the notify entry. It additionally is used to allocate - * a msg slot on the remote partition into which is copied a sent message. - */ -struct xpc_send_msg_slot_uv { - struct xpc_fifo_entry_uv next; - unsigned int msg_slot_number; - xpc_notify_func func; /* user's notify function */ - void *key; /* pointer to user's key */ -}; - -/* - * Define the structure that manages all the stuff required by a channel. In - * particular, they are used to manage the messages sent across the channel. - * - * This structure is private to a partition, and is NOT shared across the - * partition boundary. - * - * There is an array of these structures for each remote partition. It is - * allocated at the time a partition becomes active. The array contains one - * of these structures for each potential channel connection to that partition. - */ - -struct xpc_channel_uv { - void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */ - /* gru mq descriptor */ - - struct xpc_send_msg_slot_uv *send_msg_slots; - void *recv_msg_slots; /* each slot will hold a xpc_notify_mq_msg_uv */ - /* structure plus the user's payload */ - - struct xpc_fifo_head_uv msg_slot_free_list; - struct xpc_fifo_head_uv recv_msg_list; /* deliverable payloads */ -}; - -struct xpc_channel { - short partid; /* ID of remote partition connected */ - spinlock_t lock; /* lock for updating this structure */ - unsigned int flags; /* general flags */ - - enum xp_retval reason; /* reason why channel is disconnect'g */ - int reason_line; /* line# disconnect initiated from */ - - u16 number; /* channel # */ - - u16 entry_size; /* sizeof each msg entry */ - u16 local_nentries; /* #of msg entries in local msg queue */ - u16 remote_nentries; /* #of msg entries in remote msg queue */ - - atomic_t references; /* #of external references to queues */ - - atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */ - wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */ - - u8 delayed_chctl_flags; /* chctl flags received, but delayed */ - /* action until channel disconnected */ - - atomic_t n_to_notify; /* #of msg senders to notify */ - - xpc_channel_func func; /* user's channel function */ - void *key; /* pointer to user's key */ - - struct completion wdisconnect_wait; /* wait for channel disconnect */ - - /* kthread management related fields */ - - atomic_t kthreads_assigned; /* #of kthreads assigned to channel */ - u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */ - atomic_t kthreads_idle; /* #of kthreads idle waiting for work */ - u32 kthreads_idle_limit; /* limit on #of kthreads idle */ - atomic_t kthreads_active; /* #of kthreads actively working */ - - wait_queue_head_t idle_wq; /* idle kthread wait queue */ - - union { - struct xpc_channel_uv uv; - } sn; - -} ____cacheline_aligned; - -/* struct xpc_channel flags */ - -#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */ - -#define XPC_C_ROPENCOMPLETE 0x00000002 /* remote open channel complete */ -#define XPC_C_OPENCOMPLETE 0x00000004 /* local open channel complete */ -#define XPC_C_ROPENREPLY 0x00000008 /* remote open channel reply */ -#define XPC_C_OPENREPLY 0x00000010 /* local open channel reply */ -#define XPC_C_ROPENREQUEST 0x00000020 /* remote open channel request */ -#define XPC_C_OPENREQUEST 0x00000040 /* local open channel request */ - -#define XPC_C_SETUP 0x00000080 /* channel's msgqueues are alloc'd */ -#define XPC_C_CONNECTEDCALLOUT 0x00000100 /* connected callout initiated */ -#define XPC_C_CONNECTEDCALLOUT_MADE \ - 0x00000200 /* connected callout completed */ -#define XPC_C_CONNECTED 0x00000400 /* local channel is connected */ -#define XPC_C_CONNECTING 0x00000800 /* channel is being connected */ - -#define XPC_C_RCLOSEREPLY 0x00001000 /* remote close channel reply */ -#define XPC_C_CLOSEREPLY 0x00002000 /* local close channel reply */ -#define XPC_C_RCLOSEREQUEST 0x00004000 /* remote close channel request */ -#define XPC_C_CLOSEREQUEST 0x00008000 /* local close channel request */ - -#define XPC_C_DISCONNECTED 0x00010000 /* channel is disconnected */ -#define XPC_C_DISCONNECTING 0x00020000 /* channel is being disconnected */ -#define XPC_C_DISCONNECTINGCALLOUT \ - 0x00040000 /* disconnecting callout initiated */ -#define XPC_C_DISCONNECTINGCALLOUT_MADE \ - 0x00080000 /* disconnecting callout completed */ -#define XPC_C_WDISCONNECT 0x00100000 /* waiting for channel disconnect */ - -/* - * The channel control flags (chctl) union consists of a 64-bit variable which - * is divided up into eight bytes, ordered from right to left. Byte zero - * pertains to channel 0, byte one to channel 1, and so on. Each channel's byte - * can have one or more of the chctl flags set in it. - */ - -union xpc_channel_ctl_flags { - u64 all_flags; - u8 flags[XPC_MAX_NCHANNELS]; -}; - -/* chctl flags */ -#define XPC_CHCTL_CLOSEREQUEST 0x01 -#define XPC_CHCTL_CLOSEREPLY 0x02 -#define XPC_CHCTL_OPENREQUEST 0x04 -#define XPC_CHCTL_OPENREPLY 0x08 -#define XPC_CHCTL_OPENCOMPLETE 0x10 -#define XPC_CHCTL_MSGREQUEST 0x20 - -#define XPC_OPENCLOSE_CHCTL_FLAGS \ - (XPC_CHCTL_CLOSEREQUEST | XPC_CHCTL_CLOSEREPLY | \ - XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | \ - XPC_CHCTL_OPENCOMPLETE) -#define XPC_MSG_CHCTL_FLAGS XPC_CHCTL_MSGREQUEST - -static inline int -xpc_any_openclose_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) - return 1; - } - return 0; -} - -static inline int -xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - return 1; - } - return 0; -} - -struct xpc_partition_uv { - unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */ - struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */ - /* partition's heartbeat */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr of parititon's */ - /* activate mq's gru mq */ - /* descriptor */ - void *cached_activate_gru_mq_desc; /* cached copy of partition's */ - /* activate mq's gru mq descriptor */ - struct mutex cached_activate_gru_mq_desc_mutex; - spinlock_t flags_lock; /* protect updating of flags */ - unsigned int flags; /* general flags */ - u8 remote_act_state; /* remote partition's act_state */ - u8 act_state_req; /* act_state request from remote partition */ - enum xp_retval reason; /* reason for deactivate act_state request */ -}; - -/* struct xpc_partition_uv flags */ - -#define XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV 0x00000001 -#define XPC_P_ENGAGED_UV 0x00000002 - -/* struct xpc_partition_uv act_state change requests */ - -#define XPC_P_ASR_ACTIVATE_UV 0x01 -#define XPC_P_ASR_REACTIVATE_UV 0x02 -#define XPC_P_ASR_DEACTIVATE_UV 0x03 - -struct xpc_partition { - - /* XPC HB infrastructure */ - - u8 remote_rp_version; /* version# of partition's rsvd pg */ - unsigned long remote_rp_ts_jiffies; /* timestamp when rsvd pg setup */ - unsigned long remote_rp_pa; /* phys addr of partition's rsvd pg */ - u64 last_heartbeat; /* HB at last read */ - u32 activate_IRQ_rcvd; /* IRQs since activation */ - spinlock_t act_lock; /* protect updating of act_state */ - u8 act_state; /* from XPC HB viewpoint */ - enum xp_retval reason; /* reason partition is deactivating */ - int reason_line; /* line# deactivation initiated from */ - - unsigned long disengage_timeout; /* timeout in jiffies */ - struct timer_list disengage_timer; - - /* XPC infrastructure referencing and teardown control */ - - u8 setup_state; /* infrastructure setup state */ - wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */ - atomic_t references; /* #of references to infrastructure */ - - u8 nchannels; /* #of defined channels supported */ - atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */ - atomic_t nchannels_engaged; /* #of channels engaged with remote part */ - struct xpc_channel *channels; /* array of channel structures */ - - /* fields used for managing channel avialability and activity */ - - union xpc_channel_ctl_flags chctl; /* chctl flags yet to be processed */ - spinlock_t chctl_lock; /* chctl flags lock */ - - void *remote_openclose_args_base; /* base address of kmalloc'd space */ - struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */ - /* args */ - - /* channel manager related fields */ - - atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */ - wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */ - - union { - struct xpc_partition_uv uv; - } sn; - -} ____cacheline_aligned; - -struct xpc_arch_operations { - int (*setup_partitions) (void); - void (*teardown_partitions) (void); - void (*process_activate_IRQ_rcvd) (void); - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *); - int (*setup_rsvd_page) (struct xpc_rsvd_page *); - - void (*allow_hb) (short); - void (*disallow_hb) (short); - void (*disallow_all_hbs) (void); - void (*increment_heartbeat) (void); - void (*offline_heartbeat) (void); - void (*online_heartbeat) (void); - void (*heartbeat_init) (void); - void (*heartbeat_exit) (void); - enum xp_retval (*get_remote_heartbeat) (struct xpc_partition *); - - void (*request_partition_activation) (struct xpc_rsvd_page *, - unsigned long, int); - void (*request_partition_reactivation) (struct xpc_partition *); - void (*request_partition_deactivation) (struct xpc_partition *); - void (*cancel_partition_deactivation_request) (struct xpc_partition *); - enum xp_retval (*setup_ch_structures) (struct xpc_partition *); - void (*teardown_ch_structures) (struct xpc_partition *); - - enum xp_retval (*make_first_contact) (struct xpc_partition *); - - u64 (*get_chctl_all_flags) (struct xpc_partition *); - void (*send_chctl_closerequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_closereply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openrequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openreply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_opencomplete) (struct xpc_channel *, unsigned long *); - void (*process_msg_chctl_flags) (struct xpc_partition *, int); - - enum xp_retval (*save_remote_msgqueue_pa) (struct xpc_channel *, - unsigned long); - - enum xp_retval (*setup_msg_structures) (struct xpc_channel *); - void (*teardown_msg_structures) (struct xpc_channel *); - - void (*indicate_partition_engaged) (struct xpc_partition *); - void (*indicate_partition_disengaged) (struct xpc_partition *); - void (*assume_partition_disengaged) (short); - int (*partition_engaged) (short); - int (*any_partition_engaged) (void); - - int (*n_of_deliverable_payloads) (struct xpc_channel *); - enum xp_retval (*send_payload) (struct xpc_channel *, u32, void *, - u16, u8, xpc_notify_func, void *); - void *(*get_deliverable_payload) (struct xpc_channel *); - void (*received_payload) (struct xpc_channel *, void *); - void (*notify_senders_of_disconnect) (struct xpc_channel *); -}; - -/* struct xpc_partition act_state values (for XPC HB) */ - -#define XPC_P_AS_INACTIVE 0x00 /* partition is not active */ -#define XPC_P_AS_ACTIVATION_REQ 0x01 /* created thread to activate */ -#define XPC_P_AS_ACTIVATING 0x02 /* activation thread started */ -#define XPC_P_AS_ACTIVE 0x03 /* xpc_partition_up() was called */ -#define XPC_P_AS_DEACTIVATING 0x04 /* partition deactivation initiated */ - -#define XPC_DEACTIVATE_PARTITION(_p, _reason) \ - xpc_deactivate_partition(__LINE__, (_p), (_reason)) - -/* struct xpc_partition setup_state values */ - -#define XPC_P_SS_UNSET 0x00 /* infrastructure was never setup */ -#define XPC_P_SS_SETUP 0x01 /* infrastructure is setup */ -#define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */ -#define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */ - -/* number of seconds to wait for other partitions to disengage */ -#define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90 - -/* interval in seconds to print 'waiting deactivation' messages */ -#define XPC_DEACTIVATE_PRINTMSG_INTERVAL 10 - -#define XPC_PARTID(_p) ((short)((_p) - &xpc_partitions[0])) - -/* found in xp_main.c */ -extern struct xpc_registration xpc_registrations[]; - -/* found in xpc_main.c */ -extern struct device *xpc_part; -extern struct device *xpc_chan; -extern struct xpc_arch_operations xpc_arch_ops; -extern int xpc_disengage_timelimit; -extern int xpc_disengage_timedout; -extern int xpc_activate_IRQ_rcvd; -extern spinlock_t xpc_activate_IRQ_rcvd_lock; -extern wait_queue_head_t xpc_activate_IRQ_wq; -extern void *xpc_kzalloc_cacheline_aligned(size_t, gfp_t, void **); -extern void xpc_activate_partition(struct xpc_partition *); -extern void xpc_activate_kthreads(struct xpc_channel *, int); -extern void xpc_create_kthreads(struct xpc_channel *, int, int); -extern void xpc_disconnect_wait(int); - -/* found in xpc_uv.c */ -extern int xpc_init_uv(void); -extern void xpc_exit_uv(void); - -/* found in xpc_partition.c */ -extern int xpc_exiting; -extern int xpc_nasid_mask_nlongs; -extern struct xpc_rsvd_page *xpc_rsvd_page; -extern unsigned long *xpc_mach_nasids; -extern struct xpc_partition *xpc_partitions; -extern void *xpc_kmalloc_cacheline_aligned(size_t, gfp_t, void **); -extern int xpc_setup_rsvd_page(void); -extern void xpc_teardown_rsvd_page(void); -extern int xpc_identify_activate_IRQ_sender(void); -extern int xpc_partition_disengaged(struct xpc_partition *); -extern int xpc_partition_disengaged_from_timer(struct xpc_partition *part); -extern enum xp_retval xpc_mark_partition_active(struct xpc_partition *); -extern void xpc_mark_partition_inactive(struct xpc_partition *); -extern void xpc_discovery(void); -extern enum xp_retval xpc_get_remote_rp(int, unsigned long *, - struct xpc_rsvd_page *, - unsigned long *); -extern void xpc_deactivate_partition(const int, struct xpc_partition *, - enum xp_retval); -extern enum xp_retval xpc_initiate_partid_to_nasids(short, void *); - -/* found in xpc_channel.c */ -extern void xpc_initiate_connect(int); -extern void xpc_initiate_disconnect(int); -extern enum xp_retval xpc_allocate_msg_wait(struct xpc_channel *); -extern enum xp_retval xpc_initiate_send(short, int, u32, void *, u16); -extern enum xp_retval xpc_initiate_send_notify(short, int, u32, void *, u16, - xpc_notify_func, void *); -extern void xpc_initiate_received(short, int, void *); -extern void xpc_process_sent_chctl_flags(struct xpc_partition *); -extern void xpc_connected_callout(struct xpc_channel *); -extern void xpc_deliver_payload(struct xpc_channel *); -extern void xpc_disconnect_channel(const int, struct xpc_channel *, - enum xp_retval, unsigned long *); -extern void xpc_disconnect_callout(struct xpc_channel *, enum xp_retval); -extern void xpc_partition_going_down(struct xpc_partition *, enum xp_retval); - -static inline void -xpc_wakeup_channel_mgr(struct xpc_partition *part) -{ - if (atomic_inc_return(&part->channel_mgr_requests) == 1) - wake_up(&part->channel_mgr_wq); -} - -/* - * These next two inlines are used to keep us from tearing down a channel's - * msg queues while a thread may be referencing them. - */ -static inline void -xpc_msgqueue_ref(struct xpc_channel *ch) -{ - atomic_inc(&ch->references); -} - -static inline void -xpc_msgqueue_deref(struct xpc_channel *ch) -{ - s32 refs = atomic_dec_return(&ch->references); - - DBUG_ON(refs < 0); - if (refs == 0) - xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]); -} - -#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \ - xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs) - -/* - * These two inlines are used to keep us from tearing down a partition's - * setup infrastructure while a thread may be referencing it. - */ -static inline void -xpc_part_deref(struct xpc_partition *part) -{ - s32 refs = atomic_dec_return(&part->references); - - DBUG_ON(refs < 0); - if (refs == 0 && part->setup_state == XPC_P_SS_WTEARDOWN) - wake_up(&part->teardown_wq); -} - -static inline int -xpc_part_ref(struct xpc_partition *part) -{ - int setup; - - atomic_inc(&part->references); - setup = (part->setup_state == XPC_P_SS_SETUP); - if (!setup) - xpc_part_deref(part); - - return setup; -} - -/* - * The following macro is to be used for the setting of the reason and - * reason_line fields in both the struct xpc_channel and struct xpc_partition - * structures. - */ -#define XPC_SET_REASON(_p, _reason, _line) \ - { \ - (_p)->reason = _reason; \ - (_p)->reason_line = _line; \ - } - -#endif /* _DRIVERS_MISC_SGIXP_XPC_H */ diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c deleted file mode 100644 index 8e6607fc8a67..000000000000 --- a/drivers/misc/sgi-xp/xpc_channel.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) channel support. - * - * This is the part of XPC that manages the channels and - * sends/receives messages across them to/from other partitions. - * - */ - -#include -#include "xpc.h" - -/* - * Process a connect message from a remote partition. - * - * Note: xpc_process_connect() is expecting to be called with the - * spin_lock_irqsave held and will leave it locked upon return. - */ -static void -xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - enum xp_retval ret; - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_ROPENREQUEST)) { - /* nothing more to do for now */ - return; - } - DBUG_ON(!(ch->flags & XPC_C_CONNECTING)); - - if (!(ch->flags & XPC_C_SETUP)) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - ret = xpc_arch_ops.setup_msg_structures(ch); - spin_lock_irqsave(&ch->lock, *irq_flags); - - if (ret != xpSuccess) - XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); - else - ch->flags |= XPC_C_SETUP; - - if (ch->flags & XPC_C_DISCONNECTING) - return; - } - - if (!(ch->flags & XPC_C_OPENREPLY)) { - ch->flags |= XPC_C_OPENREPLY; - xpc_arch_ops.send_chctl_openreply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENREPLY)) - return; - - if (!(ch->flags & XPC_C_OPENCOMPLETE)) { - ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED); - xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENCOMPLETE)) - return; - - dev_info(xpc_chan, "channel %d to partition %d connected\n", - ch->number, ch->partid); - - ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ -} - -/* - * spin_lock_irqsave() is expected to be held on entry. - */ -static void -xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_DISCONNECTING)) - return; - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - /* make sure all activity has settled down first */ - - if (atomic_read(&ch->kthreads_assigned) > 0 || - atomic_read(&ch->references) > 0) { - return; - } - DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE)); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - /* can't proceed until the other side disengages from us */ - if (xpc_arch_ops.partition_engaged(ch->partid)) - return; - - } else { - - /* as long as the other side is up do the full protocol */ - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) - return; - - if (!(ch->flags & XPC_C_CLOSEREPLY)) { - ch->flags |= XPC_C_CLOSEREPLY; - xpc_arch_ops.send_chctl_closereply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_RCLOSEREPLY)) - return; - } - - /* wake those waiting for notify completion */ - if (atomic_read(&ch->n_to_notify) > 0) { - /* we do callout while holding ch->lock, callout can't block */ - xpc_arch_ops.notify_senders_of_disconnect(ch); - } - - /* both sides are disconnected now */ - - if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - xpc_disconnect_callout(ch, xpDisconnected); - spin_lock_irqsave(&ch->lock, *irq_flags); - } - - DBUG_ON(atomic_read(&ch->n_to_notify) != 0); - - /* it's now safe to free the channel's message queues */ - xpc_arch_ops.teardown_msg_structures(ch); - - ch->func = NULL; - ch->key = NULL; - ch->entry_size = 0; - ch->local_nentries = 0; - ch->remote_nentries = 0; - ch->kthreads_assigned_limit = 0; - ch->kthreads_idle_limit = 0; - - /* - * Mark the channel disconnected and clear all other flags, including - * XPC_C_SETUP (because of call to - * xpc_arch_ops.teardown_msg_structures()) but not including - * XPC_C_WDISCONNECT (if it was set). - */ - ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); - - atomic_dec(&part->nchannels_active); - - if (channel_was_connected) { - dev_info(xpc_chan, "channel %d to partition %d disconnected, " - "reason=%d\n", ch->number, ch->partid, ch->reason); - } - - if (ch->flags & XPC_C_WDISCONNECT) { - /* we won't lose the CPU since we're holding ch->lock */ - complete(&ch->wdisconnect_wait); - } else if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - /* time to take action on any delayed chctl flags */ - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - } - ch->delayed_chctl_flags = 0; - } -} - -/* - * Process a change in the channel's remote connection state. - */ -static void -xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, - u8 chctl_flags) -{ - unsigned long irq_flags; - struct xpc_openclose_args *args = - &part->remote_openclose_args[ch_number]; - struct xpc_channel *ch = &part->channels[ch_number]; - enum xp_retval reason; - enum xp_retval ret; - int create_kthread = 0; - - spin_lock_irqsave(&ch->lock, irq_flags); - -again: - - if ((ch->flags & XPC_C_DISCONNECTED) && - (ch->flags & XPC_C_WDISCONNECT)) { - /* - * Delay processing chctl flags until thread waiting disconnect - * has had a chance to see that the channel is disconnected. - */ - ch->delayed_chctl_flags |= chctl_flags; - goto out; - } - - if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " - "from partid=%d, channel=%d\n", args->reason, - ch->partid, ch->number); - - /* - * If RCLOSEREQUEST is set, we're probably waiting for - * RCLOSEREPLY. We should find it and a ROPENREQUEST packed - * with this RCLOSEREQUEST in the chctl_flags. - */ - - if (ch->flags & XPC_C_RCLOSEREQUEST) { - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); - DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); - - DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); - chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; - ch->flags |= XPC_C_RCLOSEREPLY; - - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - goto again; - } - - if (ch->flags & XPC_C_DISCONNECTED) { - if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_OPENREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREQUEST; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); - } - - chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | - XPC_CHCTL_OPENCOMPLETE); - - /* - * The meaningful CLOSEREQUEST connection state fields are: - * reason = reason connection is to be closed - */ - - ch->flags |= XPC_C_RCLOSEREQUEST; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - reason = args->reason; - if (reason <= xpSuccess || reason > xpUnknownReason) - reason = xpUnknownReason; - else if (reason == xpUnregistering) - reason = xpOtherUnregistering; - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); - goto out; - } - - xpc_process_disconnect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" - "%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & XPC_C_DISCONNECTED) { - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_CLOSEREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREPLY; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - ch->flags |= XPC_C_RCLOSEREPLY; - - if (ch->flags & XPC_C_CLOSEREPLY) { - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - } - } - - if (chctl_flags & XPC_CHCTL_OPENREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " - "local_nentries=%d) received from partid=%d, " - "channel=%d\n", args->entry_size, args->local_nentries, - ch->partid, ch->number); - - if (part->act_state == XPC_P_AS_DEACTIVATING || - (ch->flags & XPC_C_ROPENREQUEST)) { - goto out; - } - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { - ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; - goto out; - } - DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | - XPC_C_OPENREQUEST))); - DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_OPENREPLY | XPC_C_CONNECTED)); - - /* - * The meaningful OPENREQUEST connection state fields are: - * entry_size = size of channel's messages in bytes - * local_nentries = remote partition's local_nentries - */ - if (args->entry_size == 0 || args->local_nentries == 0) { - /* assume OPENREQUEST was delayed by mistake */ - goto out; - } - - ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); - ch->remote_nentries = args->local_nentries; - - if (ch->flags & XPC_C_OPENREQUEST) { - if (args->entry_size != ch->entry_size) { - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - goto out; - } - } else { - ch->entry_size = args->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" - "0x%lx, local_nentries=%d, remote_nentries=%d) " - "received from partid=%d, channel=%d\n", - args->local_msgqueue_pa, args->local_nentries, - args->remote_nentries, ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(ch->flags & XPC_C_CONNECTED); - - /* - * The meaningful OPENREPLY connection state fields are: - * local_msgqueue_pa = physical address of remote - * partition's local_msgqueue - * local_nentries = remote partition's local_nentries - * remote_nentries = remote partition's remote_nentries - */ - DBUG_ON(args->local_msgqueue_pa == 0); - DBUG_ON(args->local_nentries == 0); - DBUG_ON(args->remote_nentries == 0); - - ret = xpc_arch_ops.save_remote_msgqueue_pa(ch, - args->local_msgqueue_pa); - if (ret != xpSuccess) { - XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags); - goto out; - } - ch->flags |= XPC_C_ROPENREPLY; - - if (args->local_nentries < ch->remote_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "remote_nentries=%d, old remote_nentries=%d, " - "partid=%d, channel=%d\n", - args->local_nentries, ch->remote_nentries, - ch->partid, ch->number); - - ch->remote_nentries = args->local_nentries; - } - if (args->remote_nentries < ch->local_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "local_nentries=%d, old local_nentries=%d, " - "partid=%d, channel=%d\n", - args->remote_nentries, ch->local_nentries, - ch->partid, ch->number); - - ch->local_nentries = args->remote_nentries; - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_OPENREPLY)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY)); - DBUG_ON(!(ch->flags & XPC_C_CONNECTED)); - - ch->flags |= XPC_C_ROPENCOMPLETE; - - xpc_process_connect(ch, &irq_flags); - create_kthread = 1; - } - -out: - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (create_kthread) - xpc_create_kthreads(ch, 1, 0); -} - -/* - * Attempt to establish a channel connection to a remote partition. - */ -static enum xp_retval -xpc_connect_channel(struct xpc_channel *ch) -{ - unsigned long irq_flags; - struct xpc_registration *registration = &xpc_registrations[ch->number]; - - if (mutex_trylock(®istration->mutex) == 0) - return xpRetry; - - if (!XPC_CHANNEL_REGISTERED(ch->number)) { - mutex_unlock(®istration->mutex); - return xpUnregistered; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - - DBUG_ON(ch->flags & XPC_C_CONNECTED); - DBUG_ON(ch->flags & XPC_C_OPENREQUEST); - - if (ch->flags & XPC_C_DISCONNECTING) { - spin_unlock_irqrestore(&ch->lock, irq_flags); - mutex_unlock(®istration->mutex); - return ch->reason; - } - - /* add info from the channel connect registration to the channel */ - - ch->kthreads_assigned_limit = registration->assigned_limit; - ch->kthreads_idle_limit = registration->idle_limit; - DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0); - DBUG_ON(atomic_read(&ch->kthreads_idle) != 0); - DBUG_ON(atomic_read(&ch->kthreads_active) != 0); - - ch->func = registration->func; - DBUG_ON(registration->func == NULL); - ch->key = registration->key; - - ch->local_nentries = registration->nentries; - - if (ch->flags & XPC_C_ROPENREQUEST) { - if (registration->entry_size != ch->entry_size) { - /* the local and remote sides aren't the same */ - - /* - * Because XPC_DISCONNECT_CHANNEL() can block we're - * forced to up the registration sema before we unlock - * the channel lock. But that's okay here because we're - * done with the part that required the registration - * sema. XPC_DISCONNECT_CHANNEL() requires that the - * channel lock be locked and will unlock and relock - * the channel lock as needed. - */ - mutex_unlock(®istration->mutex); - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpUnequalMsgSizes; - } - } else { - ch->entry_size = registration->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&xpc_partitions[ch->partid].nchannels_active); - } - - mutex_unlock(®istration->mutex); - - /* initiate the connection */ - - ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING); - xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags); - - xpc_process_connect(ch, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - return xpSuccess; -} - -void -xpc_process_sent_chctl_flags(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - struct xpc_channel *ch; - int ch_number; - u32 ch_flags; - - chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part); - - /* - * Initiate channel connections for registered channels. - * - * For each connected channel that has pending messages activate idle - * kthreads and/or create new kthreads as needed. - */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - /* - * Process any open or close related chctl flags, and then deal - * with connecting or disconnecting the channel as required. - */ - - if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { - xpc_process_openclose_chctl_flags(part, ch_number, - chctl.flags[ch_number]); - } - - ch_flags = ch->flags; /* need an atomic snapshot of flags */ - - if (ch_flags & XPC_C_DISCONNECTING) { - spin_lock_irqsave(&ch->lock, irq_flags); - xpc_process_disconnect(ch, &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - continue; - } - - if (part->act_state == XPC_P_AS_DEACTIVATING) - continue; - - if (!(ch_flags & XPC_C_CONNECTED)) { - if (!(ch_flags & XPC_C_OPENREQUEST)) { - DBUG_ON(ch_flags & XPC_C_SETUP); - (void)xpc_connect_channel(ch); - } - continue; - } - - /* - * Process any message related chctl flags, this may involve - * the activation of kthreads to deliver any pending messages - * sent from the other partition. - */ - - if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - xpc_arch_ops.process_msg_chctl_flags(part, ch_number); - } -} - -/* - * XPC's heartbeat code calls this function to inform XPC that a partition is - * going down. XPC responds by tearing down the XPartition Communication - * infrastructure used for the just downed partition. - * - * XPC's heartbeat code will never call this function and xpc_partition_up() - * at the same time. Nor will it ever make multiple calls to either function - * at the same time. - */ -void -xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason) -{ - unsigned long irq_flags; - int ch_number; - struct xpc_channel *ch; - - dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n", - XPC_PARTID(part), reason); - - if (!xpc_part_ref(part)) { - /* infrastructure for this partition isn't currently set up */ - return; - } - - /* disconnect channels associated with the partition going down */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - xpc_msgqueue_ref(ch); - spin_lock_irqsave(&ch->lock, irq_flags); - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - xpc_msgqueue_deref(ch); - } - - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); -} - -/* - * Called by XP at the time of channel connection registration to cause - * XPC to establish connections to all currently active partitions. - */ -void -xpc_initiate_connect(int ch_number) -{ - short partid; - struct xpc_partition *part; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - /* - * Initiate the establishment of a connection on the - * newly registered channel to the remote partition. - */ - xpc_wakeup_channel_mgr(part); - xpc_part_deref(part); - } - } -} - -void -xpc_connected_callout(struct xpc_channel *ch) -{ - /* let the registerer know that a connection has been established */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - ch->func(xpConnected, ch->partid, ch->number, - (void *)(u64)ch->local_nentries, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - } -} - -/* - * Called by XP at the time of channel connection unregistration to cause - * XPC to teardown all current connections for the specified channel. - * - * Before returning xpc_initiate_disconnect() will wait until all connections - * on the specified channel have been closed/torndown. So the caller can be - * assured that they will not be receiving any more callouts from XPC to the - * function they registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_initiate_disconnect(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - /* initiate the channel disconnect for every active partition */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - - if (!(ch->flags & XPC_C_DISCONNECTED)) { - ch->flags |= XPC_C_WDISCONNECT; - - XPC_DISCONNECT_CHANNEL(ch, xpUnregistering, - &irq_flags); - } - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - } - } - - xpc_disconnect_wait(ch_number); -} - -/* - * To disconnect a channel, and reflect it back to all who may be waiting. - * - * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by - * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by - * xpc_disconnect_wait(). - * - * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. - */ -void -xpc_disconnect_channel(const int line, struct xpc_channel *ch, - enum xp_retval reason, unsigned long *irq_flags) -{ - u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); - - lockdep_assert_held(&ch->lock); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - return; - - DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED))); - - dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n", - reason, line, ch->partid, ch->number); - - XPC_SET_REASON(ch, reason, line); - - ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); - /* some of these may not have been set */ - ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | - XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_CONNECTING | XPC_C_CONNECTED); - - xpc_arch_ops.send_chctl_closerequest(ch, irq_flags); - - if (channel_was_connected) - ch->flags |= XPC_C_WASCONNECTED; - - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - /* wake all idle kthreads so they can exit */ - if (atomic_read(&ch->kthreads_idle) > 0) { - wake_up_all(&ch->idle_wq); - - } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - /* start a kthread that will do the xpDisconnecting callout */ - xpc_create_kthreads(ch, 1, 1); - } - - /* wake those waiting to allocate an entry from the local msg queue */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); - - spin_lock_irqsave(&ch->lock, *irq_flags); -} - -void -xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason) -{ - /* - * Let the channel's registerer know that the channel is being - * disconnected. We don't want to do this if the registerer was never - * informed of a connection being made. - */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - - ch->func(reason, ch->partid, ch->number, NULL, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - } -} - -/* - * Wait for a message entry to become available for the specified channel, - * but don't wait any longer than 1 jiffy. - */ -enum xp_retval -xpc_allocate_msg_wait(struct xpc_channel *ch) -{ - enum xp_retval ret; - DEFINE_WAIT(wait); - - if (ch->flags & XPC_C_DISCONNECTING) { - DBUG_ON(ch->reason == xpInterrupted); - return ch->reason; - } - - atomic_inc(&ch->n_on_msg_allocate_wq); - prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE); - ret = schedule_timeout(1); - finish_wait(&ch->msg_allocate_wq, &wait); - atomic_dec(&ch->n_on_msg_allocate_wq); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - DBUG_ON(ch->reason == xpInterrupted); - } else if (ret == 0) { - ret = xpTimeout; - } else { - ret = xpInterrupted; - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * Once sent, this routine will not wait for the message to be received, nor - * will notification be given when it does happen. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - */ -enum xp_retval -xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, 0, NULL, NULL); - xpc_part_deref(part); - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * This routine will not wait for the message to be sent or received. - * - * Once the remote end of the channel has received the message, the function - * passed as an argument to xpc_initiate_send_notify() will be called. This - * allows the sender to free up or re-use any buffers referenced by the - * message, but does NOT mean the message has been processed at the remote - * end by a receiver. - * - * If this routine returns an error, the caller's function will NOT be called. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - * func - function to call with asynchronous notification of message - * receipt. THIS FUNCTION MUST BE NON-BLOCKING. - * key - user-defined key to be passed to the function when it's called. - */ -enum xp_retval -xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - DBUG_ON(func == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, XPC_N_CALL, func, key); - xpc_part_deref(part); - } - return ret; -} - -/* - * Deliver a message's payload to its intended recipient. - */ -void -xpc_deliver_payload(struct xpc_channel *ch) -{ - void *payload; - - payload = xpc_arch_ops.get_deliverable_payload(ch); - if (payload != NULL) { - - /* - * This ref is taken to protect the payload itself from being - * freed before the user is finished with it, which the user - * indicates by calling xpc_initiate_received(). - */ - xpc_msgqueue_ref(ch); - - atomic_inc(&ch->kthreads_active); - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - - /* deliver the message to its intended recipient */ - ch->func(xpMsgReceived, ch->partid, ch->number, payload, - ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - } - - atomic_dec(&ch->kthreads_active); - } -} - -/* - * Acknowledge receipt of a delivered message's payload. - * - * This function, although called by users, does not call xpc_part_ref() to - * ensure that the partition infrastructure is in place. It relies on the - * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload(). - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # message received on. - * payload - pointer to the payload area allocated via - * xpc_initiate_send() or xpc_initiate_send_notify(). - */ -void -xpc_initiate_received(short partid, int ch_number, void *payload) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - - ch = &part->channels[ch_number]; - xpc_arch_ops.received_payload(ch, payload); - - /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */ - xpc_msgqueue_deref(ch); -} diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c deleted file mode 100644 index 15b6a8e35681..000000000000 --- a/drivers/misc/sgi-xp/xpc_main.c +++ /dev/null @@ -1,1309 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) support - standard version. - * - * XPC provides a message passing capability that crosses partition - * boundaries. This module is made up of two parts: - * - * partition This part detects the presence/absence of other - * partitions. It provides a heartbeat and monitors - * the heartbeats of other partitions. - * - * channel This part manages the channels and sends/receives - * messages across them to/from other partitions. - * - * There are a couple of additional functions residing in XP, which - * provide an interface to XPC for its users. - * - * - * Caveats: - * - * . Currently on sn2, we have no way to determine which nasid an IRQ - * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write - * followed by an IPI. The amo indicates where data is to be pulled - * from, so after the IPI arrives, the remote partition checks the amo - * word. The IPI can actually arrive before the amo however, so other - * code must periodically check for this case. Also, remote amo - * operations do not reliably time out. Thus we do a remote PIO read - * solely to know whether the remote partition is down and whether we - * should stop sending IPIs to it. This remote PIO read operation is - * set up in a special nofault region so SAL knows to ignore (and - * cleanup) any errors due to the remote amo write, PIO read, and/or - * PIO write operations. - * - * If/when new hardware solves this IPI problem, we should abandon - * the current approach. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "xpc.h" - -#ifdef CONFIG_X86_64 -#include -#endif - -/* define two XPC debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpc_dbg_name = { - .name = "xpc" -}; - -static struct device xpc_part_dbg_subname = { - .init_name = "", /* set to "part" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -static struct device xpc_chan_dbg_subname = { - .init_name = "", /* set to "chan" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -struct device *xpc_part = &xpc_part_dbg_subname; -struct device *xpc_chan = &xpc_chan_dbg_subname; - -static int xpc_kdebug_ignore; - -/* systune related variables for /proc/sys directories */ - -static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL; -static int xpc_hb_min_interval = 1; -static int xpc_hb_max_interval = 10; - -static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL; -static int xpc_hb_check_min_interval = 10; -static int xpc_hb_check_max_interval = 120; - -int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT; -static int xpc_disengage_min_timelimit; /* = 0 */ -static int xpc_disengage_max_timelimit = 120; - -static const struct ctl_table xpc_sys_xpc_hb[] = { - { - .procname = "hb_interval", - .data = &xpc_hb_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_min_interval, - .extra2 = &xpc_hb_max_interval}, - { - .procname = "hb_check_interval", - .data = &xpc_hb_check_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_check_min_interval, - .extra2 = &xpc_hb_check_max_interval}, -}; -static const struct ctl_table xpc_sys_xpc[] = { - { - .procname = "disengage_timelimit", - .data = &xpc_disengage_timelimit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_disengage_min_timelimit, - .extra2 = &xpc_disengage_max_timelimit}, -}; - -static struct ctl_table_header *xpc_sysctl; -static struct ctl_table_header *xpc_sysctl_hb; - -/* non-zero if any remote partition disengage was timed out */ -int xpc_disengage_timedout; - -/* #of activate IRQs received and not yet processed */ -int xpc_activate_IRQ_rcvd; -DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock); - -/* IRQ handler notifies this wait queue on receipt of an IRQ */ -DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq); - -static unsigned long xpc_hb_check_timeout; -static struct timer_list xpc_hb_timer; - -/* notification that the xpc_hb_checker thread has exited */ -static DECLARE_COMPLETION(xpc_hb_checker_exited); - -/* notification that the xpc_discovery thread has exited */ -static DECLARE_COMPLETION(xpc_discovery_exited); - -static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); - -static int xpc_system_reboot(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_reboot_notifier = { - .notifier_call = xpc_system_reboot, -}; - -static int xpc_system_die(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_die_notifier = { - .notifier_call = xpc_system_die, -}; - -struct xpc_arch_operations xpc_arch_ops; - -/* - * Timer function to enforce the timelimit on the partition disengage. - */ -static void -xpc_timeout_partition_disengage(struct timer_list *t) -{ - struct xpc_partition *part = timer_container_of(part, t, - disengage_timer); - - DBUG_ON(time_is_after_jiffies(part->disengage_timeout)); - - xpc_partition_disengaged_from_timer(part); - - DBUG_ON(part->disengage_timeout != 0); - DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part))); -} - -/* - * Timer to produce the heartbeat. The timer structures function is - * already set when this is initially called. A tunable is used to - * specify when the next timeout should occur. - */ -static void -xpc_hb_beater(struct timer_list *unused) -{ - xpc_arch_ops.increment_heartbeat(); - - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ); - add_timer(&xpc_hb_timer); -} - -static void -xpc_start_hb_beater(void) -{ - xpc_arch_ops.heartbeat_init(); - timer_setup(&xpc_hb_timer, xpc_hb_beater, 0); - xpc_hb_beater(NULL); -} - -static void -xpc_stop_hb_beater(void) -{ - timer_delete_sync(&xpc_hb_timer); - xpc_arch_ops.heartbeat_exit(); -} - -/* - * At periodic intervals, scan through all active partitions and ensure - * their heartbeat is still active. If not, the partition is deactivated. - */ -static void -xpc_check_remote_hb(void) -{ - struct xpc_partition *part; - short partid; - enum xp_retval ret; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - - if (xpc_exiting) - break; - - if (partid == xp_partition_id) - continue; - - part = &xpc_partitions[partid]; - - if (part->act_state == XPC_P_AS_INACTIVE || - part->act_state == XPC_P_AS_DEACTIVATING) { - continue; - } - - ret = xpc_arch_ops.get_remote_heartbeat(part); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(part, ret); - } -} - -/* - * This thread is responsible for nearly all of the partition - * activation/deactivation. - */ -static int -xpc_hb_checker(void *ignore) -{ - int force_IRQ = 0; - - /* this thread was marked active by xpc_hb_init() */ - - set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU)); - - /* set our heartbeating to other partitions into motion */ - xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); - xpc_start_hb_beater(); - - while (!xpc_exiting) { - - dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have " - "been received\n", - (int)(xpc_hb_check_timeout - jiffies), - xpc_activate_IRQ_rcvd); - - /* checking of remote heartbeats is skewed by IRQ handling */ - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) { - xpc_hb_check_timeout = jiffies + - (xpc_hb_check_interval * HZ); - - dev_dbg(xpc_part, "checking remote heartbeats\n"); - xpc_check_remote_hb(); - } - - /* check for outstanding IRQs */ - if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) { - force_IRQ = 0; - dev_dbg(xpc_part, "processing activate IRQs " - "received\n"); - xpc_arch_ops.process_activate_IRQ_rcvd(); - } - - /* wait for IRQ or timeout */ - (void)wait_event_interruptible(xpc_activate_IRQ_wq, - (time_is_before_eq_jiffies( - xpc_hb_check_timeout) || - xpc_activate_IRQ_rcvd > 0 || - xpc_exiting)); - } - - xpc_stop_hb_beater(); - - dev_dbg(xpc_part, "heartbeat checker is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_hb_checker_exited); - return 0; -} - -/* - * This thread will attempt to discover other partitions to activate - * based on info provided by SAL. This new thread is short lived and - * will exit once discovery is complete. - */ -static int -xpc_initiate_discovery(void *ignore) -{ - xpc_discovery(); - - dev_dbg(xpc_part, "discovery thread is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_discovery_exited); - return 0; -} - -/* - * The first kthread assigned to a newly activated partition is the one - * created by XPC HB with which it calls xpc_activating(). XPC hangs on to - * that kthread until the partition is brought down, at which time that kthread - * returns back to XPC HB. (The return of that kthread will signify to XPC HB - * that XPC has dismantled all communication infrastructure for the associated - * partition.) This kthread becomes the channel manager for that partition. - * - * Each active partition has a channel manager, who, besides connecting and - * disconnecting channels, will ensure that each of the partition's connected - * channels has the required number of assigned kthreads to get the work done. - */ -static void -xpc_channel_mgr(struct xpc_partition *part) -{ - while (part->act_state != XPC_P_AS_DEACTIVATING || - atomic_read(&part->nchannels_active) > 0 || - !xpc_partition_disengaged(part)) { - - xpc_process_sent_chctl_flags(part); - - /* - * Wait until we've been requested to activate kthreads or - * all of the channel's message queues have been torn down or - * a signal is pending. - * - * The channel_mgr_requests is set to 1 after being awakened, - * This is done to prevent the channel mgr from making one pass - * through the loop for each request, since he will - * be servicing all the requests in one pass. The reason it's - * set to 1 instead of 0 is so that other kthreads will know - * that the channel mgr is running and won't bother trying to - * wake him up. - */ - atomic_dec(&part->channel_mgr_requests); - (void)wait_event_interruptible(part->channel_mgr_wq, - (atomic_read(&part->channel_mgr_requests) > 0 || - part->chctl.all_flags != 0 || - (part->act_state == XPC_P_AS_DEACTIVATING && - atomic_read(&part->nchannels_active) == 0 && - xpc_partition_disengaged(part)))); - atomic_set(&part->channel_mgr_requests, 1); - } -} - -/* - * Guarantee that the kzalloc'd memory is cacheline aligned. - */ -void * -xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kzalloc will give us cachline aligned memory by default */ - *base = kzalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kzalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Setup the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static enum xp_retval -xpc_setup_ch_structures(struct xpc_partition *part) -{ - enum xp_retval ret; - int ch_number; - struct xpc_channel *ch; - short partid = XPC_PARTID(part); - - /* - * Allocate all of the channel structures as a contiguous chunk of - * memory. - */ - DBUG_ON(part->channels != NULL); - part->channels = kzalloc_objs(struct xpc_channel, XPC_MAX_NCHANNELS); - if (part->channels == NULL) { - dev_err(xpc_chan, "can't get memory for channels\n"); - return xpNoMemory; - } - - /* allocate the remote open and close args */ - - part->remote_openclose_args = - xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE, - GFP_KERNEL, &part-> - remote_openclose_args_base); - if (part->remote_openclose_args == NULL) { - dev_err(xpc_chan, "can't get memory for remote connect args\n"); - ret = xpNoMemory; - goto out_1; - } - - part->chctl.all_flags = 0; - spin_lock_init(&part->chctl_lock); - - atomic_set(&part->channel_mgr_requests, 1); - init_waitqueue_head(&part->channel_mgr_wq); - - part->nchannels = XPC_MAX_NCHANNELS; - - atomic_set(&part->nchannels_active, 0); - atomic_set(&part->nchannels_engaged, 0); - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - ch->partid = partid; - ch->number = ch_number; - ch->flags = XPC_C_DISCONNECTED; - - atomic_set(&ch->kthreads_assigned, 0); - atomic_set(&ch->kthreads_idle, 0); - atomic_set(&ch->kthreads_active, 0); - - atomic_set(&ch->references, 0); - atomic_set(&ch->n_to_notify, 0); - - spin_lock_init(&ch->lock); - init_completion(&ch->wdisconnect_wait); - - atomic_set(&ch->n_on_msg_allocate_wq, 0); - init_waitqueue_head(&ch->msg_allocate_wq); - init_waitqueue_head(&ch->idle_wq); - } - - ret = xpc_arch_ops.setup_ch_structures(part); - if (ret != xpSuccess) - goto out_2; - - /* - * With the setting of the partition setup_state to XPC_P_SS_SETUP, - * we're declaring that this partition is ready to go. - */ - part->setup_state = XPC_P_SS_SETUP; - - return xpSuccess; - - /* setup of ch structures failed */ -out_2: - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; -out_1: - kfree(part->channels); - part->channels = NULL; - return ret; -} - -/* - * Teardown the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static void -xpc_teardown_ch_structures(struct xpc_partition *part) -{ - DBUG_ON(atomic_read(&part->nchannels_engaged) != 0); - DBUG_ON(atomic_read(&part->nchannels_active) != 0); - - /* - * Make this partition inaccessible to local processes by marking it - * as no longer setup. Then wait before proceeding with the teardown - * until all existing references cease. - */ - DBUG_ON(part->setup_state != XPC_P_SS_SETUP); - part->setup_state = XPC_P_SS_WTEARDOWN; - - wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); - - /* now we can begin tearing down the infrastructure */ - - xpc_arch_ops.teardown_ch_structures(part); - - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; - kfree(part->channels); - part->channels = NULL; - - part->setup_state = XPC_P_SS_TORNDOWN; -} - -/* - * When XPC HB determines that a partition has come up, it will create a new - * kthread and that kthread will call this function to attempt to set up the - * basic infrastructure used for Cross Partition Communication with the newly - * upped partition. - * - * The kthread that was created by XPC HB and which setup the XPC - * infrastructure will remain assigned to the partition becoming the channel - * manager for that partition until the partition is deactivating, at which - * time the kthread will teardown the XPC infrastructure and then exit. - */ -static int -xpc_activating(void *__partid) -{ - short partid = (u64)__partid; - struct xpc_partition *part = &xpc_partitions[partid]; - unsigned long irq_flags; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; - return 0; - } - - /* indicate the thread is activating */ - DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ); - part->act_state = XPC_P_AS_ACTIVATING; - - XPC_SET_REASON(part, 0, 0); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - dev_dbg(xpc_part, "activating partition %d\n", partid); - - xpc_arch_ops.allow_hb(partid); - - if (xpc_setup_ch_structures(part) == xpSuccess) { - (void)xpc_part_ref(part); /* this will always succeed */ - - if (xpc_arch_ops.make_first_contact(part) == xpSuccess) { - xpc_mark_partition_active(part); - xpc_channel_mgr(part); - /* won't return until partition is deactivating */ - } - - xpc_part_deref(part); - xpc_teardown_ch_structures(part); - } - - xpc_arch_ops.disallow_hb(partid); - xpc_mark_partition_inactive(part); - - if (part->reason == xpReactivating) { - /* interrupting ourselves results in activating partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - - return 0; -} - -void -xpc_activate_partition(struct xpc_partition *part) -{ - short partid = XPC_PARTID(part); - unsigned long irq_flags; - struct task_struct *kthread; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - DBUG_ON(part->act_state != XPC_P_AS_INACTIVE); - - part->act_state = XPC_P_AS_ACTIVATION_REQ; - XPC_SET_REASON(part, xpCloneKThread, __LINE__); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d", - partid); - if (IS_ERR(kthread)) { - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - } -} - -void -xpc_activate_kthreads(struct xpc_channel *ch, int needed) -{ - int idle = atomic_read(&ch->kthreads_idle); - int assigned = atomic_read(&ch->kthreads_assigned); - int wakeup; - - DBUG_ON(needed <= 0); - - if (idle > 0) { - wakeup = (needed > idle) ? idle : needed; - needed -= wakeup; - - dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, " - "channel=%d\n", wakeup, ch->partid, ch->number); - - /* only wakeup the requested number of kthreads */ - wake_up_nr(&ch->idle_wq, wakeup); - } - - if (needed <= 0) - return; - - if (needed + assigned > ch->kthreads_assigned_limit) { - needed = ch->kthreads_assigned_limit - assigned; - if (needed <= 0) - return; - } - - dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n", - needed, ch->partid, ch->number); - - xpc_create_kthreads(ch, needed, 0); -} - -/* - * This function is where XPC's kthreads wait for messages to deliver. - */ -static void -xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch) -{ - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - do { - /* deliver messages to their intended recipients */ - - while (n_of_deliverable_payloads(ch) > 0 && - !(ch->flags & XPC_C_DISCONNECTING)) { - xpc_deliver_payload(ch); - } - - if (atomic_inc_return(&ch->kthreads_idle) > - ch->kthreads_idle_limit) { - /* too many idle kthreads on this channel */ - atomic_dec(&ch->kthreads_idle); - break; - } - - dev_dbg(xpc_chan, "idle kthread calling " - "wait_event_interruptible_exclusive()\n"); - - (void)wait_event_interruptible_exclusive(ch->idle_wq, - (n_of_deliverable_payloads(ch) > 0 || - (ch->flags & XPC_C_DISCONNECTING))); - - atomic_dec(&ch->kthreads_idle); - - } while (!(ch->flags & XPC_C_DISCONNECTING)); -} - -static int -xpc_kthread_start(void *args) -{ - short partid = XPC_UNPACK_ARG1(args); - u16 ch_number = XPC_UNPACK_ARG2(args); - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - int n_needed; - unsigned long irq_flags; - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n", - partid, ch_number); - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - - /* let registerer know that connection has been established */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) { - ch->flags |= XPC_C_CONNECTEDCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_connected_callout(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - /* - * It is possible that while the callout was being - * made that the remote partition sent some messages. - * If that is the case, we may need to activate - * additional kthreads to help deliver them. We only - * need one less than total #of messages to deliver. - */ - n_needed = n_of_deliverable_payloads(ch) - 1; - if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING)) - xpc_activate_kthreads(ch, n_needed); - - } else { - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - - xpc_kthread_waitmsgs(part, ch); - } - - /* let registerer know that connection is disconnecting */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - ch->flags |= XPC_C_DISCONNECTINGCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_disconnect_callout(ch, xpDisconnecting); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE; - } - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - xpc_arch_ops.indicate_partition_disengaged(part); - } - - xpc_msgqueue_deref(ch); - - dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n", - partid, ch_number); - - xpc_part_deref(part); - return 0; -} - -/* - * For each partition that XPC has established communications with, there is - * a minimum of one kernel thread assigned to perform any operation that - * may potentially sleep or block (basically the callouts to the asynchronous - * functions registered via xpc_connect()). - * - * Additional kthreads are created and destroyed by XPC as the workload - * demands. - * - * A kthread is assigned to one of the active channels that exists for a given - * partition. - */ -void -xpc_create_kthreads(struct xpc_channel *ch, int needed, - int ignore_disconnecting) -{ - unsigned long irq_flags; - u64 args = XPC_PACK_ARGS(ch->partid, ch->number); - struct xpc_partition *part = &xpc_partitions[ch->partid]; - struct task_struct *kthread; - void (*indicate_partition_disengaged) (struct xpc_partition *) = - xpc_arch_ops.indicate_partition_disengaged; - - while (needed-- > 0) { - - /* - * The following is done on behalf of the newly created - * kthread. That kthread is responsible for doing the - * counterpart to the following before it exits. - */ - if (ignore_disconnecting) { - if (!atomic_inc_not_zero(&ch->kthreads_assigned)) { - /* kthreads assigned had gone to zero */ - BUG_ON(!(ch->flags & - XPC_C_DISCONNECTINGCALLOUT_MADE)); - break; - } - - } else if (ch->flags & XPC_C_DISCONNECTING) { - break; - - } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 && - atomic_inc_return(&part->nchannels_engaged) == 1) { - xpc_arch_ops.indicate_partition_engaged(part); - } - (void)xpc_part_ref(part); - xpc_msgqueue_ref(ch); - - kthread = kthread_run(xpc_kthread_start, (void *)args, - "xpc%02dc%d", ch->partid, ch->number); - if (IS_ERR(kthread)) { - /* the fork failed */ - - /* - * NOTE: if (ignore_disconnecting && - * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true, - * then we'll deadlock if all other kthreads assigned - * to this channel are blocked in the channel's - * registerer, because the only thing that will unblock - * them is the xpDisconnecting callout that this - * failed kthread_run() would have made. - */ - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - indicate_partition_disengaged(part); - } - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - - if (atomic_read(&ch->kthreads_assigned) < - ch->kthreads_idle_limit) { - /* - * Flag this as an error only if we have an - * insufficient #of kthreads for the channel - * to function. - */ - spin_lock_irqsave(&ch->lock, irq_flags); - XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - break; - } - } -} - -void -xpc_disconnect_wait(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - int wakeup_channel_mgr; - - /* now wait for all callouts to the caller's function to cease */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (!xpc_part_ref(part)) - continue; - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_WDISCONNECT)) { - xpc_part_deref(part); - continue; - } - - wait_for_completion(&ch->wdisconnect_wait); - - spin_lock_irqsave(&ch->lock, irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - wakeup_channel_mgr = 0; - - if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - wakeup_channel_mgr = 1; - } - ch->delayed_chctl_flags = 0; - } - - ch->flags &= ~XPC_C_WDISCONNECT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (wakeup_channel_mgr) - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); - } -} - -static int -xpc_setup_partitions(void) -{ - short partid; - struct xpc_partition *part; - - xpc_partitions = kzalloc_objs(struct xpc_partition, xp_max_npartitions); - if (xpc_partitions == NULL) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - return -ENOMEM; - } - - /* - * The first few fields of each entry of xpc_partitions[] need to - * be initialized now so that calls to xpc_connect() and - * xpc_disconnect() can be made prior to the activation of any remote - * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE - * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING - * PARTITION HAS BEEN ACTIVATED. - */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part)); - - part->activate_IRQ_rcvd = 0; - spin_lock_init(&part->act_lock); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, 0, 0); - - timer_setup(&part->disengage_timer, - xpc_timeout_partition_disengage, 0); - - part->setup_state = XPC_P_SS_UNSET; - init_waitqueue_head(&part->teardown_wq); - atomic_set(&part->references, 0); - } - - return xpc_arch_ops.setup_partitions(); -} - -static void -xpc_teardown_partitions(void) -{ - xpc_arch_ops.teardown_partitions(); - kfree(xpc_partitions); -} - -static void -xpc_do_exit(enum xp_retval reason) -{ - short partid; - int active_part_count, printed_waiting_msg = 0; - struct xpc_partition *part; - unsigned long printmsg_time, disengage_timeout = 0; - - /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */ - DBUG_ON(xpc_exiting == 1); - - /* - * Let the heartbeat checker thread and the discovery thread - * (if one is running) know that they should exit. Also wake up - * the heartbeat checker thread in case it's sleeping. - */ - xpc_exiting = 1; - wake_up_interruptible(&xpc_activate_IRQ_wq); - - /* wait for the discovery thread to exit */ - wait_for_completion(&xpc_discovery_exited); - - /* wait for the heartbeat checker thread to exit */ - wait_for_completion(&xpc_hb_checker_exited); - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - /* wait for all partitions to become inactive */ - - printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - xpc_disengage_timedout = 0; - - do { - active_part_count = 0; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_partition_disengaged(part) && - part->act_state == XPC_P_AS_INACTIVE) { - continue; - } - - active_part_count++; - - XPC_DEACTIVATE_PARTITION(part, reason); - - if (part->disengage_timeout > disengage_timeout) - disengage_timeout = part->disengage_timeout; - } - - if (xpc_arch_ops.any_partition_engaged()) { - if (time_is_before_jiffies(printmsg_time)) { - dev_info(xpc_part, "waiting for remote " - "partitions to deactivate, timeout in " - "%ld seconds\n", (disengage_timeout - - jiffies) / HZ); - printmsg_time = jiffies + - (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - printed_waiting_msg = 1; - } - - } else if (active_part_count > 0) { - if (printed_waiting_msg) { - dev_info(xpc_part, "waiting for local partition" - " to deactivate\n"); - printed_waiting_msg = 0; - } - - } else { - if (!xpc_disengage_timedout) { - dev_info(xpc_part, "all partitions have " - "deactivated\n"); - } - break; - } - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - } while (1); - - DBUG_ON(xpc_arch_ops.any_partition_engaged()); - - xpc_teardown_rsvd_page(); - - if (reason == xpUnloading) { - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); - } - - /* clear the interface to XPC's functions */ - xpc_clear_interface(); - - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - - xpc_teardown_partitions(); - - if (is_uv_system()) - xpc_exit_uv(); -} - -/* - * This function is called when the system is being rebooted. - */ -static int -xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused) -{ - enum xp_retval reason; - - switch (event) { - case SYS_RESTART: - reason = xpSystemReboot; - break; - case SYS_HALT: - reason = xpSystemHalt; - break; - case SYS_POWER_OFF: - reason = xpSystemPoweroff; - break; - default: - reason = xpSystemGoingDown; - } - - xpc_do_exit(reason); - return NOTIFY_DONE; -} - -/* Used to only allow one cpu to complete disconnect */ -static unsigned int xpc_die_disconnecting; - -/* - * Notify other partitions to deactivate from us by first disengaging from all - * references to our memory. - */ -static void -xpc_die_deactivate(void) -{ - struct xpc_partition *part; - short partid; - int any_engaged; - long keep_waiting; - long wait_to_print; - - if (cmpxchg(&xpc_die_disconnecting, 0, 1)) - return; - - /* keep xpc_hb_checker thread from doing anything (just in case) */ - xpc_exiting = 1; - - xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */ - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_arch_ops.partition_engaged(partid) || - part->act_state != XPC_P_AS_INACTIVE) { - xpc_arch_ops.request_partition_deactivation(part); - xpc_arch_ops.indicate_partition_disengaged(part); - } - } - - /* - * Though we requested that all other partitions deactivate from us, - * we only wait until they've all disengaged or we've reached the - * defined timelimit. - * - * Given that one iteration through the following while-loop takes - * approximately 200 microseconds, calculate the #of loops to take - * before bailing and the #of loops before printing a waiting message. - */ - keep_waiting = xpc_disengage_timelimit * 1000 * 5; - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; - - while (1) { - any_engaged = xpc_arch_ops.any_partition_engaged(); - if (!any_engaged) { - dev_info(xpc_part, "all partitions have deactivated\n"); - break; - } - - if (!keep_waiting--) { - for (partid = 0; partid < xp_max_npartitions; - partid++) { - if (xpc_arch_ops.partition_engaged(partid)) { - dev_info(xpc_part, "deactivate from " - "remote partition %d timed " - "out\n", partid); - } - } - break; - } - - if (!wait_to_print--) { - dev_info(xpc_part, "waiting for remote partitions to " - "deactivate, timeout in %ld seconds\n", - keep_waiting / (1000 * 5)); - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * - 1000 * 5; - } - - udelay(200); - } -} - -/* - * This function is called when the system is being restarted or halted due - * to some sort of system failure. If this is the case we need to notify the - * other partitions to disengage from all references to our memory. - * This function can also be called when our heartbeater could be offlined - * for a time. In this case we need to notify other partitions to not worry - * about the lack of a heartbeat. - */ -static int -xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) -{ - struct die_args *die_args = _die_args; - - switch (event) { - case DIE_TRAP: - if (die_args->trapnr == X86_TRAP_DF) - xpc_die_deactivate(); - - if (((die_args->trapnr == X86_TRAP_MF) || - (die_args->trapnr == X86_TRAP_XF)) && - !user_mode(die_args->regs)) - xpc_die_deactivate(); - - break; - case DIE_INT3: - case DIE_DEBUG: - break; - case DIE_OOPS: - case DIE_GPF: - default: - xpc_die_deactivate(); - } - - return NOTIFY_DONE; -} - -static int __init -xpc_init(void) -{ - int ret; - struct task_struct *kthread; - - dev_set_name(xpc_part, "part"); - dev_set_name(xpc_chan, "chan"); - - if (is_uv_system()) { - ret = xpc_init_uv(); - - } else { - ret = -ENODEV; - } - - if (ret != 0) - return ret; - - ret = xpc_setup_partitions(); - if (ret != 0) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - goto out_1; - } - - xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc); - xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb); - - /* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ - ret = xpc_setup_rsvd_page(); - if (ret != 0) { - dev_err(xpc_part, "can't setup our reserved page\n"); - goto out_2; - } - - /* add ourselves to the reboot_notifier_list */ - ret = register_reboot_notifier(&xpc_reboot_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register reboot notifier\n"); - - /* add ourselves to the die_notifier list */ - ret = register_die_notifier(&xpc_die_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register die notifier\n"); - - /* - * The real work-horse behind xpc. This processes incoming - * interrupts and monitors remote heartbeats. - */ - kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking hb check thread\n"); - ret = -EBUSY; - goto out_3; - } - - /* - * Startup a thread that will attempt to discover other partitions to - * activate based on info provided by SAL. This new thread is short - * lived and will exit once discovery is complete. - */ - kthread = kthread_run(xpc_initiate_discovery, NULL, - XPC_DISCOVERY_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking discovery thread\n"); - - /* mark this new thread as a non-starter */ - complete(&xpc_discovery_exited); - - xpc_do_exit(xpUnloading); - return -EBUSY; - } - - /* set the interface to point at XPC's functions */ - xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, - xpc_initiate_send, xpc_initiate_send_notify, - xpc_initiate_received, xpc_initiate_partid_to_nasids); - - return 0; - - /* initialization was not successful */ -out_3: - xpc_teardown_rsvd_page(); - - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); -out_2: - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - - xpc_teardown_partitions(); -out_1: - if (is_uv_system()) - xpc_exit_uv(); - return ret; -} - -module_init(xpc_init); - -static void __exit -xpc_exit(void) -{ - xpc_do_exit(xpUnloading); -} - -module_exit(xpc_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Communication (XPC) support"); -MODULE_LICENSE("GPL"); - -module_param(xpc_hb_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " - "heartbeat increments."); - -module_param(xpc_hb_check_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " - "heartbeat checks."); - -module_param(xpc_disengage_timelimit, int, 0); -MODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait " - "for disengage to complete."); - -module_param(xpc_kdebug_ignore, int, 0); -MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by " - "other partitions when dropping into kdebug."); diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c deleted file mode 100644 index d0467010558c..000000000000 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) partition support. - * - * This is the part of XPC that detects the presence/absence of - * other partitions. It provides a heartbeat and monitors the - * heartbeats of other partitions. - * - */ - -#include -#include -#include -#include "xpc.h" -#include - -/* XPC is exiting flag */ -int xpc_exiting; - -/* this partition's reserved page pointers */ -struct xpc_rsvd_page *xpc_rsvd_page; -static unsigned long *xpc_part_nasids; -unsigned long *xpc_mach_nasids; - -static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */ -int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */ - -struct xpc_partition *xpc_partitions; - -/* - * Guarantee that the kmalloc'd memory is cacheline aligned. - */ -void * -xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kmalloc will give us cachline aligned memory by default */ - *base = kmalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kmalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Given a nasid, get the physical address of the partition's reserved page - * for that nasid. This function returns 0 on any error. - */ -static unsigned long -xpc_get_rsvd_page_pa(int nasid) -{ - enum xp_retval ret; - u64 cookie = 0; - unsigned long rp_pa = nasid; /* seed with nasid */ - size_t len = 0; - size_t buf_len = 0; - void *buf = NULL; - void *buf_base = NULL; - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *) = - xpc_arch_ops.get_partition_rsvd_page_pa; - - while (1) { - - /* !!! rp_pa will need to be _gpa on UV. - * ??? So do we save it into the architecture specific parts - * ??? of the xpc_partition structure? Do we rename this - * ??? function or have two versions? Rename rp_pa for UV to - * ??? rp_gpa? - */ - ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len); - - dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, " - "address=0x%016lx, len=0x%016lx\n", ret, - (unsigned long)cookie, rp_pa, len); - - if (ret != xpNeedMoreInfo) - break; - - if (len > buf_len) { - kfree(buf_base); - buf_len = L1_CACHE_ALIGN(len); - buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, - &buf_base); - if (buf_base == NULL) { - dev_err(xpc_part, "unable to kmalloc " - "len=0x%016lx\n", buf_len); - ret = xpNoMemory; - break; - } - } - - ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret); - break; - } - } - - kfree(buf_base); - - if (ret != xpSuccess) - rp_pa = 0; - - dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa); - return rp_pa; -} - -/* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ -int -xpc_setup_rsvd_page(void) -{ - int ret; - struct xpc_rsvd_page *rp; - unsigned long rp_pa; - unsigned long new_ts_jiffies; - - /* get the local reserved page's address */ - - preempt_disable(); - rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id())); - preempt_enable(); - if (rp_pa == 0) { - dev_err(xpc_part, "SAL failed to locate the reserved page\n"); - return -ESRCH; - } - rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa)); - - if (rp->SAL_version < 3) { - /* SAL_versions < 3 had a SAL_partid defined as a u8 */ - rp->SAL_partid &= 0xff; - } - BUG_ON(rp->SAL_partid != xp_partition_id); - - if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) { - dev_err(xpc_part, "the reserved page's partid of %d is outside " - "supported range (< 0 || >= %d)\n", rp->SAL_partid, - xp_max_npartitions); - return -EINVAL; - } - - rp->version = XPC_RP_VERSION; - rp->max_npartitions = xp_max_npartitions; - - /* establish the actual sizes of the nasid masks */ - if (rp->SAL_version == 1) { - /* SAL_version 1 didn't set the nasids_size field */ - rp->SAL_nasids_size = 128; - } - xpc_nasid_mask_nbytes = rp->SAL_nasids_size; - xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size * - BITS_PER_BYTE); - - /* setup the pointers to the various items in the reserved page */ - xpc_part_nasids = XPC_RP_PART_NASIDS(rp); - xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp); - - ret = xpc_arch_ops.setup_rsvd_page(rp); - if (ret != 0) - return ret; - - /* - * Set timestamp of when reserved page was setup by XPC. - * This signifies to the remote partition that our reserved - * page is initialized. - */ - new_ts_jiffies = jiffies; - if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies) - new_ts_jiffies++; - rp->ts_jiffies = new_ts_jiffies; - - xpc_rsvd_page = rp; - return 0; -} - -void -xpc_teardown_rsvd_page(void) -{ - /* a zero timestamp indicates our rsvd page is not initialized */ - xpc_rsvd_page->ts_jiffies = 0; -} - -/* - * Get a copy of a portion of the remote partition's rsvd page. - * - * remote_rp points to a buffer that is cacheline aligned for BTE copies and - * is large enough to contain a copy of their reserved page header and - * part_nasids mask. - */ -enum xp_retval -xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids, - struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa) -{ - int l; - enum xp_retval ret; - - /* get the reserved page's physical address */ - - *remote_rp_pa = xpc_get_rsvd_page_pa(nasid); - if (*remote_rp_pa == 0) - return xpNoRsvdPageAddr; - - /* pull over the reserved page header and part_nasids mask */ - ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa, - XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes); - if (ret != xpSuccess) - return ret; - - if (discovered_nasids != NULL) { - unsigned long *remote_part_nasids = - XPC_RP_PART_NASIDS(remote_rp); - - for (l = 0; l < xpc_nasid_mask_nlongs; l++) - discovered_nasids[l] |= remote_part_nasids[l]; - } - - /* zero timestamp indicates the reserved page has not been setup */ - if (remote_rp->ts_jiffies == 0) - return xpRsvdPageNotSet; - - if (XPC_VERSION_MAJOR(remote_rp->version) != - XPC_VERSION_MAJOR(XPC_RP_VERSION)) { - return xpBadVersion; - } - - /* check that both remote and local partids are valid for each side */ - if (remote_rp->SAL_partid < 0 || - remote_rp->SAL_partid >= xp_max_npartitions || - remote_rp->max_npartitions <= xp_partition_id) { - return xpInvalidPartid; - } - - if (remote_rp->SAL_partid == xp_partition_id) - return xpLocalPartid; - - return xpSuccess; -} - -/* - * See if the other side has responded to a partition deactivate request - * from us. Though we requested the remote partition to deactivate with regard - * to us, we really only need to wait for the other side to disengage from us. - */ -static int __xpc_partition_disengaged(struct xpc_partition *part, - bool from_timer) -{ - short partid = XPC_PARTID(part); - int disengaged; - - disengaged = !xpc_arch_ops.partition_engaged(partid); - if (part->disengage_timeout) { - if (!disengaged) { - if (time_is_after_jiffies(part->disengage_timeout)) { - /* timelimit hasn't been reached yet */ - return 0; - } - - /* - * Other side hasn't responded to our deactivate - * request in a timely fashion, so assume it's dead. - */ - - dev_info(xpc_part, "deactivate request to remote " - "partition %d timed out\n", partid); - xpc_disengage_timedout = 1; - xpc_arch_ops.assume_partition_disengaged(partid); - disengaged = 1; - } - part->disengage_timeout = 0; - - /* Cancel the timer function if not called from it */ - if (!from_timer) - timer_delete_sync(&part->disengage_timer); - - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING && - part->act_state != XPC_P_AS_INACTIVE); - if (part->act_state != XPC_P_AS_INACTIVE) - xpc_wakeup_channel_mgr(part); - - xpc_arch_ops.cancel_partition_deactivation_request(part); - } - return disengaged; -} - -int xpc_partition_disengaged(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, false); -} - -int xpc_partition_disengaged_from_timer(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, true); -} - -/* - * Mark specified partition as active. - */ -enum xp_retval -xpc_mark_partition_active(struct xpc_partition *part) -{ - unsigned long irq_flags; - enum xp_retval ret; - - dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - if (part->act_state == XPC_P_AS_ACTIVATING) { - part->act_state = XPC_P_AS_ACTIVE; - ret = xpSuccess; - } else { - DBUG_ON(part->reason == xpSuccess); - ret = part->reason; - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - return ret; -} - -/* - * Start the process of deactivating the specified partition. - */ -void -xpc_deactivate_partition(const int line, struct xpc_partition *part, - enum xp_retval reason) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_INACTIVE) { - XPC_SET_REASON(part, reason, line); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - if (reason == xpReactivating) { - /* we interrupt ourselves to reactivate partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - return; - } - if (part->act_state == XPC_P_AS_DEACTIVATING) { - if ((part->reason == xpUnloading && reason != xpUnloading) || - reason == xpReactivating) { - XPC_SET_REASON(part, reason, line); - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - return; - } - - part->act_state = XPC_P_AS_DEACTIVATING; - XPC_SET_REASON(part, reason, line); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - /* ask remote partition to deactivate with regard to us */ - xpc_arch_ops.request_partition_deactivation(part); - - /* set a timelimit on the disengage phase of the deactivation request */ - part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ); - part->disengage_timer.expires = part->disengage_timeout; - add_timer(&part->disengage_timer); - - dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", - XPC_PARTID(part), reason); - - xpc_partition_going_down(part, reason); -} - -/* - * Mark specified partition as inactive. - */ -void -xpc_mark_partition_inactive(struct xpc_partition *part) -{ - unsigned long irq_flags; - - dev_dbg(xpc_part, "setting partition %d to INACTIVE\n", - XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; -} - -/* - * SAL has provided a partition and machine mask. The partition mask - * contains a bit for each even nasid in our partition. The machine - * mask contains a bit for each even nasid in the entire machine. - * - * Using those two bit arrays, we can determine which nasids are - * known in the machine. Each should also have a reserved page - * initialized if they are available for partitioning. - */ -void -xpc_discovery(void) -{ - void *remote_rp_base; - struct xpc_rsvd_page *remote_rp; - unsigned long remote_rp_pa; - int region; - int region_size; - int max_regions; - int nasid; - unsigned long *discovered_nasids; - enum xp_retval ret; - - remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE + - xpc_nasid_mask_nbytes, - GFP_KERNEL, &remote_rp_base); - if (remote_rp == NULL) - return; - - discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long), - GFP_KERNEL); - if (discovered_nasids == NULL) { - kfree(remote_rp_base); - return; - } - - /* - * The term 'region' in this context refers to the minimum number of - * nodes that can comprise an access protection grouping. The access - * protection is in regards to memory, IOI and IPI. - */ - region_size = xp_region_size; - - if (is_uv_system()) - max_regions = 256; - else { - max_regions = 64; - - switch (region_size) { - case 128: - max_regions *= 2; - fallthrough; - case 64: - max_regions *= 2; - fallthrough; - case 32: - max_regions *= 2; - region_size = 16; - } - } - - for (region = 0; region < max_regions; region++) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "searching region %d\n", region); - - for (nasid = (region * region_size * 2); - nasid < ((region + 1) * region_size * 2); nasid += 2) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "checking nasid %d\n", nasid); - - if (test_bit(nasid / 2, xpc_part_nasids)) { - dev_dbg(xpc_part, "PROM indicates Nasid %d is " - "part of the local partition; skipping " - "region\n", nasid); - break; - } - - if (!(test_bit(nasid / 2, xpc_mach_nasids))) { - dev_dbg(xpc_part, "PROM indicates Nasid %d was " - "not on Numa-Link network at reset\n", - nasid); - continue; - } - - if (test_bit(nasid / 2, discovered_nasids)) { - dev_dbg(xpc_part, "Nasid %d is part of a " - "partition which was previously " - "discovered\n", nasid); - continue; - } - - /* pull over the rsvd page header & part_nasids mask */ - - ret = xpc_get_remote_rp(nasid, discovered_nasids, - remote_rp, &remote_rp_pa); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "unable to get reserved page " - "from nasid %d, reason=%d\n", nasid, - ret); - - if (ret == xpLocalPartid) - break; - - continue; - } - - xpc_arch_ops.request_partition_activation(remote_rp, - remote_rp_pa, nasid); - } - } - - kfree(discovered_nasids); - kfree(remote_rp_base); -} - -/* - * Given a partid, get the nasids owned by that partition from the - * remote partition's reserved page. - */ -enum xp_retval -xpc_initiate_partid_to_nasids(short partid, void *nasid_mask) -{ - struct xpc_partition *part; - unsigned long part_nasid_pa; - - part = &xpc_partitions[partid]; - if (part->remote_rp_pa == 0) - return xpPartitionDown; - - memset(nasid_mask, 0, xpc_nasid_mask_nbytes); - - part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa); - - return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa, - xpc_nasid_mask_nbytes); -} diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c deleted file mode 100644 index 79c2f00ed4d7..000000000000 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ /dev/null @@ -1,1727 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sgi-gru/gru.h" -#include "../sgi-gru/grukservices.h" -#include "xpc.h" - -static struct xpc_heartbeat_uv *xpc_heartbeat_uv; - -#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES) -#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_ACTIVATE_MSG_SIZE_UV) -#define XPC_ACTIVATE_IRQ_NAME "xpc_activate" - -#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES) -#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_NOTIFY_MSG_SIZE_UV) -#define XPC_NOTIFY_IRQ_NAME "xpc_notify" - -static int xpc_mq_node = NUMA_NO_NODE; - -static struct xpc_gru_mq_uv *xpc_activate_mq_uv; -static struct xpc_gru_mq_uv *xpc_notify_mq_uv; - -static int -xpc_setup_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - mutex_init(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_init(&part_uv->flags_lock); - part_uv->remote_act_state = XPC_P_AS_INACTIVE; - } - return 0; -} - -static void -xpc_teardown_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - unsigned long irq_flags; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - if (part_uv->cached_activate_gru_mq_desc != NULL) { - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - kfree(part_uv->cached_activate_gru_mq_desc); - part_uv->cached_activate_gru_mq_desc = NULL; - mutex_unlock(&part_uv-> - cached_activate_gru_mq_desc_mutex); - } - } -} - -static int -xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) -{ - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset, - UV_AFFINITY_CPU); - if (mq->irq < 0) - return mq->irq; - - mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset); - - return 0; -} - -static void -xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) -{ - uv_teardown_irq(mq->irq); -} - -static int -xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - - ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address), - mq->order, &mq->mmr_offset); - if (ret < 0) { - dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, " - "ret=%d\n", ret); - return ret; - } - - mq->watchlist_num = ret; - return 0; -} - -static void -xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num); - BUG_ON(ret != BIOS_STATUS_SUCCESS); -} - -static struct xpc_gru_mq_uv * -xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, - irq_handler_t irq_handler) -{ - enum xp_retval xp_ret; - int ret; - int nid; - int nasid; - int pg_order; - struct page *page; - struct xpc_gru_mq_uv *mq; - struct uv_IO_APIC_route_entry *mmr_value; - - mq = kmalloc_obj(struct xpc_gru_mq_uv); - if (mq == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a xpc_gru_mq_uv structure\n"); - ret = -ENOMEM; - goto out_0; - } - - mq->gru_mq_desc = kzalloc_obj(struct gru_message_queue_desc); - if (mq->gru_mq_desc == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a gru_message_queue_desc structure\n"); - ret = -ENOMEM; - goto out_1; - } - - pg_order = get_order(mq_size); - mq->order = pg_order + PAGE_SHIFT; - mq_size = 1UL << mq->order; - - mq->mmr_blade = uv_cpu_to_blade_id(cpu); - - nid = cpu_to_node(cpu); - page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, - pg_order); - if (page == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d " - "bytes of memory on nid=%d for GRU mq\n", mq_size, nid); - ret = -ENOMEM; - goto out_2; - } - mq->address = page_address(page); - - /* enable generation of irq when GRU mq operation occurs to this mq */ - ret = xpc_gru_mq_watchlist_alloc_uv(mq); - if (ret != 0) - goto out_3; - - ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name); - if (ret != 0) - goto out_4; - - ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL); - if (ret != 0) { - dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n", - mq->irq, -ret); - goto out_5; - } - - nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu)); - - mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value; - ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size, - nasid, mmr_value->vector, mmr_value->dest); - if (ret != 0) { - dev_err(xpc_part, "gru_create_message_queue() returned " - "error=%d\n", ret); - ret = -EINVAL; - goto out_6; - } - - /* allow other partitions to access this GRU mq */ - xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size); - if (xp_ret != xpSuccess) { - ret = -EACCES; - goto out_6; - } - - return mq; - - /* something went wrong */ -out_6: - free_irq(mq->irq, NULL); -out_5: - xpc_release_gru_mq_irq_uv(mq); -out_4: - xpc_gru_mq_watchlist_free_uv(mq); -out_3: - free_pages((unsigned long)mq->address, pg_order); -out_2: - kfree(mq->gru_mq_desc); -out_1: - kfree(mq); -out_0: - return ERR_PTR(ret); -} - -static void -xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq) -{ - unsigned int mq_size; - int pg_order; - int ret; - - /* disallow other partitions to access GRU mq */ - mq_size = 1UL << mq->order; - ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size); - BUG_ON(ret != xpSuccess); - - /* unregister irq handler and release mq irq/vector mapping */ - free_irq(mq->irq, NULL); - xpc_release_gru_mq_irq_uv(mq); - - /* disable generation of irq when GRU mq op occurs to this mq */ - xpc_gru_mq_watchlist_free_uv(mq); - - pg_order = mq->order - PAGE_SHIFT; - free_pages((unsigned long)mq->address, pg_order); - - kfree(mq); -} - -static enum xp_retval -xpc_send_gru_msg(struct gru_message_queue_desc *gru_mq_desc, void *msg, - size_t msg_size) -{ - enum xp_retval xp_ret; - int ret; - - while (1) { - ret = gru_send_message_gpa(gru_mq_desc, msg, msg_size); - if (ret == MQE_OK) { - xp_ret = xpSuccess; - break; - } - - if (ret == MQE_QUEUE_FULL) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_QUEUE_FULL\n"); - /* !!! handle QLimit reached; delay & try again */ - /* ??? Do we add a limit to the number of retries? */ - (void)msleep_interruptible(10); - } else if (ret == MQE_CONGESTION) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_CONGESTION\n"); - /* !!! handle LB Overflow; simply try again */ - /* ??? Do we add a limit to the number of retries? */ - } else { - /* !!! Currently this is MQE_UNEXPECTED_CB_ERR */ - dev_err(xpc_chan, "gru_send_message_gpa() returned " - "error=%d\n", ret); - xp_ret = xpGruSendMqError; - break; - } - } - return xp_ret; -} - -static void -xpc_process_activate_IRQ_rcvd_uv(void) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - u8 act_state_req; - - DBUG_ON(xpc_activate_IRQ_rcvd == 0); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part = &xpc_partitions[partid]; - - if (part->sn.uv.act_state_req == 0) - continue; - - xpc_activate_IRQ_rcvd--; - BUG_ON(xpc_activate_IRQ_rcvd < 0); - - act_state_req = part->sn.uv.act_state_req; - part->sn.uv.act_state_req = 0; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - if (act_state_req == XPC_P_ASR_ACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else if (part->act_state == XPC_P_AS_DEACTIVATING) - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_REACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_DEACTIVATE_UV) { - XPC_DEACTIVATE_PARTITION(part, part->sn.uv.reason); - - } else { - BUG(); - } - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (xpc_activate_IRQ_rcvd == 0) - break; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - -} - -static void -xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, - struct xpc_activate_mq_msghdr_uv *msg_hdr, - int part_setup, - int *wakeup_hb_checker) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_openclose_args *args; - - part_uv->remote_act_state = msg_hdr->act_state; - - switch (msg_hdr->type) { - case XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV: - /* syncing of remote_act_state was just done above */ - break; - - case XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_activate_req_uv *msg; - - /* - * ??? Do we deal here with ts_jiffies being different - * ??? if act_state != XPC_P_AS_INACTIVE instead of - * ??? below? - */ - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_activate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_ACTIVATE_UV; - part->remote_rp_pa = msg->rp_gpa; /* !!! _pa is _gpa */ - part->remote_rp_ts_jiffies = msg_hdr->rp_ts_jiffies; - part_uv->heartbeat_gpa = msg->heartbeat_gpa; - - if (msg->activate_gru_mq_desc_gpa != - part_uv->activate_gru_mq_desc_gpa) { - spin_lock(&part_uv->flags_lock); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock(&part_uv->flags_lock); - part_uv->activate_gru_mq_desc_gpa = - msg->activate_gru_mq_desc_gpa; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - break; - } - case XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_deactivate_req_uv *msg; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_deactivate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = msg->reason; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_closerequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closerequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->reason = msg->reason; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: { - struct xpc_activate_mq_msg_chctl_closereply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closereply_uv, - hdr); - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_openrequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openrequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->entry_size = msg->entry_size; - args->local_nentries = msg->local_nentries; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: { - struct xpc_activate_mq_msg_chctl_openreply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openreply_uv, hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->remote_nentries = msg->remote_nentries; - args->local_nentries = msg->local_nentries; - args->local_msgqueue_pa = msg->notify_gru_mq_desc_gpa; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: { - struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_opencomplete_uv, hdr); - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENCOMPLETE; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - } - fallthrough; - case XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - case XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - default: - dev_err(xpc_part, "received unknown activate_mq msg type=%d " - "from partition=%d\n", msg_hdr->type, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadMsgType; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - - if (msg_hdr->rp_ts_jiffies != part->remote_rp_ts_jiffies && - part->remote_rp_ts_jiffies != 0) { - /* - * ??? Does what we do here need to be sensitive to - * ??? act_state or remote_act_state? - */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_REACTIVATE_UV; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - } -} - -static irqreturn_t -xpc_handle_activate_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr; - short partid; - struct xpc_partition *part; - int wakeup_hb_checker = 0; - int part_referenced; - - while (1) { - msg_hdr = gru_get_next_message(xpc_activate_mq_uv->gru_mq_desc); - if (msg_hdr == NULL) - break; - - partid = msg_hdr->partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_activate_IRQ_uv() " - "received invalid partid=0x%x in message\n", - partid); - } else { - part = &xpc_partitions[partid]; - - part_referenced = xpc_part_ref(part); - xpc_handle_activate_mq_msg_uv(part, msg_hdr, - part_referenced, - &wakeup_hb_checker); - if (part_referenced) - xpc_part_deref(part); - } - - gru_free_message(xpc_activate_mq_uv->gru_mq_desc, msg_hdr); - } - - if (wakeup_hb_checker) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - return IRQ_HANDLED; -} - -static enum xp_retval -xpc_cache_remote_gru_mq_desc_uv(struct gru_message_queue_desc *gru_mq_desc, - unsigned long gru_mq_desc_gpa) -{ - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(gru_mq_desc), gru_mq_desc_gpa, - sizeof(struct gru_message_queue_desc)); - if (ret == xpSuccess) - gru_mq_desc->mq = NULL; - - return ret; -} - -static enum xp_retval -xpc_send_activate_IRQ_uv(struct xpc_partition *part, void *msg, size_t msg_size, - int msg_type) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr = msg; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct gru_message_queue_desc *gru_mq_desc; - unsigned long irq_flags; - enum xp_retval ret; - - DBUG_ON(msg_size > XPC_ACTIVATE_MSG_SIZE_UV); - - msg_hdr->type = msg_type; - msg_hdr->partid = xp_partition_id; - msg_hdr->act_state = part->act_state; - msg_hdr->rp_ts_jiffies = xpc_rsvd_page->ts_jiffies; - - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); -again: - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) { - gru_mq_desc = part_uv->cached_activate_gru_mq_desc; - if (gru_mq_desc == NULL) { - gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc, - GFP_ATOMIC); - if (gru_mq_desc == NULL) { - ret = xpNoMemory; - goto done; - } - part_uv->cached_activate_gru_mq_desc = gru_mq_desc; - } - - ret = xpc_cache_remote_gru_mq_desc_uv(gru_mq_desc, - part_uv-> - activate_gru_mq_desc_gpa); - if (ret != xpSuccess) - goto done; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - } - - /* ??? Is holding a spin_lock (ch->lock) during this call a bad idea? */ - ret = xpc_send_gru_msg(part_uv->cached_activate_gru_mq_desc, msg, - msg_size); - if (ret != xpSuccess) { - smp_rmb(); /* ensure a fresh copy of part_uv->flags */ - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) - goto again; - } -done: - mutex_unlock(&part_uv->cached_activate_gru_mq_desc_mutex); - return ret; -} - -static void -xpc_send_activate_IRQ_part_uv(struct xpc_partition *part, void *msg, - size_t msg_size, int msg_type) -{ - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) - XPC_DEACTIVATE_PARTITION(part, ret); -} - -static void -xpc_send_activate_IRQ_ch_uv(struct xpc_channel *ch, unsigned long *irq_flags, - void *msg, size_t msg_size, int msg_type) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) { - if (irq_flags != NULL) - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - XPC_DEACTIVATE_PARTITION(part, ret); - - if (irq_flags != NULL) - spin_lock_irqsave(&ch->lock, *irq_flags); - } -} - -static void -xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - - /* - * !!! Make our side think that the remote partition sent an activate - * !!! mq message our way by doing what the activate IRQ handler would - * !!! do had one really been sent. - */ - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = act_state_req; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); -} - -static enum xp_retval -xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa, - size_t *len) -{ - s64 status; - enum xp_retval ret; - - status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa, - (u64 *)len); - if (status == BIOS_STATUS_SUCCESS) - ret = xpSuccess; - else if (status == BIOS_STATUS_MORE_PASSES) - ret = xpNeedMoreInfo; - else - ret = xpBiosError; - - return ret; -} - -static int -xpc_setup_rsvd_page_uv(struct xpc_rsvd_page *rp) -{ - xpc_heartbeat_uv = - &xpc_partitions[sn_partition_id].sn.uv.cached_heartbeat; - rp->sn.uv.heartbeat_gpa = uv_gpa(xpc_heartbeat_uv); - rp->sn.uv.activate_gru_mq_desc_gpa = - uv_gpa(xpc_activate_mq_uv->gru_mq_desc); - return 0; -} - -static void -xpc_allow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_all_hbs_uv(void) -{ -} - -static void -xpc_increment_heartbeat_uv(void) -{ - xpc_heartbeat_uv->value++; -} - -static void -xpc_offline_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 1; -} - -static void -xpc_online_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_init_uv(void) -{ - xpc_heartbeat_uv->value = 1; - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_exit_uv(void) -{ - xpc_offline_heartbeat_uv(); -} - -static enum xp_retval -xpc_get_remote_heartbeat_uv(struct xpc_partition *part) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(&part_uv->cached_heartbeat), - part_uv->heartbeat_gpa, - sizeof(struct xpc_heartbeat_uv)); - if (ret != xpSuccess) - return ret; - - if (part_uv->cached_heartbeat.value == part->last_heartbeat && - !part_uv->cached_heartbeat.offline) { - - ret = xpNoHeartbeat; - } else { - part->last_heartbeat = part_uv->cached_heartbeat.value; - } - return ret; -} - -static void -xpc_request_partition_activation_uv(struct xpc_rsvd_page *remote_rp, - unsigned long remote_rp_gpa, int nasid) -{ - short partid = remote_rp->SAL_partid; - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_activate_mq_msg_activate_req_uv msg; - - part->remote_rp_pa = remote_rp_gpa; /* !!! _pa here is really _gpa */ - part->remote_rp_ts_jiffies = remote_rp->ts_jiffies; - part->sn.uv.heartbeat_gpa = remote_rp->sn.uv.heartbeat_gpa; - part->sn.uv.activate_gru_mq_desc_gpa = - remote_rp->sn.uv.activate_gru_mq_desc_gpa; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state == XPC_P_AS_INACTIVE) { - msg.rp_gpa = uv_gpa(xpc_rsvd_page); - msg.heartbeat_gpa = xpc_rsvd_page->sn.uv.heartbeat_gpa; - msg.activate_gru_mq_desc_gpa = - xpc_rsvd_page->sn.uv.activate_gru_mq_desc_gpa; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV); - } - - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_reactivation_uv(struct xpc_partition *part) -{ - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_deactivation_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_deactivate_req_uv msg; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state != XPC_P_AS_DEACTIVATING && - part->sn.uv.remote_act_state != XPC_P_AS_INACTIVE) { - - msg.reason = part->reason; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV); - } -} - -static void -xpc_cancel_partition_deactivation_request_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static void -xpc_init_fifo_uv(struct xpc_fifo_head_uv *head) -{ - head->first = NULL; - head->last = NULL; - spin_lock_init(&head->lock); - head->n_entries = 0; -} - -static void * -xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head) -{ - unsigned long irq_flags; - struct xpc_fifo_entry_uv *first; - - spin_lock_irqsave(&head->lock, irq_flags); - first = head->first; - if (head->first != NULL) { - head->first = first->next; - if (head->first == NULL) - head->last = NULL; - - head->n_entries--; - BUG_ON(head->n_entries < 0); - - first->next = NULL; - } - spin_unlock_irqrestore(&head->lock, irq_flags); - return first; -} - -static void -xpc_put_fifo_entry_uv(struct xpc_fifo_head_uv *head, - struct xpc_fifo_entry_uv *last) -{ - unsigned long irq_flags; - - last->next = NULL; - spin_lock_irqsave(&head->lock, irq_flags); - if (head->last != NULL) - head->last->next = last; - else - head->first = last; - head->last = last; - head->n_entries++; - spin_unlock_irqrestore(&head->lock, irq_flags); -} - -static int -xpc_n_of_fifo_entries_uv(struct xpc_fifo_head_uv *head) -{ - return head->n_entries; -} - -/* - * Setup the channel structures that are uv specific. - */ -static enum xp_retval -xpc_setup_ch_structures_uv(struct xpc_partition *part) -{ - struct xpc_channel_uv *ch_uv; - int ch_number; - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch_uv = &part->channels[ch_number].sn.uv; - - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - } - - return xpSuccess; -} - -/* - * Teardown the channel structures that are uv specific. - */ -static void -xpc_teardown_ch_structures_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static enum xp_retval -xpc_make_first_contact_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - /* - * We send a sync msg to get the remote partition's remote_act_state - * updated to our current act_state which at this point should - * be XPC_P_AS_ACTIVATING. - */ - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV); - - while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) || - (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) { - - dev_dbg(xpc_part, "waiting to make first contact with " - "partition %d\n", XPC_PARTID(part)); - - /* wait a 1/4 of a second or so */ - (void)msleep_interruptible(250); - - if (part->act_state == XPC_P_AS_DEACTIVATING) - return part->reason; - } - - return xpSuccess; -} - -static u64 -xpc_get_chctl_all_flags_uv(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - chctl = part->chctl; - if (chctl.all_flags != 0) - part->chctl.all_flags = 0; - - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - return chctl.all_flags; -} - -static enum xp_retval -xpc_allocate_send_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_send_msg_slot_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->local_nentries; nentries > 0; nentries--) { - nbytes = nentries * sizeof(struct xpc_send_msg_slot_uv); - ch_uv->send_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->send_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = &ch_uv->send_msg_slots[entry]; - - msg_slot->msg_slot_number = entry; - xpc_put_fifo_entry_uv(&ch_uv->msg_slot_free_list, - &msg_slot->next); - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->local_nentries) - ch->local_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -static enum xp_retval -xpc_allocate_recv_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->remote_nentries; nentries > 0; nentries--) { - nbytes = nentries * ch->entry_size; - ch_uv->recv_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->recv_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = ch_uv->recv_msg_slots + - entry * ch->entry_size; - - msg_slot->hdr.msg_slot_number = entry; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->remote_nentries) - ch->remote_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -/* - * Allocate msg_slots associated with the channel. - */ -static enum xp_retval -xpc_setup_msg_structures_uv(struct xpc_channel *ch) -{ - static enum xp_retval ret; - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch->flags & XPC_C_SETUP); - - ch_uv->cached_notify_gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc); - if (ch_uv->cached_notify_gru_mq_desc == NULL) - return xpNoMemory; - - ret = xpc_allocate_send_msg_slot_uv(ch); - if (ret == xpSuccess) { - - ret = xpc_allocate_recv_msg_slot_uv(ch); - if (ret != xpSuccess) { - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - } - } - return ret; -} - -/* - * Free up msg_slots and clear other stuff that were setup for the specified - * channel. - */ -static void -xpc_teardown_msg_structures_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - lockdep_assert_held(&ch->lock); - - kfree(ch_uv->cached_notify_gru_mq_desc); - ch_uv->cached_notify_gru_mq_desc = NULL; - - if (ch->flags & XPC_C_SETUP) { - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - kfree(ch_uv->recv_msg_slots); - } -} - -static void -xpc_send_chctl_closerequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closerequest_uv msg; - - msg.ch_number = ch->number; - msg.reason = ch->reason; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV); -} - -static void -xpc_send_chctl_closereply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closereply_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV); -} - -static void -xpc_send_chctl_openrequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openrequest_uv msg; - - msg.ch_number = ch->number; - msg.entry_size = ch->entry_size; - msg.local_nentries = ch->local_nentries; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV); -} - -static void -xpc_send_chctl_openreply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openreply_uv msg; - - msg.ch_number = ch->number; - msg.local_nentries = ch->local_nentries; - msg.remote_nentries = ch->remote_nentries; - msg.notify_gru_mq_desc_gpa = uv_gpa(xpc_notify_mq_uv->gru_mq_desc); - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV); -} - -static void -xpc_send_chctl_opencomplete_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_opencomplete_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV); -} - -static void -xpc_send_chctl_local_msgrequest_uv(struct xpc_partition *part, int ch_number) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[ch_number] |= XPC_CHCTL_MSGREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); -} - -static enum xp_retval -xpc_save_remote_msgqueue_pa_uv(struct xpc_channel *ch, - unsigned long gru_mq_desc_gpa) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch_uv->cached_notify_gru_mq_desc == NULL); - return xpc_cache_remote_gru_mq_desc_uv(ch_uv->cached_notify_gru_mq_desc, - gru_mq_desc_gpa); -} - -static void -xpc_indicate_partition_engaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV); -} - -static void -xpc_indicate_partition_disengaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV); -} - -static void -xpc_assume_partition_disengaged_uv(short partid) -{ - struct xpc_partition_uv *part_uv = &xpc_partitions[partid].sn.uv; - unsigned long irq_flags; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); -} - -static int -xpc_partition_engaged_uv(short partid) -{ - return (xpc_partitions[partid].sn.uv.flags & XPC_P_ENGAGED_UV) != 0; -} - -static int -xpc_any_partition_engaged_uv(void) -{ - struct xpc_partition_uv *part_uv; - short partid; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - if ((part_uv->flags & XPC_P_ENGAGED_UV) != 0) - return 1; - } - return 0; -} - -static enum xp_retval -xpc_allocate_msg_slot_uv(struct xpc_channel *ch, u32 flags, - struct xpc_send_msg_slot_uv **address_of_msg_slot) -{ - enum xp_retval ret; - struct xpc_send_msg_slot_uv *msg_slot; - struct xpc_fifo_entry_uv *entry; - - while (1) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list); - if (entry != NULL) - break; - - if (flags & XPC_NOWAIT) - return xpNoWait; - - ret = xpc_allocate_msg_wait(ch); - if (ret != xpInterrupted && ret != xpTimeout) - return ret; - } - - msg_slot = container_of(entry, struct xpc_send_msg_slot_uv, next); - *address_of_msg_slot = msg_slot; - return xpSuccess; -} - -static void -xpc_free_msg_slot_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot) -{ - xpc_put_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list, &msg_slot->next); - - /* wakeup anyone waiting for a free msg slot */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); -} - -static void -xpc_notify_sender_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot, - enum xp_retval reason) -{ - xpc_notify_func func = msg_slot->func; - - if (func != NULL && cmpxchg(&msg_slot->func, func, NULL) == func) { - - atomic_dec(&ch->n_to_notify); - - dev_dbg(xpc_chan, "msg_slot->func() called, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - - func(reason, ch->partid, ch->number, msg_slot->key); - - dev_dbg(xpc_chan, "msg_slot->func() returned, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - } -} - -static void -xpc_handle_notify_mq_ack_uv(struct xpc_channel *ch, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry = msg->hdr.msg_slot_number % ch->local_nentries; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - - BUG_ON(msg_slot->msg_slot_number != msg->hdr.msg_slot_number); - msg_slot->msg_slot_number += ch->local_nentries; - - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, xpMsgDelivered); - - xpc_free_msg_slot_uv(ch, msg_slot); -} - -static void -xpc_handle_notify_mq_msg_uv(struct xpc_partition *part, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_channel *ch; - struct xpc_channel_uv *ch_uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int ch_number = msg->hdr.ch_number; - - if (unlikely(ch_number >= part->nchannels)) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received invalid " - "channel number=0x%x in message from partid=%d\n", - ch_number, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadChannelNumber; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); - return; - } - - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - if (!(ch->flags & XPC_C_CONNECTED)) { - xpc_msgqueue_deref(ch); - return; - } - - /* see if we're really dealing with an ACK for a previously sent msg */ - if (msg->hdr.size == 0) { - xpc_handle_notify_mq_ack_uv(ch, msg); - xpc_msgqueue_deref(ch); - return; - } - - /* we're dealing with a normal message sent via the notify_mq */ - ch_uv = &ch->sn.uv; - - msg_slot = ch_uv->recv_msg_slots + - (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size; - - BUG_ON(msg_slot->hdr.size != 0); - - memcpy(msg_slot, msg, msg->hdr.size); - - xpc_put_fifo_entry_uv(&ch_uv->recv_msg_list, &msg_slot->hdr.u.next); - - if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) { - /* - * If there is an existing idle kthread get it to deliver - * the payload, otherwise we'll have to get the channel mgr - * for this partition to create a kthread to do the delivery. - */ - if (atomic_read(&ch->kthreads_idle) > 0) - wake_up_nr(&ch->idle_wq, 1); - else - xpc_send_chctl_local_msgrequest_uv(part, ch->number); - } - xpc_msgqueue_deref(ch); -} - -static irqreturn_t -xpc_handle_notify_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_notify_mq_msg_uv *msg; - short partid; - struct xpc_partition *part; - - while ((msg = gru_get_next_message(xpc_notify_mq_uv->gru_mq_desc)) != - NULL) { - - partid = msg->hdr.partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received " - "invalid partid=0x%x in message\n", partid); - } else { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - xpc_handle_notify_mq_msg_uv(part, msg); - xpc_part_deref(part); - } - } - - gru_free_message(xpc_notify_mq_uv->gru_mq_desc, msg); - } - - return IRQ_HANDLED; -} - -static int -xpc_n_of_deliverable_payloads_uv(struct xpc_channel *ch) -{ - return xpc_n_of_fifo_entries_uv(&ch->sn.uv.recv_msg_list); -} - -static void -xpc_process_msg_chctl_flags_uv(struct xpc_partition *part, int ch_number) -{ - struct xpc_channel *ch = &part->channels[ch_number]; - int ndeliverable_payloads; - - xpc_msgqueue_ref(ch); - - ndeliverable_payloads = xpc_n_of_deliverable_payloads_uv(ch); - - if (ndeliverable_payloads > 0 && - (ch->flags & XPC_C_CONNECTED) && - (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE)) { - - xpc_activate_kthreads(ch, ndeliverable_payloads); - } - - xpc_msgqueue_deref(ch); -} - -static enum xp_retval -xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload, - u16 payload_size, u8 notify_type, xpc_notify_func func, - void *key) -{ - enum xp_retval ret = xpSuccess; - struct xpc_send_msg_slot_uv *msg_slot = NULL; - struct xpc_notify_mq_msg_uv *msg; - u8 msg_buffer[XPC_NOTIFY_MSG_SIZE_UV]; - size_t msg_size; - - DBUG_ON(notify_type != XPC_N_CALL); - - msg_size = sizeof(struct xpc_notify_mq_msghdr_uv) + payload_size; - if (msg_size > ch->entry_size) - return xpPayloadTooBig; - - xpc_msgqueue_ref(ch); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_1; - } - if (!(ch->flags & XPC_C_CONNECTED)) { - ret = xpNotConnected; - goto out_1; - } - - ret = xpc_allocate_msg_slot_uv(ch, flags, &msg_slot); - if (ret != xpSuccess) - goto out_1; - - if (func != NULL) { - atomic_inc(&ch->n_to_notify); - - msg_slot->key = key; - smp_wmb(); /* a non-NULL func must hit memory after the key */ - msg_slot->func = func; - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_2; - } - } - - msg = (struct xpc_notify_mq_msg_uv *)&msg_buffer; - msg->hdr.partid = xp_partition_id; - msg->hdr.ch_number = ch->number; - msg->hdr.size = msg_size; - msg->hdr.msg_slot_number = msg_slot->msg_slot_number; - memcpy(&msg->payload, payload, payload_size); - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - msg_size); - if (ret == xpSuccess) - goto out_1; - - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -out_2: - if (func != NULL) { - /* - * Try to NULL the msg_slot's func field. If we fail, then - * xpc_notify_senders_of_disconnect_uv() beat us to it, in which - * case we need to pretend we succeeded to send the message - * since the user will get a callout for the disconnect error - * by xpc_notify_senders_of_disconnect_uv(), and to also get an - * error returned here will confuse them. Additionally, since - * in this case the channel is being disconnected we don't need - * to put the msg_slot back on the free list. - */ - if (cmpxchg(&msg_slot->func, func, NULL) != func) { - ret = xpSuccess; - goto out_1; - } - - msg_slot->key = NULL; - atomic_dec(&ch->n_to_notify); - } - xpc_free_msg_slot_uv(ch, msg_slot); -out_1: - xpc_msgqueue_deref(ch); - return ret; -} - -/* - * Tell the callers of xpc_send_notify() that the status of their payloads - * is unknown because the channel is now disconnecting. - * - * We don't worry about putting these msg_slots on the free list since the - * msg_slots themselves are about to be kfree'd. - */ -static void -xpc_notify_senders_of_disconnect_uv(struct xpc_channel *ch) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry; - - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - - for (entry = 0; entry < ch->local_nentries; entry++) { - - if (atomic_read(&ch->n_to_notify) == 0) - break; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, ch->reason); - } -} - -/* - * Get the next deliverable message's payload. - */ -static void * -xpc_get_deliverable_payload_uv(struct xpc_channel *ch) -{ - struct xpc_fifo_entry_uv *entry; - struct xpc_notify_mq_msg_uv *msg; - void *payload = NULL; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.recv_msg_list); - if (entry != NULL) { - msg = container_of(entry, struct xpc_notify_mq_msg_uv, - hdr.u.next); - payload = &msg->payload; - } - } - return payload; -} - -static void -xpc_received_payload_uv(struct xpc_channel *ch, void *payload) -{ - struct xpc_notify_mq_msg_uv *msg; - enum xp_retval ret; - - msg = container_of(payload, struct xpc_notify_mq_msg_uv, payload); - - /* return an ACK to the sender of this message */ - - msg->hdr.partid = xp_partition_id; - msg->hdr.size = 0; /* size of zero indicates this is an ACK */ - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - sizeof(struct xpc_notify_mq_msghdr_uv)); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -} - -static const struct xpc_arch_operations xpc_arch_ops_uv = { - .setup_partitions = xpc_setup_partitions_uv, - .teardown_partitions = xpc_teardown_partitions_uv, - .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_uv, - .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_uv, - .setup_rsvd_page = xpc_setup_rsvd_page_uv, - - .allow_hb = xpc_allow_hb_uv, - .disallow_hb = xpc_disallow_hb_uv, - .disallow_all_hbs = xpc_disallow_all_hbs_uv, - .increment_heartbeat = xpc_increment_heartbeat_uv, - .offline_heartbeat = xpc_offline_heartbeat_uv, - .online_heartbeat = xpc_online_heartbeat_uv, - .heartbeat_init = xpc_heartbeat_init_uv, - .heartbeat_exit = xpc_heartbeat_exit_uv, - .get_remote_heartbeat = xpc_get_remote_heartbeat_uv, - - .request_partition_activation = - xpc_request_partition_activation_uv, - .request_partition_reactivation = - xpc_request_partition_reactivation_uv, - .request_partition_deactivation = - xpc_request_partition_deactivation_uv, - .cancel_partition_deactivation_request = - xpc_cancel_partition_deactivation_request_uv, - - .setup_ch_structures = xpc_setup_ch_structures_uv, - .teardown_ch_structures = xpc_teardown_ch_structures_uv, - - .make_first_contact = xpc_make_first_contact_uv, - - .get_chctl_all_flags = xpc_get_chctl_all_flags_uv, - .send_chctl_closerequest = xpc_send_chctl_closerequest_uv, - .send_chctl_closereply = xpc_send_chctl_closereply_uv, - .send_chctl_openrequest = xpc_send_chctl_openrequest_uv, - .send_chctl_openreply = xpc_send_chctl_openreply_uv, - .send_chctl_opencomplete = xpc_send_chctl_opencomplete_uv, - .process_msg_chctl_flags = xpc_process_msg_chctl_flags_uv, - - .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_uv, - - .setup_msg_structures = xpc_setup_msg_structures_uv, - .teardown_msg_structures = xpc_teardown_msg_structures_uv, - - .indicate_partition_engaged = xpc_indicate_partition_engaged_uv, - .indicate_partition_disengaged = xpc_indicate_partition_disengaged_uv, - .assume_partition_disengaged = xpc_assume_partition_disengaged_uv, - .partition_engaged = xpc_partition_engaged_uv, - .any_partition_engaged = xpc_any_partition_engaged_uv, - - .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_uv, - .send_payload = xpc_send_payload_uv, - .get_deliverable_payload = xpc_get_deliverable_payload_uv, - .received_payload = xpc_received_payload_uv, - .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv, -}; - -static int -xpc_init_mq_node(int nid) -{ - int cpu; - - cpus_read_lock(); - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_activate_mq_uv = - xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid, - XPC_ACTIVATE_IRQ_NAME, - xpc_handle_activate_IRQ_uv); - if (!IS_ERR(xpc_activate_mq_uv)) - break; - } - if (IS_ERR(xpc_activate_mq_uv)) { - cpus_read_unlock(); - return PTR_ERR(xpc_activate_mq_uv); - } - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_notify_mq_uv = - xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid, - XPC_NOTIFY_IRQ_NAME, - xpc_handle_notify_IRQ_uv); - if (!IS_ERR(xpc_notify_mq_uv)) - break; - } - if (IS_ERR(xpc_notify_mq_uv)) { - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); - cpus_read_unlock(); - return PTR_ERR(xpc_notify_mq_uv); - } - - cpus_read_unlock(); - return 0; -} - -int -xpc_init_uv(void) -{ - int nid; - int ret = 0; - - xpc_arch_ops = xpc_arch_ops_uv; - - if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) { - dev_err(xpc_part, "xpc_notify_mq_msghdr_uv is larger than %d\n", - XPC_MSG_HDR_MAX_SIZE); - return -E2BIG; - } - - if (xpc_mq_node < 0) - for_each_online_node(nid) { - ret = xpc_init_mq_node(nid); - - if (!ret) - break; - } - else - ret = xpc_init_mq_node(xpc_mq_node); - - if (ret < 0) - dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n", - -ret); - - return ret; -} - -void -xpc_exit_uv(void) -{ - xpc_destroy_gru_mq_uv(xpc_notify_mq_uv); - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); -} - -module_param(xpc_mq_node, int, 0); -MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues."); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c deleted file mode 100644 index 1533b72d57b1..000000000000 --- a/drivers/misc/sgi-xp/xpnet.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * Cross Partition Network Interface (XPNET) support - * - * XPNET provides a virtual network layered on top of the Cross - * Partition communication layer. - * - * XPNET provides direct point-to-point and broadcast-like support - * for an ethernet-like device. The ethernet broadcast medium is - * replaced with a point-to-point message structure which passes - * pointers to a DMA-capable block that a remote partition should - * retrieve and pass to the upper level networking layer. - * - */ - -#include -#include -#include -#include -#include "xp.h" - -/* - * The message payload transferred by XPC. - * - * buf_pa is the physical address where the DMA should pull from. - * - * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a - * cacheline boundary. To accomplish this, we record the number of - * bytes from the beginning of the first cacheline to the first useful - * byte of the skb (leadin_ignore) and the number of bytes from the - * last useful byte of the skb to the end of the last cacheline - * (tailout_ignore). - * - * size is the number of bytes to transfer which includes the skb->len - * (useful bytes of the senders skb) plus the leadin and tailout - */ -struct xpnet_message { - u16 version; /* Version for this message */ - u16 embedded_bytes; /* #of bytes embedded in XPC message */ - u32 magic; /* Special number indicating this is xpnet */ - unsigned long buf_pa; /* phys address of buffer to retrieve */ - u32 size; /* #of bytes in buffer */ - u8 leadin_ignore; /* #of bytes to ignore at the beginning */ - u8 tailout_ignore; /* #of bytes to ignore at the end */ - unsigned char data; /* body of small packets */ -}; - -/* - * Determine the size of our message, the cacheline aligned size, - * and then the number of message will request from XPC. - * - * XPC expects each message to exist in an individual cacheline. - */ -#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE -#define XPNET_MSG_DATA_MAX \ - (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data)) -#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE) - -#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1) -#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1) - -/* - * Version number of XPNET implementation. XPNET can always talk to versions - * with same major #, and never talk to versions with a different version. - */ -#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor)) -#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf) - -#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */ -#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */ -#define XPNET_MAGIC 0x88786984 /* "XNET" */ - -#define XPNET_VALID_MSG(_m) \ - ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \ - && (msg->magic == XPNET_MAGIC)) - -#define XPNET_DEVICE_NAME "xp0" - -/* - * When messages are queued with xpc_send_notify, a kmalloc'd buffer - * of the following type is passed as a notification cookie. When the - * notification function is called, we use the cookie to decide - * whether all outstanding message sends have completed. The skb can - * then be released. - */ -struct xpnet_pending_msg { - struct sk_buff *skb; - atomic_t use_count; -}; - -static struct net_device *xpnet_device; - -/* - * When we are notified of other partitions activating, we add them to - * our bitmask of partitions to which we broadcast. - */ -static unsigned long *xpnet_broadcast_partitions; -/* protect above */ -static DEFINE_SPINLOCK(xpnet_broadcast_lock); - -/* - * Since the Block Transfer Engine (BTE) is being used for the transfer - * and it relies upon cache-line size transfers, we need to reserve at - * least one cache-line for head and tail alignment. The BTE is - * limited to 8MB transfers. - * - * Testing has shown that changing MTU to greater than 64KB has no effect - * on TCP as the two sides negotiate a Max Segment Size that is limited - * to 64K. Other protocols May use packets greater than this, but for - * now, the default is 64KB. - */ -#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) -/* 68 comes from min TCP+IP+MAC header */ -#define XPNET_MIN_MTU 68 -/* 32KB has been determined to be the ideal */ -#define XPNET_DEF_MTU (0x8000UL) - -/* - * The partid is encapsulated in the MAC address beginning in the following - * octet and it consists of two octets. - */ -#define XPNET_PARTID_OCTET 2 - -/* Define the XPNET debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpnet_dbg_name = { - .name = "xpnet" -}; - -static struct device xpnet_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xpnet_dbg_name -}; - -static struct device *xpnet = &xpnet_dbg_subname; - -/* - * Packet was recevied by XPC and forwarded to us. - */ -static void -xpnet_receive(short partid, int channel, struct xpnet_message *msg) -{ - struct sk_buff *skb; - void *dst; - enum xp_retval ret; - - if (!XPNET_VALID_MSG(msg)) { - /* - * Packet with a different XPC version. Ignore. - */ - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - /* reserve an extra cache line */ - skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES); - if (!skb) { - dev_err(xpnet, "failed on dev_alloc_skb(%d)\n", - msg->size + L1_CACHE_BYTES); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - - /* - * The allocated skb has some reserved space. - * In order to use xp_remote_memcpy(), we need to get the - * skb->data pointer moved forward. - */ - skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data & - (L1_CACHE_BYTES - 1)) + - msg->leadin_ignore)); - - /* - * Update the tail pointer to indicate data actually - * transferred. - */ - skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore)); - - /* - * Move the data over from the other side. - */ - if ((XPNET_VERSION_MINOR(msg->version) == 1) && - (msg->embedded_bytes != 0)) { - dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, " - "%lu)\n", skb->data, &msg->data, - (size_t)msg->embedded_bytes); - - skb_copy_to_linear_data(skb, &msg->data, - (size_t)msg->embedded_bytes); - } else { - dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t" - "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst, - (void *)msg->buf_pa, msg->size); - - ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size); - if (ret != xpSuccess) { - /* - * !!! Need better way of cleaning skb. Currently skb - * !!! appears in_use and we can't just call - * !!! dev_kfree_skb. - */ - dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) " - "returned error=0x%x\n", dst, - (void *)msg->buf_pa, msg->size, ret); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - } - - dev_dbg(xpnet, "head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - skb->protocol = eth_type_trans(skb, xpnet_device); - skb->ip_summed = CHECKSUM_UNNECESSARY; - - dev_dbg(xpnet, "passing skb to network layer\n" - "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", - (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->len); - - xpnet_device->stats.rx_packets++; - xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; - - netif_rx(skb); - xpc_received(partid, channel, (void *)msg); -} - -/* - * This is the handler which XPC calls during any sort of change in - * state or message reception on a connection. - */ -static void -xpnet_connection_activity(enum xp_retval reason, short partid, int channel, - void *data, void *key) -{ - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(channel != XPC_NET_CHANNEL); - - switch (reason) { - case xpMsgReceived: /* message received */ - DBUG_ON(data == NULL); - - xpnet_receive(partid, channel, (struct xpnet_message *)data); - break; - - case xpConnected: /* connection completed to a partition */ - spin_lock_bh(&xpnet_broadcast_lock); - __set_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - netif_carrier_on(xpnet_device); - - dev_dbg(xpnet, "%s connected to partition %d\n", - xpnet_device->name, partid); - break; - - default: - spin_lock_bh(&xpnet_broadcast_lock); - __clear_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - if (bitmap_empty(xpnet_broadcast_partitions, - xp_max_npartitions)) { - netif_carrier_off(xpnet_device); - } - - dev_dbg(xpnet, "%s disconnected from partition %d\n", - xpnet_device->name, partid); - break; - } -} - -static int -xpnet_dev_open(struct net_device *dev) -{ - enum xp_retval ret; - - dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, " - "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity, - (unsigned long)XPNET_MSG_SIZE, - (unsigned long)XPNET_MSG_NENTRIES, - (unsigned long)XPNET_MAX_KTHREADS, - (unsigned long)XPNET_MAX_IDLE_KTHREADS); - - ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL, - XPNET_MSG_SIZE, XPNET_MSG_NENTRIES, - XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS); - if (ret != xpSuccess) { - dev_err(xpnet, "ifconfig up of %s failed on XPC connect, " - "ret=%d\n", dev->name, ret); - - return -ENOMEM; - } - - dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name); - - return 0; -} - -static int -xpnet_dev_stop(struct net_device *dev) -{ - xpc_disconnect(XPC_NET_CHANNEL); - - dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name); - - return 0; -} - -/* - * Notification that the other end has received the message and - * DMA'd the skb information. At this point, they are done with - * our side. When all recipients are done processing, we - * release the skb and then release our pending message structure. - */ -static void -xpnet_send_completed(enum xp_retval reason, short partid, int channel, - void *__qm) -{ - struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm; - - DBUG_ON(queued_msg == NULL); - - dev_dbg(xpnet, "message to %d notified with reason %d\n", - partid, reason); - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_dbg(xpnet, "all acks for skb->head=-x%p\n", - (void *)queued_msg->skb->head); - - dev_kfree_skb_any(queued_msg->skb); - kfree(queued_msg); - } -} - -static void -xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, - u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid) -{ - u8 msg_buffer[XPNET_MSG_SIZE]; - struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer; - u16 msg_size = sizeof(struct xpnet_message); - enum xp_retval ret; - - msg->embedded_bytes = embedded_bytes; - if (unlikely(embedded_bytes != 0)) { - msg->version = XPNET_VERSION_EMBED; - dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n", - &msg->data, skb->data, (size_t)embedded_bytes); - skb_copy_from_linear_data(skb, &msg->data, - (size_t)embedded_bytes); - msg_size += embedded_bytes - 1; - } else { - msg->version = XPNET_VERSION; - } - msg->magic = XPNET_MAGIC; - msg->size = end_addr - start_addr; - msg->leadin_ignore = (u64)skb->data - start_addr; - msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb); - msg->buf_pa = xp_pa((void *)start_addr); - - dev_dbg(xpnet, "sending XPC message to %d:%d\n" - "msg->buf_pa=0x%lx, msg->size=%u, " - "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n", - dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - atomic_inc(&queued_msg->use_count); - - ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg, - msg_size, xpnet_send_completed, queued_msg); - if (unlikely(ret != xpSuccess)) - atomic_dec(&queued_msg->use_count); -} - -/* - * Network layer has formatted a packet (skb) and is ready to place it - * "on the wire". Prepare and send an xpnet_message to all partitions - * which have connected with us and are targets of this packet. - * - * MAC-NOTE: For the XPNET driver, the MAC address contains the - * destination partid. If the destination partid octets are 0xffff, - * this packet is to be broadcast to all connected partitions. - */ -static netdev_tx_t -xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xpnet_pending_msg *queued_msg; - u64 start_addr, end_addr; - short dest_partid; - u16 embedded_bytes = 0; - - dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - if (skb->data[0] == 0x33) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; /* nothing needed to be done */ - } - - /* - * The xpnet_pending_msg tracks how many outstanding - * xpc_send_notifies are relying on this skb. When none - * remain, release the skb. - */ - queued_msg = kmalloc_obj(struct xpnet_pending_msg, GFP_ATOMIC); - if (queued_msg == NULL) { - dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping " - "packet\n", sizeof(struct xpnet_pending_msg)); - - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - /* get the beginning of the first cacheline and end of last */ - start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb)); - - /* calculate how many bytes to embed in the XPC message */ - if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) { - /* skb->data does fit so embed */ - embedded_bytes = skb->len; - } - - /* - * Since the send occurs asynchronously, we set the count to one - * and begin sending. Any sends that happen to complete before - * we are done sending will not free the skb. We will be left - * with that task during exit. This also handles the case of - * a packet destined for a partition which is no longer up. - */ - atomic_set(&queued_msg->use_count, 1); - queued_msg->skb = skb; - - if (skb->data[0] == 0xff) { - /* we are being asked to broadcast to all partitions */ - for_each_set_bit(dest_partid, xpnet_broadcast_partitions, - xp_max_npartitions) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } else { - dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1]; - dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8; - - if (dest_partid >= 0 && - dest_partid < xp_max_npartitions && - test_bit(dest_partid, xpnet_broadcast_partitions) != 0) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_kfree_skb(skb); - kfree(queued_msg); - } - - return NETDEV_TX_OK; -} - -/* - * Deal with transmit timeouts coming from the network layer. - */ -static void -xpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - dev->stats.tx_errors++; -} - -static const struct net_device_ops xpnet_netdev_ops = { - .ndo_open = xpnet_dev_open, - .ndo_stop = xpnet_dev_stop, - .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_tx_timeout = xpnet_dev_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init -xpnet_init(void) -{ - u8 addr[ETH_ALEN]; - int result; - - if (!is_uv_system()) - return -ENODEV; - - dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); - - xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions, - GFP_KERNEL); - if (xpnet_broadcast_partitions == NULL) - return -ENOMEM; - - /* - * use ether_setup() to init the majority of our device - * structure and then override the necessary pieces. - */ - xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN, - ether_setup); - if (xpnet_device == NULL) { - bitmap_free(xpnet_broadcast_partitions); - return -ENOMEM; - } - - netif_carrier_off(xpnet_device); - - xpnet_device->netdev_ops = &xpnet_netdev_ops; - xpnet_device->mtu = XPNET_DEF_MTU; - xpnet_device->min_mtu = XPNET_MIN_MTU; - xpnet_device->max_mtu = XPNET_MAX_MTU; - - memset(addr, 0, sizeof(addr)); - /* - * Multicast assumes the LSB of the first octet is set for multicast - * MAC addresses. We chose the first octet of the MAC to be unlikely - * to collide with any vendor's officially issued MAC. - */ - addr[0] = 0x02; /* locally administered, no OUI */ - - addr[XPNET_PARTID_OCTET + 1] = xp_partition_id; - addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8); - eth_hw_addr_set(xpnet_device, addr); - - /* - * ether_setup() sets this to a multicast device. We are - * really not supporting multicast at this time. - */ - xpnet_device->flags &= ~IFF_MULTICAST; - - /* - * No need to checksum as it is a DMA transfer. The BTE will - * report an error if the data is not retrievable and the - * packet will be dropped. - */ - xpnet_device->features = NETIF_F_HW_CSUM; - - result = register_netdev(xpnet_device); - if (result != 0) { - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); - } - - return result; -} - -module_init(xpnet_init); - -static void __exit -xpnet_exit(void) -{ - dev_info(xpnet, "unregistering network device %s\n", - xpnet_device[0].name); - - unregister_netdev(xpnet_device); - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); -} - -module_exit(xpnet_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 1919d24c8236..c8990e83a65a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -370,7 +370,7 @@ static const struct of_device_id sram_dt_ids[] = { { .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config }, - {} + { } }; static int sram_probe(struct platform_device *pdev) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 2aac83ec4a39..9e2d24f89457 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -718,7 +718,7 @@ static void fpc202_remove(struct i2c_client *client) static const struct of_device_id fpc202_of_match[] = { { .compatible = "ti,fpc202" }, - {} + { } }; MODULE_DEVICE_TABLE(of, fpc202_of_match); diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c index 2fbd3fbdf713..50e121669c6d 100644 --- a/drivers/misc/tps6594-esm.c +++ b/drivers/misc/tps6594-esm.c @@ -62,7 +62,7 @@ static int tps6594_esm_probe(struct platform_device *pdev) tps6594_esm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG, diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 44fa81d6cec2..c3a0d6367f2c 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -308,7 +308,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev) tps6594_pfsm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } platform_set_drvdata(pdev, pfsm); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 03f19eda641e..c871ea860bb5 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id tsl2550_id[] = { - { "tsl2550" }, + { .name = "tsl2550" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2550_id); diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c index 26166357b255..1306b2c1ef99 100644 --- a/drivers/misc/vcpu_stall_detector.c +++ b/drivers/misc/vcpu_stall_detector.c @@ -224,8 +224,8 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev) } static const struct of_device_id vcpu_stall_detect_of_match[] = { - { .compatible = "qemu,vcpu-stall-detector", }, - {} + { .compatible = "qemu,vcpu-stall-detector" }, + { } }; MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match); diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index fffe068a26eb..c3ef3b98c432 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event, } } + *new_subscription_id = sub->id; if (have_new_id) { list_add_rcu(&sub->node, &subscriber_array[event]); retval = VMCI_SUCCESS; } else { + kfree(sub); retval = VMCI_ERROR_NO_RESOURCES; } mutex_unlock(&subscriber_mutex); - - *new_subscription_id = sub->id; return retval; } EXPORT_SYMBOL_GPL(vmci_event_subscribe); diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 476af89e751b..83355c0ec1d2 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -960,8 +960,8 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) } static const struct pci_device_id vmci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, - { 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI) }, + { } }; MODULE_DEVICE_TABLE(pci, vmci_ids); diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index 3135ba3a58ee..a19eed39c516 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -456,10 +456,25 @@ static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg) return err; } +static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high, + u32 offset, u32 *addr) +{ + if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) { + dev_dbg(xsdfec->dev, + "LDPC register offset %u outside space 0x%x-0x%x", + offset, base, high); + return -EINVAL; + } + + *addr = base + offset * XSDFEC_LDPC_REG_JUMP; + return 0; +} + static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, u32 offset) { u32 wdata; + u32 addr; if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 || (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { @@ -476,17 +491,11 @@ static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, k = k << XSDFEC_REG0_K_LSB; wdata = k | n; - if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x", - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE, + XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -494,6 +503,7 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, u32 no_packing, u32 nm, u32 offset) { u32 wdata; + u32 addr; if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) { dev_dbg(xsdfec->dev, "Psize is not in range"); @@ -510,17 +520,11 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK; wdata = nm | no_packing | psize; - if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x", - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG1_ADDR_BASE, + XSDFEC_LDPC_CODE_REG1_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -529,6 +533,7 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, u32 max_schedule, u32 offset) { u32 wdata; + u32 addr; if (nlayers < XSDFEC_REG2_NLAYERS_MIN || nlayers > XSDFEC_REG2_NLAYERS_MAX) { @@ -563,17 +568,11 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, wdata = (max_schedule | no_final_parity | special_qc | norm_type | nmqc | nlayers); - if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x", - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG2_ADDR_BASE, + XSDFEC_LDPC_CODE_REG2_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -581,20 +580,15 @@ static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off, u16 qc_off, u32 offset) { u32 wdata; + u32 addr; wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) | (la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off); - if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x", - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG3_ADDR_BASE, + XSDFEC_LDPC_CODE_REG3_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -1390,10 +1384,8 @@ static int xsdfec_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, xsdfec->irq, NULL, xsdfec_irq_thread, IRQF_ONESHOT, "xilinx-sdfec16", xsdfec); - if (err < 0) { - dev_err(dev, "unable to request IRQ%d", xsdfec->irq); + if (err < 0) goto err_xsdfec_dev; - } } err = ida_alloc(&dev_nrs, GFP_KERNEL); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 74ddbd0f79b0..c36c2a4c2a0b 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -28,6 +28,107 @@ source "drivers/nvmem/layouts/Kconfig" # Devices +if NVMEM_SYSFS + +config EEPROM_AT24 + tristate "I2C EEPROMs / RAMs / ROMs from most vendors" + depends on I2C && SYSFS + select REGMAP + select REGMAP_I2C + help + Enable this driver to get read/write support to most I2C EEPROMs + and compatible devices like FRAMs, SRAMs, ROMs etc. After you + configure the driver to know about each chip on your target + board. Use these generic chip names, instead of vendor-specific + ones like at24c64, 24lc02 or fm24c04: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + + If you use this with an SMBus adapter instead of an I2C adapter, + full functionality is not available. Only smaller devices are + supported (24c16 and below, max 4 kByte). + + This driver can also be built as a module. If so, the module + will be called at24. + +config EEPROM_AT25 + tristate "SPI EEPROMs (FRAMs) from most vendors" + depends on SPI && SYSFS + select SPI_MEM + help + Enable this driver to get read/write support to most SPI EEPROMs + and Cypress FRAMs, + after you configure the board init code to know about each eeprom + on your target board. + + This driver can also be built as a module. If so, the module + will be called at25. + +config EEPROM_93XX46 + tristate "Microwire EEPROM 93XX46 support" + depends on SPI && SYSFS + select REGMAP + help + Driver for the microwire EEPROM chipsets 93xx46x. The driver + supports both read and write commands and also the command to + erase the whole EEPROM. + + This driver can also be built as a module. If so, the module + will be called eeprom_93xx46. + + If unsure, say N. + +config EEPROM_EE1004 + tristate "SPD EEPROMs on DDR4 memory modules" + depends on I2C && SYSFS + help + Enable this driver to get read support to SPD EEPROMs following + the JEDEC EE1004 standard. These are typically found on DDR4 + SDRAM memory modules. + + This driver can also be built as a module. If so, the module + will be called ee1004. + +config EEPROM_M24LR + tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" + depends on I2C && SYSFS + select REGMAP_I2C + help + This enables support for STMicroelectronics M24LR RFID/NFC EEPROM + chips. These dual-interface devices expose two I2C addresses: + one for EEPROM memory access and another for control and system + configuration (e.g. UID, password handling). + + This driver provides a sysfs interface for control functions and + integrates with the nvmem subsystem for EEPROM access. + + To compile this driver as a module, choose M here: the + module will be called m24lr. + +endif # NVMEM_SYSFS + +config NVMEM_AIROHA_SMC_EFUSES + tristate "Airoha SMC eFuse support" + depends on ARM64 + depends on ARCH_AIROHA || COMPILE_TEST + depends on HAVE_ARM_SMCCC + default ARCH_AIROHA + help + Say y here to enable support for reading eFuses on Airoha AN7581 + SoCs. These are e.g. used to store factory programmed + calibration data required for the PCIe or the USB-C PHY or Thermal. + + This driver can also be built as a module. If so, the module will + be called nvmem-airoha-smc-efuses. + config NVMEM_AN8855_EFUSE tristate "Airoha AN8855 eFuse support" depends on COMPILE_TEST @@ -138,7 +239,7 @@ config NVMEM_JZ4780_EFUSE config NVMEM_LAN9662_OTPC tristate "Microchip LAN9662 OTP controller support" - depends on SOC_LAN966 || COMPILE_TEST + depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST depends on HAS_IOMEM help This driver enables the OTP controller available on Microchip LAN9662 @@ -262,7 +363,7 @@ config NVMEM_S32G_OCOTP Programmable memory pages. config NVMEM_QCOM_QFPROM - tristate "QCOM QFPROM Support" + tristate "Qualcomm QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM help @@ -273,7 +374,7 @@ config NVMEM_QCOM_QFPROM will be called nvmem_qfprom. config NVMEM_QCOM_SEC_QFPROM - tristate "QCOM SECURE QFPROM Support" + tristate "Qualcomm SECURE QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM depends on OF @@ -303,7 +404,6 @@ config NVMEM_RAVE_SP_EEPROM config NVMEM_RCAR_EFUSE tristate "Renesas R-Car Gen4 E-FUSE support" depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST - depends on NVMEM help Enable support for reading the fuses in the E-FUSE or OTP non-volatile memory block on Renesas R-Car Gen4 SoCs. @@ -483,4 +583,4 @@ config NVMEM_QORIQ_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_qoriq_efuse. -endif +endif # NVMEM diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 7252b8ec88d4..2bbfb9ff1885 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -10,6 +10,13 @@ nvmem_layouts-y := layouts.o obj-y += layouts/ # Devices +obj-$(CONFIG_EEPROM_AT24) += at24.o +obj-$(CONFIG_EEPROM_AT25) += at25.o +obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o +obj-$(CONFIG_EEPROM_EE1004) += ee1004.o +obj-$(CONFIG_EEPROM_M24LR) += m24lr.o +obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES) += nvmem-airoha-smc-efuses.o +nvmem-airoha-smc-efuses-y := airoha-smc-efuses.o obj-$(CONFIG_NVMEM_AN8855_EFUSE) += nvmem-an8855-efuse.o nvmem-an8855-efuse-y := an8855-efuse.o obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o diff --git a/drivers/nvmem/airoha-smc-efuses.c b/drivers/nvmem/airoha-smc-efuses.c new file mode 100644 index 000000000000..e56a99f4aa1f --- /dev/null +++ b/drivers/nvmem/airoha-smc-efuses.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Christian Marangi + */ + +#include +#include +#include +#include +#include +#include +#include + +#define AIROHA_SMC_EFUSE_FID 0x82000001 +#define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552 + +#define AIROHA_EFUSE_CELLS 64 + +struct airoha_efuse_bank_priv { + u32 bank_index; +}; + +static int airoha_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct regmap *regmap = context; + + return regmap_bulk_read(regmap, offset, + val, bytes / sizeof(u32)); +} + +static int airoha_efuse_reg_read(void *context, unsigned int offset, + unsigned int *val) +{ + struct airoha_efuse_bank_priv *priv = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, + AIROHA_SMC_EFUSE_SUB_ID_READ, + priv->bank_index, offset, 0, 0, 0, 0, &res); + + /* check if SMC reported an error */ + if (res.a0) + return -EIO; + + *val = res.a1; + return 0; +} + +static int airoha_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + for_each_child_of_node_scoped(dev->of_node, child) { + struct nvmem_config nvmem_config = { + .size = AIROHA_EFUSE_CELLS * sizeof(u32), + .stride = sizeof(u32), + .word_size = sizeof(u32), + .reg_read = airoha_efuse_read, + }; + struct regmap_config regmap_config = { + .reg_read = airoha_efuse_reg_read, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + struct airoha_efuse_bank_priv *priv; + struct nvmem_device *nvmem; + struct regmap *regmap; + const char *name; + u32 bank; + + ret = of_property_read_u32(child, "reg", &bank); + if (ret) + return ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u", + bank); + if (!name) + return -ENOMEM; + + priv->bank_index = bank; + + regmap_config.name = name; + regmap = devm_regmap_init(dev, NULL, priv, + ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvmem_config.name = name; + nvmem_config.priv = regmap; + nvmem_config.dev = dev; + nvmem_config.id = bank; + nvmem_config.of_node = child; + nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + } + + return 0; +} + +static const struct of_device_id airoha_efuse_of_match[] = { + { .compatible = "airoha,an7581-efuses", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, airoha_efuse_of_match); + +static struct platform_driver airoha_efuse_driver = { + .probe = airoha_efuse_probe, + .driver = { + .name = "airoha-efuse", + .of_match_table = airoha_efuse_of_match, + }, +}; +module_platform_driver(airoha_efuse_driver); + +MODULE_AUTHOR("Christian Marangi "); +MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/at24.c b/drivers/nvmem/at24.c similarity index 100% rename from drivers/misc/eeprom/at24.c rename to drivers/nvmem/at24.c diff --git a/drivers/misc/eeprom/at25.c b/drivers/nvmem/at25.c similarity index 100% rename from drivers/misc/eeprom/at25.c rename to drivers/nvmem/at25.c diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index aaa6537798bf..9f77aee37121 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device } WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); + if (priv->data_len < sizeof(struct brcm_nvram_header)) { + dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len); + return -EINVAL; + } + priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); if (!priv->data) return -ENOMEM; @@ -187,9 +192,13 @@ static int brcm_nvram_parse(struct brcm_nvram *priv) } len = le32_to_cpu(header->len); - if (len > priv->nvmem_size) { - dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, - priv->nvmem_size); + if (len < sizeof(*header)) { + dev_err(dev, "NVRAM length (%zd) too small\n", len); + return -EINVAL; + } + if (len > priv->data_len) { + dev_err(dev, "NVRAM length (%zd) exceeds data size (%zd)\n", len, + priv->data_len); return -EINVAL; } diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 2f971b059704..0556d140170a 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -6,6 +6,7 @@ * Copyright (C) 2013 Maxime Ripard */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -55,25 +57,34 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (nvmem->reg_read) - return nvmem->reg_read(nvmem->priv, offset, val, bytes); + struct nvmem_operations *ops = nvmem->ops; - return -EINVAL; + if (!ops->reg_read) + return -EOPNOTSUPP; + + return ops->reg_read(nvmem->priv, offset, val, bytes); } static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - int ret; + struct nvmem_operations *ops = nvmem->ops; + int ret, wr_ok; - if (nvmem->reg_write) { - gpiod_set_value_cansleep(nvmem->wp_gpio, 0); - ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); - gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (!ops->reg_write) + return -EOPNOTSUPP; + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + if (ret) return ret; - } - return -EINVAL; + wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes); + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (ret) + return ret; + + return wr_ok; } static int nvmem_access_with_keepouts(struct nvmem_device *nvmem, @@ -231,13 +242,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_read) - return -EPERM; - rc = nvmem_reg_read(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } @@ -264,19 +274,23 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_write || nvmem->read_only) + if (nvmem->read_only) return -EPERM; rc = nvmem_reg_write(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) { + struct nvmem_operations *ops = nvmem->ops; + umode_t mode = 0400; if (!nvmem->root_only) @@ -285,10 +299,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) if (!nvmem->read_only) mode |= 0200; - if (!nvmem->reg_write) + if (!ops->reg_write) mode &= ~0200; - if (!nvmem->reg_read) + if (!ops->reg_read) mode &= ~0444; return mode; @@ -319,6 +333,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); + struct nvmem_operations *ops = nvmem->ops; /* * If the device has no .reg_write operation, do not allow @@ -327,7 +342,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, * can be forced into read-write mode using the 'force_ro' * attribute. */ - if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write) + if (attr == &dev_attr_force_ro.attr && !ops->reg_write) return 0; /* Attribute not visible */ return attr->mode; @@ -445,10 +460,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, return 0; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { - if (config->compat) + if (nvmem->flags & FLAG_COMPAT) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); } @@ -461,27 +475,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) const struct bin_attribute **pattrs; struct bin_attribute *attrs; unsigned int ncells = 0, i = 0; - int ret = 0; + int ret; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) - goto unlock_mutex; + return 0; /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, sizeof(struct bin_attribute *), GFP_KERNEL); - if (!pattrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!pattrs) + return -ENOMEM; attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); - if (!attrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs) + return -ENOMEM; /* Initialize each attribute to take the name and size of the cell */ list_for_each_entry(entry, &nvmem->cells, node) { @@ -494,10 +504,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) attrs[i].size = entry->bytes; attrs[i].read = &nvmem_cell_attr_read; attrs[i].private = entry; - if (!attrs[i].attr.name) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs[i].attr.name) + return -ENOMEM; pattrs[i] = &attrs[i]; i++; @@ -507,13 +515,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ret = device_add_group(&nvmem->dev, &group); if (ret) - goto unlock_mutex; + return ret; nvmem->sysfs_cells_populated = true; -unlock_mutex: - mutex_unlock(&nvmem_mutex); - return ret; } @@ -524,8 +529,7 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, { return -ENOSYS; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { } @@ -537,6 +541,7 @@ static void nvmem_release(struct device *dev) ida_free(&nvmem_ida, nvmem->id); gpiod_put(nvmem->wp_gpio); + kfree(nvmem->ops); kfree(nvmem); } @@ -551,9 +556,8 @@ static const struct bus_type nvmem_bus_type = { static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); - mutex_lock(&nvmem_mutex); - list_del(&cell->node); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_del(&cell->node); of_node_put(cell->np); kfree_const(cell->name); kfree(cell); @@ -569,9 +573,8 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { - mutex_lock(&nvmem_mutex); - list_add_tail(&cell->node, &cell->nvmem->cells); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_add_tail(&cell->node, &cell->nvmem->cells); blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } @@ -721,14 +724,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id) { struct nvmem_cell_entry *iter, *cell = NULL; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); + list_for_each_entry(iter, &nvmem->cells, node) { if (strcmp(cell_id, iter->name) == 0) { cell = iter; break; } } - mutex_unlock(&nvmem_mutex); return cell; } @@ -883,6 +886,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister); struct nvmem_device *nvmem_register(const struct nvmem_config *config) { + struct nvmem_operations *ops; struct nvmem_device *nvmem; int rval; @@ -896,8 +900,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (!nvmem) return ERR_PTR(-ENOMEM); + ops = kzalloc_obj(*ops); + if (!ops) { + kfree(nvmem); + return ERR_PTR(-ENOMEM); + } + rval = ida_alloc(&nvmem_ida, GFP_KERNEL); if (rval < 0) { + kfree(ops); kfree(nvmem); return ERR_PTR(rval); } @@ -907,6 +918,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.type = &nvmem_provider_type; nvmem->dev.bus = &nvmem_bus_type; nvmem->dev.parent = config->dev; + nvmem->ops = ops; device_initialize(&nvmem->dev); @@ -923,6 +935,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) INIT_LIST_HEAD(&nvmem->cells); nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + ops->reg_read = config->reg_read; + ops->reg_write = config->reg_write; + nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) nvmem->owner = config->dev->driver->owner; @@ -932,8 +947,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->root_only = config->root_only; nvmem->priv = config->priv; nvmem->type = config->type; - nvmem->reg_read = config->reg_read; - nvmem->reg_write = config->reg_write; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -959,7 +972,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; nvmem->read_only = device_property_present(config->dev, "read-only") || - config->read_only || !nvmem->reg_write; + config->read_only || !ops->reg_write; #ifdef CONFIG_NVMEM_SYSFS nvmem->dev.groups = nvmem_dev_groups; @@ -999,6 +1012,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_dev; + /* If the device has WP GPIO, default to read-only */ + if (nvmem->wp_gpio) + nvmem->read_only = true; + #ifdef CONFIG_NVMEM_SYSFS rval = nvmem_populate_sysfs_cells(nvmem); if (rval) @@ -1017,8 +1034,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) device_del(&nvmem->dev); err_remove_cells: nvmem_device_remove_all_cells(nvmem); - if (config->compat) - nvmem_sysfs_remove_compat(nvmem, config); + nvmem_sysfs_remove_compat(nvmem); err_put_device: put_device(&nvmem->dev); @@ -1034,8 +1050,7 @@ static void nvmem_device_release(struct kref *kref) blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); - if (nvmem->flags & FLAG_COMPAT) - device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + nvmem_sysfs_remove_compat(nvmem); nvmem_device_remove_all_cells(nvmem); nvmem_destroy_layout(nvmem); @@ -1088,17 +1103,17 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_nvmem_register); -static struct nvmem_device *__nvmem_device_get(void *data, +static struct nvmem_device *nvmem_device_match(void *data, int (*match)(struct device *dev, const void *data)) { struct nvmem_device *nvmem = NULL; struct device *dev; - mutex_lock(&nvmem_mutex); - dev = bus_find_device(&nvmem_bus_type, NULL, data, match); - if (dev) - nvmem = to_nvmem_device(dev); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) { + dev = bus_find_device(&nvmem_bus_type, NULL, data, match); + if (dev) + nvmem = to_nvmem_device(dev); + } if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -1116,13 +1131,6 @@ static struct nvmem_device *__nvmem_device_get(void *data, return nvmem; } -static void __nvmem_device_put(struct nvmem_device *nvmem) -{ - put_device(&nvmem->dev); - module_put(nvmem->owner); - kref_put(&nvmem->refcnt, nvmem_device_release); -} - #if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id @@ -1147,7 +1155,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) if (!nvmem_np) return ERR_PTR(-ENOENT); - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); return nvmem; } @@ -1175,7 +1183,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name) } - return __nvmem_device_get((void *)dev_name, device_match_name); + return nvmem_device_match((void *)dev_name, device_match_name); } EXPORT_SYMBOL_GPL(nvmem_device_get); @@ -1191,7 +1199,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get); struct nvmem_device *nvmem_device_find(void *data, int (*match)(struct device *dev, const void *data)) { - return __nvmem_device_get(data, match); + return nvmem_device_match(data, match); } EXPORT_SYMBOL_GPL(nvmem_device_find); @@ -1235,7 +1243,9 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put); */ void nvmem_device_put(struct nvmem_device *nvmem) { - __nvmem_device_put(nvmem); + put_device(&nvmem->dev); + module_put(nvmem->owner); + kref_put(&nvmem->refcnt, nvmem_device_release); } EXPORT_SYMBOL_GPL(nvmem_device_put); @@ -1308,35 +1318,32 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) dev_id = dev_name(dev); - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); list_for_each_entry(lookup, &nvmem_lookup_list, node) { if ((strcmp(lookup->dev_id, dev_id) == 0) && (strcmp(lookup->con_id, con_id) == 0)) { /* This is the right entry. */ - nvmem = __nvmem_device_get((void *)lookup->nvmem_name, + nvmem = nvmem_device_match((void *)lookup->nvmem_name, device_match_name); - if (IS_ERR(nvmem)) { + if (IS_ERR(nvmem)) /* Provider may not be registered yet. */ - cell = ERR_CAST(nvmem); - break; - } + return ERR_CAST(nvmem); cell_entry = nvmem_find_cell_entry_by_name(nvmem, lookup->cell_name); if (!cell_entry) { - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); cell = ERR_PTR(-ENOENT); } else { cell = nvmem_create_cell(cell_entry, con_id, 0); if (IS_ERR(cell)) - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } break; } } - mutex_unlock(&nvmem_lookup_mutex); return cell; } @@ -1350,18 +1357,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem) static struct nvmem_cell_entry * nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_cell_entry *iter, *cell = NULL; + struct nvmem_cell_entry *cell; - mutex_lock(&nvmem_mutex); - list_for_each_entry(iter, &nvmem->cells, node) { - if (np == iter->np) { - cell = iter; - break; - } + guard(mutex)(&nvmem_mutex); + + list_for_each_entry(cell, &nvmem->cells, node) { + if (np == cell->np) + return cell; } - mutex_unlock(&nvmem_mutex); - return cell; + return NULL; } static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) @@ -1431,7 +1436,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) } } - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); if (IS_ERR(nvmem)) { of_node_put(cell_np); @@ -1441,7 +1446,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) ret = nvmem_layout_module_get_optional(nvmem); if (ret) { of_node_put(cell_np); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } @@ -1450,14 +1455,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (!cell_entry) { nvmem_layout_module_put(nvmem); ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT; - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } cell = nvmem_create_cell(cell_entry, id, cell_index); if (IS_ERR(cell)) { nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } return cell; @@ -1572,7 +1577,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree(cell); nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); @@ -2094,10 +2099,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_add_tail(&entries[i].node, &nvmem_lookup_list); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups); @@ -2112,10 +2117,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_del(&entries[i].node); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups); diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/nvmem/ee1004.c similarity index 100% rename from drivers/misc/eeprom/ee1004.c rename to drivers/nvmem/ee1004.c diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c similarity index 100% rename from drivers/misc/eeprom/eeprom_93xx46.c rename to drivers/nvmem/eeprom_93xx46.c diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index f6d452b6a28f..4e610deeaa7b 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -7,10 +7,15 @@ #include #include +/* Hold pointers to callbacks owned by the nvmem provider module. */ +struct nvmem_operations { + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; +}; + struct nvmem_device { struct module *owner; struct device dev; - struct list_head node; int stride; int word_size; int id; @@ -27,10 +32,9 @@ struct nvmem_device { struct nvmem_cell_info *cell); const struct nvmem_keepout *keepout; unsigned int nkeepout; - nvmem_reg_read_t reg_read; - nvmem_reg_write_t reg_write; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; + struct nvmem_operations *ops; void *priv; bool sysfs_cells_populated; }; diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c index 56fc19f092a7..62d1d6381bf8 100644 --- a/drivers/nvmem/lan9662-otpc.c +++ b/drivers/nvmem/lan9662-otpc.c @@ -27,7 +27,6 @@ #define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) #define OTP_OTP_STATUS_OTP_BUSY BIT(0) -#define OTP_MEM_SIZE 8192 #define OTP_SLEEP_US 10 #define OTP_TIMEOUT_US 500000 @@ -176,7 +175,6 @@ static struct nvmem_config otp_config = { .word_size = 1, .reg_read = lan9662_otp_read, .reg_write = lan9662_otp_write, - .size = OTP_MEM_SIZE, }; static int lan9662_otp_probe(struct platform_device *pdev) @@ -196,6 +194,7 @@ static int lan9662_otp_probe(struct platform_device *pdev) otp_config.priv = otp; otp_config.dev = dev; + otp_config.size = (uintptr_t) device_get_match_data(dev); nvmem = devm_nvmem_register(dev, &otp_config); @@ -203,7 +202,14 @@ static int lan9662_otp_probe(struct platform_device *pdev) } static const struct of_device_id lan9662_otp_match[] = { - { .compatible = "microchip,lan9662-otpc", }, + { + .compatible = "microchip,lan9662-otpc", + .data = (const void *) SZ_8K, + }, + { + .compatible = "microchip,lan9691-otpc", + .data = (const void *) SZ_16K, + }, { }, }; MODULE_DEVICE_TABLE(of, lan9662_otp_match); diff --git a/drivers/nvmem/layouts/u-boot-env.c b/drivers/nvmem/layouts/u-boot-env.c index f27f387bb52a..33ec2350386f 100644 --- a/drivers/nvmem/layouts/u-boot-env.c +++ b/drivers/nvmem/layouts/u-boot-env.c @@ -38,9 +38,6 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i { u8 mac[ETH_ALEN]; - if (bytes != MAC_ADDR_STR_LEN) - return -EINVAL; - if (!mac_pton(buf, mac)) return -EINVAL; @@ -75,7 +72,7 @@ static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem 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")) { + if (!strcmp(var, "ethaddr") && info.bytes == MAC_ADDR_STR_LEN) { info.raw_len = strlen(value); info.bytes = ETH_ALEN; info.read_post_process = u_boot_env_read_post_process_ethaddr; diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/nvmem/m24lr.c similarity index 100% rename from drivers/misc/eeprom/m24lr.c rename to drivers/nvmem/m24lr.c diff --git a/drivers/nvmem/nintendo-otp.c b/drivers/nvmem/nintendo-otp.c index 4440d4e5fb83..a4d986e4588e 100644 --- a/drivers/nvmem/nintendo-otp.c +++ b/drivers/nvmem/nintendo-otp.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #define HW_OTPCMD 0 @@ -73,8 +73,7 @@ MODULE_DEVICE_TABLE(of, nintendo_otp_of_table); static int nintendo_otp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id = - of_match_device(nintendo_otp_of_table, dev); + const struct nintendo_otp_devtype_data *data; struct nvmem_device *nvmem; struct nintendo_otp_priv *priv; @@ -94,8 +93,8 @@ static int nintendo_otp_probe(struct platform_device *pdev) if (IS_ERR(priv->regs)) return PTR_ERR(priv->regs); - if (of_id->data) { - const struct nintendo_otp_devtype_data *data = of_id->data; + data = of_device_get_match_data(dev); + if (data) { config.name = data->name; config.size = data->num_banks * BANK_SIZE; } diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 0ec78b5e19e7..2c0feb036f3f 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -78,9 +78,9 @@ struct rockchip_data { struct rockchip_otp { struct device *dev; void __iomem *base; - struct clk_bulk_data *clks; struct reset_control *rst; const struct rockchip_data *data; + struct clk_bulk_data clks[]; }; static int rockchip_otp_reset(struct rockchip_otp *otp) @@ -424,7 +424,7 @@ static int rockchip_otp_probe(struct platform_device *pdev) if (!data) return dev_err_probe(dev, -EINVAL, "failed to get match data\n"); - otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), + otp = devm_kzalloc(&pdev->dev, struct_size(otp, clks, data->num_clks), GFP_KERNEL); if (!otp) return -ENOMEM; @@ -436,11 +436,6 @@ static int rockchip_otp_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(otp->base), "failed to ioremap resource\n"); - otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks), - GFP_KERNEL); - if (!otp->clks) - return -ENOMEM; - for (i = 0; i < data->num_clks; ++i) otp->clks[i].id = data->clks[i]; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index c75abdd8ef25..bccfe75fe6e5 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2745,116 +2745,128 @@ static struct parport_pc_pci { static const struct pci_device_id parport_pc_pci_tbl[] = { /* Super-IO onboard chips */ - { 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a }, - { 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 }, - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 }, + { PCI_DEVICE(0x1106, 0x0686), + .driver_data = sio_via_686a }, + { PCI_DEVICE(0x1106, 0x8231), + .driver_data = sio_via_8231 }, + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872), + .driver_data = sio_ite_8872 }, /* PCI cards */ - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_10x), + .driver_data = siig_1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_10x), + .driver_data = siig_2p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_20x), + .driver_data = siig_1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_20x), + .driver_data = siig_2p_20x }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PARALLEL), + .driver_data = lava_parallel }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A), + .driver_data = lava_parallel_dual_a }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B), + .driver_data = lava_parallel_dual_b }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR), + .driver_data = boca_ioppar }, + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014), + .driver_data = plx_9050 }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a }, - { 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 }, - { 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a }, - { 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 }, - { 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0101), + .driver_data = timedia_4006a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0102), + .driver_data = timedia_4014 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0103), + .driver_data = timedia_4008a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0104), + .driver_data = timedia_4018 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x9018), + .driver_data = timedia_9018a }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_2P_EPP), + .driver_data = syba_2p_epp }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_1P_ECP), + .driver_data = syba_1p_ecp }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_010L), + .driver_data = titan_010l }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ /* AFAVLAB_TK9902 */ - { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p}, - { 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p}, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 }, - { 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp }, + { PCI_DEVICE(0x14db, 0x2120), + .driver_data = avlab_1p }, + { PCI_DEVICE(0x14db, 0x2121), + .driver_data = avlab_2p }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP), + .driver_data = oxsemi_952 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP), + .driver_data = oxsemi_954 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840), + .driver_data = oxsemi_840 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(AKS, PCI_DEVICE_ID_AKS_ALADDINCARD), + .driver_data = aks_0100 }, + { PCI_DEVICE(0x14f2, 0x0121), + .driver_data = mobility_pp }, /* NetMos communication controllers */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x2000, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x2000, 0, 0, netmos_9901 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, 0, 0, netmos_9865 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x2000, 0, 0, netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x2000), + .driver_data = netmos_9900 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9705), + .driver_data = netmos_9705 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9715), + .driver_data = netmos_9715 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9755), + .driver_data = netmos_9755 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9805), + .driver_data = netmos_9805 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9815), + .driver_data = netmos_9815 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x2000), + .driver_data = netmos_9901 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x1000), + .driver_data = netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x2000), + .driver_data = netmos_9865 }, /* ASIX AX99100 PCIe to Multi I/O Controller */ - { PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100, - 0xA000, 0x2000, 0, 0, asix_ax99100 }, + { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, 0xA000, 0x2000), + .driver_data = asix_ax99100 }, /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, + { PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100), + .driver_data = quatech_sppxp100 }, /* WCH CH382L PCI-E single parallel port card */ - { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l }, + { PCI_DEVICE_SUB(0x1c00, 0x3050, 0x1c00, 0x3050), + .driver_data = wch_ch382l }, /* Brainboxes IX-500/550 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x402a), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes UC-146/UC-157 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0be1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0be2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be1), + .driver_data = brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be2), + .driver_data = brainboxes_uc146 }, /* Brainboxes PX-146/PX-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x401c), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes PX-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4007, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px203 }, + { PCI_VDEVICE(INTASHIELD, 0x4007), + .driver_data = brainboxes_px203 }, /* Brainboxes PX-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401f, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { 0, } /* terminate list */ + { PCI_VDEVICE(INTASHIELD, 0x401f), + .driver_data = oxsemi_pcie_pport }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 24d4f3a3ec3d..4d4140c8584d 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -170,158 +170,176 @@ static struct parport_pc_pci cards[] = { static struct pci_device_id parport_serial_pci_tbl[] = { /* PCI cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0020, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0022, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3011, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3012, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3020, 0, 0, netmos_9900_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x2000, 0, 0, netmos_99xx_1p }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_110L), + .driver_data = titan_110l }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_210L), + .driver_data = titan_210l }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9735), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9745), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9835), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9845), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0020), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0022), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9855), + .driver_data = netmos_9855 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3011), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3012), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3020), + .driver_data = netmos_9900_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, 0xA000, 0x2000), + .driver_data = netmos_99xx_1p }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ - { PCI_VENDOR_ID_AFAVLAB, 0x2110, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2111, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2112, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2140, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2141, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2142, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2160, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2161, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2162, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VDEVICE(AFAVLAB, 0x2110), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2111), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2112), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2140), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2141), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2142), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2160), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2161), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2162), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850), + .driver_data = siig_2s1p_20x }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a }, - { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h }, - { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h }, - { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a }, - { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a }, - { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a }, - { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a }, - { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u }, - { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a }, - { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u }, - { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r }, - { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s }, - { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d }, - { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e }, - { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f }, - { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, - { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, - { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4078), + .driver_data = timedia_4078a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4079), + .driver_data = timedia_4079h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4085), + .driver_data = timedia_4085h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4088), + .driver_data = timedia_4088a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4089), + .driver_data = timedia_4089a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4095), + .driver_data = timedia_4095a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4096), + .driver_data = timedia_4096a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5078), + .driver_data = timedia_4078u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5079), + .driver_data = timedia_4079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5085), + .driver_data = timedia_4085u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x6079), + .driver_data = timedia_4079r }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x7079), + .driver_data = timedia_4079s }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x8079), + .driver_data = timedia_4079d }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x9079), + .driver_data = timedia_4079e }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xa079), + .driver_data = timedia_4079f }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xb079), + .driver_data = timedia_9079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xc079), + .driver_data = timedia_9079b }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xd079), + .driver_data = timedia_9079c }, /* WCH CARDS */ - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p }, - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, - 0x4348, 0x3253, 0, 0, wch_ch353_2s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, - 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, - 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p }, + { PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P), + .driver_data = wch_ch353_1s1p }, + { PCI_VDEVICE_SUB(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, 0x4348, 0x3253), + .driver_data = wch_ch353_2s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, 0x1c00, 0x3050), + .driver_data = wch_ch382_0s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, 0x1c00, 0x3250), + .driver_data = wch_ch382_2s1p }, /* BrainBoxes PX272/PX306 MIO card */ - { PCI_VENDOR_ID_INTASHIELD, 0x4100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p }, + { PCI_VDEVICE(INTASHIELD, 0x4100), + .driver_data = brainboxes_5s1p }, /* Sunix boards */ - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0100, 0, 0, sunix_4008a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0101, 0, 0, sunix_5069a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0102, 0, 0, sunix_5079a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0104, 0, 0, sunix_5099a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0100), + .driver_data = sunix_4008a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0101), + .driver_data = sunix_5069a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0102), + .driver_data = sunix_5079a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0104), + .driver_data = sunix_5099a }, /* Brainboxes UC-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0bc1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0bc2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc1), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc2), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0861, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0862, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0863, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0861), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0862), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0863), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-414 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0e61, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc414 }, + { PCI_VDEVICE(INTASHIELD, 0x0e61), + .driver_data = brainboxes_uc414 }, /* Brainboxes UC-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0981, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0982, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0981), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0982), + .driver_data = brainboxes_uc257 }, /* Brainboxes IS-300/IS-500 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0da0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_is300 }, + { PCI_VDEVICE(INTASHIELD, 0x0da0), + .driver_data = brainboxes_is300 }, /* Brainboxes PX-263/PX-295 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px263 }, + { PCI_VDEVICE(INTASHIELD, 0x402c), + .driver_data = brainboxes_px263 }, - { 0, } /* terminate list */ + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index c3e3b58fb86c..78f1fb1a6b0c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -624,6 +624,15 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl, return ret; } +static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + + dmaengine_synchronize(epf_mhi->dma_chan_rx); + dmaengine_synchronize(epf_mhi->dma_chan_tx); + flush_workqueue(epf_mhi->dma_wq); +} + struct epf_dma_filter { struct device *dev; u32 dma_mask; @@ -792,6 +801,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) mhi_cntrl->write_sync = pci_epf_mhi_edma_write; mhi_cntrl->read_async = pci_epf_mhi_edma_read_async; mhi_cntrl->write_async = pci_epf_mhi_edma_write_async; + mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async; } /* Register the MHI EP controller */ diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 7cb020dd3423..9591672b0511 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -717,3 +717,4 @@ MODULE_ALIAS("platform:cpcap_usb"); MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP usb phy driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 032140bd6682..90a61d9db72c 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -826,3 +826,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-btemp"); MODULE_DESCRIPTION("AB8500 battery temperature driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index bc6c070a564d..ff38254d06ef 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3748,3 +3748,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-charger"); MODULE_DESCRIPTION("AB8500 charger management driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index cfcc05dfe28d..e6b5efdf5dc9 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3262,3 +3262,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:ab8500-fg"); MODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 474661ba7cf2..d9892b351621 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -418,3 +418,4 @@ module_platform_driver(axp20x_ac_power_driver); MODULE_AUTHOR("Quentin Schulz "); MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 50ca8e110085..ee8701a6e907 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -1155,3 +1155,4 @@ module_platform_driver(axp20x_batt_driver); MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); MODULE_AUTHOR("Quentin Schulz "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 29f105f9212d..e951b2b64b25 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -1077,3 +1077,4 @@ module_platform_driver(axp20x_usb_power_driver); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index aa04c54ff97e..68dabaf7a4f8 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -817,3 +817,4 @@ MODULE_AUTHOR("Ramakrishna Pallala "); MODULE_AUTHOR("Todd Brandt "); MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 1d2b8bb08564..808714c6ce95 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1202,3 +1202,4 @@ module_platform_driver(cpcap_battery_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP PMIC Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 76e2d2e3f5f5..34a57dd5c3cf 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -973,3 +973,4 @@ MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cpcap-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index 27f36ef5b88d..58449df6068c 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -644,3 +644,4 @@ module_platform_driver(da9150_charger_driver); MODULE_DESCRIPTION("Charger Driver for DA9150"); MODULE_AUTHOR("Adam Thomson "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index 6913179ebb84..185f55d2fd70 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -298,3 +298,4 @@ module_platform_driver(gab_driver); MODULE_AUTHOR("anish kumar "); MODULE_DESCRIPTION("generic battery driver using IIO"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index b111c7ce2be3..5be269f17bff 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -190,3 +190,4 @@ module_platform_driver(ingenic_battery_driver); MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs"); MODULE_AUTHOR("Artur Rojek "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c index 67a75281b0ac..cb6fa8d88b43 100644 --- a/drivers/power/supply/intel_dc_ti_battery.c +++ b/drivers/power/supply/intel_dc_ti_battery.c @@ -389,3 +389,4 @@ MODULE_ALIAS("platform:" DEV_NAME); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 582644f4e8b3..1e53f11a8e2b 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -230,3 +230,4 @@ module_platform_driver(lego_ev3_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Lechner "); MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 1bc2c150fb91..b8b74efc9671 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -727,3 +727,4 @@ MODULE_DESCRIPTION("TI LP8788 Charger Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lp8788-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 41744191a363..16e42de28436 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -655,3 +655,4 @@ module_i2c_driver(max17040_i2c_driver); MODULE_AUTHOR("Minkyu Kang "); MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c index 04294359ed7d..0aace0040259 100644 --- a/drivers/power/supply/mp2629_charger.c +++ b/drivers/power/supply/mp2629_charger.c @@ -657,3 +657,4 @@ module_platform_driver(mp2629_charger_driver); MODULE_AUTHOR("Saravanan Sekar "); MODULE_DESCRIPTION("MP2629 Charger driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index 9d388bc53098..29ce34fd3804 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -928,3 +928,4 @@ module_platform_driver(mt6370_chg_driver); MODULE_AUTHOR("ChiaEn Wu "); MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c index 7bda5cd77a75..67fdb4335338 100644 --- a/drivers/power/supply/qcom_smbx.c +++ b/drivers/power/supply/qcom_smbx.c @@ -1056,3 +1056,4 @@ module_platform_driver(qcom_spmi_smb); MODULE_AUTHOR("Casey Connolly "); MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 5963a55341ee..84b9d6248489 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -818,3 +818,4 @@ module_platform_driver(rn5t618_power_driver); MODULE_ALIAS("platform:rn5t618-power"); MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index b0220ec2d926..57266921dc8e 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -246,3 +246,4 @@ MODULE_ALIAS("platform:rx51-battery"); MODULE_AUTHOR("Pali Rohár "); MODULE_DESCRIPTION("Nokia RX-51 battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 9af0017fa88e..0999fc5bc662 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1346,3 +1346,4 @@ module_platform_driver(sc27xx_fgu_driver); MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index c46b22ab8d99..e6c9b2b1ffce 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1146,3 +1146,4 @@ MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_bci"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index a99b3ff26929..5e04b4f0a135 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -235,3 +235,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Märdian "); MODULE_DESCRIPTION("twl4030_madc battery driver"); MODULE_ALIAS("platform:twl4030_madc_battery"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c index 020512d0e306..e8ef221fa0f3 100644 --- a/drivers/power/supply/twl6030_charger.c +++ b/drivers/power/supply/twl6030_charger.c @@ -577,3 +577,4 @@ module_platform_driver(twl6030_charger_driver); MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 0e32a2295990..30644d2d7c54 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -426,9 +426,9 @@ static const struct of_device_id ad7816_of_match[] = { MODULE_DEVICE_TABLE(of, ad7816_of_match); static const struct spi_device_id ad7816_id[] = { - { "ad7816", ID_AD7816 }, - { "ad7817", ID_AD7817 }, - { "ad7818", ID_AD7818 }, + { .name = "ad7816", .driver_data = ID_AD7816 }, + { .name = "ad7817", .driver_data = ID_AD7817 }, + { .name = "ad7818", .driver_data = ID_AD7818 }, { } }; diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c index 1debcc36c1af..459dd7416b1d 100644 --- a/drivers/staging/iio/addac/adt7316-spi.c +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -126,15 +126,14 @@ static int adt7316_spi_probe(struct spi_device *spi_dev) } static const struct spi_device_id adt7316_spi_id[] = { - { "adt7316", 0 }, - { "adt7317", 0 }, - { "adt7318", 0 }, - { "adt7516", 0 }, - { "adt7517", 0 }, - { "adt7519", 0 }, + { .name = "adt7316" }, + { .name = "adt7317" }, + { .name = "adt7318" }, + { .name = "adt7516" }, + { .name = "adt7517" }, + { .name = "adt7519" }, { } }; - MODULE_DEVICE_TABLE(spi, adt7316_spi_id); static const struct of_device_id adt7316_of_spi_match[] = { diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 59fb3bd26bc1..20f6052cc7fb 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1814,11 +1814,8 @@ static int adt7316_setup_irq(struct iio_dev *indio_dev) NULL, adt7316_event_handler, irq_type | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&indio_dev->dev, "failed to request irq %d\n", - chip->bus.irq); + if (ret) return ret; - } if (irq_type & IRQF_TRIGGER_HIGH) chip->config1 |= ADT7316_INT_POLARITY; diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 60c33e10c46f..a78249812b26 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -252,22 +252,22 @@ static ssize_t ad9832_write(struct device *dev, struct device_attribute *attr, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9832_write, AD9832_FREQ_SYM); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H); -static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H); -static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0200, 0, 2, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0200, 0, 3, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9832_write, AD9832_PHASE_SYM); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9832_write, AD9832_PINCTRL_EN); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9832_write, AD9832_OUTPUT_EN); static struct attribute *ad9832_attributes[] = { @@ -380,8 +380,8 @@ static const struct of_device_id ad9832_of_match[] = { MODULE_DEVICE_TABLE(of, ad9832_of_match); static const struct spi_device_id ad9832_id[] = { - {"ad9832", 0}, - {"ad9835", 0}, + { .name = "ad9832" }, + { .name = "ad9835" }, { } }; MODULE_DEVICE_TABLE(spi, ad9832_id); diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 33dfd723923c..0d20f796e0f0 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -314,21 +314,21 @@ static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9834_write, AD9834_REG_FREQ0); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9834_write, AD9834_REG_FREQ1); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9834_write, AD9834_FSEL); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9834_write, AD9834_FSEL); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9834_write, AD9834_REG_PHASE0); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9834_write, AD9834_REG_PHASE1); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9834_write, AD9834_PSEL); +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9834_write, AD9834_PSEL); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9834_write, AD9834_PIN_SW); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9834_write, AD9834_RESET); -static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, 0200, NULL, ad9834_write, AD9834_OPBITEN); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0200, 0, 1, NULL, ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 1, ad9834_store_wavetype, 1); static struct attribute *ad9834_attributes[] = { &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, @@ -464,10 +464,10 @@ static int ad9834_probe(struct spi_device *spi) } static const struct spi_device_id ad9834_id[] = { - {"ad9833", ID_AD9833}, - {"ad9834", ID_AD9834}, - {"ad9837", ID_AD9837}, - {"ad9838", ID_AD9838}, + { .name = "ad9833", .driver_data = ID_AD9833 }, + { .name = "ad9834", .driver_data = ID_AD9834 }, + { .name = "ad9837", .driver_data = ID_AD9837 }, + { .name = "ad9838", .driver_data = ID_AD9838 }, { } }; MODULE_DEVICE_TABLE(spi, ad9834_id); diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h index 2ebe68eb7398..d5f0139b79f0 100644 --- a/drivers/staging/iio/frequency/dds.h +++ b/drivers/staging/iio/frequency/dds.h @@ -7,102 +7,102 @@ #ifndef IIO_DDS_H_ #define IIO_DDS_H_ -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY */ -#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQ(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale */ #define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol */ -#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQSYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY */ -#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASE(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale */ #define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol */ -#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASESYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en */ -#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en */ -#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en */ -#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_out_enable */ -#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_ENABLE(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable */ -#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ - _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUTY_ENABLE(_mode, _channel, _output, \ + _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype */ -#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_WAVETYPE(_mode, _channel, _output, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ - 0200, NULL, _store, _addr) + _mode, NULL, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available */ diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index d7f2e6ca92c2..bb6222c8cc5f 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -1069,3 +1069,4 @@ module_platform_driver(adc_tm5_driver); MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index f39ca0ddd17b..fb003ca96454 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -904,3 +904,4 @@ module_platform_driver(qpnp_tm_driver); MODULE_ALIAS("platform:spmi-temp-alarm"); MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c index e25e36c99a88..7ced8f76a0ec 100644 --- a/drivers/thermal/renesas/rzg3s_thermal.c +++ b/drivers/thermal/renesas/rzg3s_thermal.c @@ -270,3 +270,4 @@ module_platform_driver(rzg3s_thermal_driver); MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver"); MODULE_AUTHOR("Claudiu Beznea "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 7c844589b153..cfdb8e674dd2 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -228,3 +228,4 @@ module_platform_driver(gadc_thermal_driver); MODULE_AUTHOR("Laxman Dewangan "); MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 1e4ade78ed84..f8fa20522660 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -150,7 +150,7 @@ static ssize_t portio_size_show(struct uio_port *port, char *buf) static ssize_t portio_porttype_show(struct uio_port *port, char *buf) { - const char *porttypes[] = {"none", "x86", "gpio", "other"}; + static const char * const porttypes[] = {"none", "x86", "gpio", "other"}; if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) return -EINVAL; @@ -1057,6 +1057,11 @@ int __uio_register_device(struct module *owner, err_request_irq: uio_dev_del_attributes(idev); err_uio_dev_add_attributes: + mutex_lock(&idev->info_lock); + idev->info = NULL; + mutex_unlock(&idev->info_lock); + wake_up_interruptible(&idev->wait); + kill_fasync(&idev->async_queue, SIGIO, POLL_HUP); device_del(&idev->dev); err_device_create: uio_free_minor(idev->minor); diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index 338dd2aaabc8..08cb150fb12f 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c @@ -45,8 +45,7 @@ #include #include #include -#include -#include +#include #include @@ -249,19 +248,12 @@ static int check_of_data(struct fsl_elbc_gpcm *priv, static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, struct resource *res, u32 *reg_br, - u32 *reg_or, unsigned int *irq, char **name) + u32 *reg_or, char **name) { const char *dt_name; const char *type; int ret; - /* get the memory resource */ - ret = of_address_to_resource(node, 0, res); - if (ret) { - dev_err(priv->dev, "failed to get resource\n"); - return ret; - } - /* get the bank number */ ret = of_property_read_u32(node, "reg", &priv->bank); if (ret) { @@ -288,9 +280,6 @@ static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, if (of_property_read_string(node, "device_type", &type) == 0) setup_periph(priv, type); - /* get optional irq value */ - *irq = irq_of_parse_and_map(node, 0); - /* sanity check device tree data */ ret = check_of_data(priv, res, *reg_br, *reg_or); if (ret) @@ -312,7 +301,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) struct fsl_elbc_gpcm *priv; struct uio_info *info; char *uio_name = NULL; - struct resource res; + struct resource *res; + void __iomem *base; unsigned int irq; u32 reg_br_cur; u32 reg_or_cur; @@ -323,6 +313,16 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; + /* map the memory resource */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* get optional irq value */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + /* allocate private data */ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -331,8 +331,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) priv->lbc = fsl_lbc_ctrl_dev->regs; /* get device tree data */ - ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new, - &irq, &uio_name); + ret = get_of_data(priv, node, res, ®_br_new, ®_or_new, + &uio_name); if (ret) return ret; @@ -349,7 +349,7 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if ((reg_br_cur & BR_V)) { if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || (reg_br_cur & reg_or_cur & BR_BA) - != fsl_lbc_addr(res.start)) { + != fsl_lbc_addr(res->start)) { dev_err(priv->dev, "bank in use by another peripheral\n"); return -ENODEV; @@ -371,26 +371,18 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) /* configure the bank (force base address and GPCM) */ reg_br_new &= ~(BR_BA | BR_MSEL); - reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; + reg_br_new |= fsl_lbc_addr(res->start) | BR_MS_GPCM | BR_V; out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); - /* map the memory resource */ - info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); - if (!info->mem[0].internal_addr) { - dev_err(priv->dev, "failed to map chip region\n"); - return -ENODEV; - } - /* set all UIO data */ info->mem[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", node); - if (!info->mem[0].name) { - ret = -ENODEV; - goto out_err3; - } + if (!info->mem[0].name) + return -ENODEV; - info->mem[0].addr = res.start; - info->mem[0].size = resource_size(&res); + info->mem[0].internal_addr = base; + info->mem[0].addr = res->start; + info->mem[0].size = resource_size(res); info->mem[0].memtype = UIO_MEM_PHYS; info->priv = priv; info->name = uio_name; @@ -421,16 +413,13 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) dev_info(priv->dev, "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", - priv->name, (unsigned long long)res.start, priv->bank, + priv->name, (unsigned long long)res->start, priv->bank, irq ? : -1); return 0; out_err2: if (priv->shutdown) priv->shutdown(info, true); - -out_err3: - iounmap(info->mem[0].internal_addr); return ret; } @@ -443,7 +432,6 @@ static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev) uio_unregister_device(info); if (priv->shutdown) priv->shutdown(info, false); - iounmap(info->mem[0].internal_addr); } diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 12afc2fa1a0b..80087897f9b8 100644 --- a/drivers/uio/uio_sercos3.c +++ b/drivers/uio/uio_sercos3.c @@ -212,6 +212,7 @@ static const struct pci_device_id sercos3_pci_ids[] = { }, { 0, } }; +MODULE_DEVICE_TABLE(pci, sercos3_pci_ids); static struct pci_driver sercos3_pci_driver = { .name = "sercos3", diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 0069e6f854d7..cbd11bc61274 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -310,6 +310,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) mutex_unlock(&pdev->access_lock); + /* On bus error, decode to 3 (no device responded) to abort the search */ + if (status < 0) + status = 3 << 5; + /* Decode the status */ return (status >> 5); } @@ -545,9 +549,17 @@ static const struct i2c_device_id ds2482_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds2482_id); +static const struct of_device_id ds2482_of_match[] = { + { .compatible = "maxim,ds2482", }, + { .compatible = "maxim,ds2484", }, + { } +}; +MODULE_DEVICE_TABLE(of, ds2482_of_match); + static struct i2c_driver ds2482_driver = { .driver = { .name = "ds2482", + .of_match_table = ds2482_of_match, }, .probe = ds2482_probe, .remove = ds2482_remove, diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index e53bc41bde3c..b638963d4b59 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i+1].flags & I2C_M_RECV_LEN) { + if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i+1].addr, &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); if (result < 0) { @@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i].flags & I2C_M_RECV_LEN) { + if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i].addr, &(msgs[i].buf[1]), diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 486f321eadc8..c16946642789 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -403,6 +403,11 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, const char *error_msg = "bad slave string format, expecting " "ff-dddddddddddd\n"; + if (count < 3) { + dev_err(dev, "%s", error_msg); + return -EINVAL; + } + if (buf[2] != '-') { dev_err(dev, "%s", error_msg); return -EINVAL; diff --git a/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h new file mode 100644 index 000000000000..6ee9a9ecffc1 --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H +#define _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H + +#define MT6323_AUXADC_BATON2 0 +#define MT6323_AUXADC_CH6 1 +#define MT6323_AUXADC_BAT_TEMP 2 +#define MT6323_AUXADC_CHIP_TEMP 3 +#define MT6323_AUXADC_VCDT 4 +#define MT6323_AUXADC_BATON1 5 +#define MT6323_AUXADC_ISENSE 6 +#define MT6323_AUXADC_BATSNS 7 +#define MT6323_AUXADC_ACCDET 8 +#define MT6323_AUXADC_AUDIO0 9 +#define MT6323_AUXADC_AUDIO1 10 +#define MT6323_AUXADC_AUDIO2 11 +#define MT6323_AUXADC_AUDIO3 12 +#define MT6323_AUXADC_AUDIO4 13 +#define MT6323_AUXADC_AUDIO5 14 +#define MT6323_AUXADC_AUDIO6 15 +#define MT6323_AUXADC_AUDIO7 16 + +#endif diff --git a/include/dt-bindings/interconnect/qcom,maili-rpmh.h b/include/dt-bindings/interconnect/qcom,maili-rpmh.h new file mode 100644 index 000000000000..ae3e48b14eab --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,maili-rpmh.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_2 1 +#define MASTER_QUP_3 2 +#define MASTER_QUP_4 3 +#define MASTER_QUP_5 4 +#define MASTER_CRYPTO 5 +#define MASTER_IPA 6 +#define MASTER_QUP_1 7 +#define MASTER_SOCCP_PROC 8 +#define MASTER_QDSS_ETR 9 +#define MASTER_QDSS_ETR_1 10 +#define MASTER_SDCC_2 11 +#define MASTER_SDCC_4 12 +#define MASTER_UFS_MEM 13 +#define MASTER_USB3 14 +#define SLAVE_A1NOC_SNOC 15 + +#define MASTER_DDR_EFF_VETO 0 +#define MASTER_QUP_CORE_0 1 +#define MASTER_QUP_CORE_1 2 +#define MASTER_QUP_CORE_2 3 +#define MASTER_QUP_CORE_3 4 +#define MASTER_QUP_CORE_4 5 +#define MASTER_QUP_CORE_5 6 +#define SLAVE_DDR_EFF_VETO 7 +#define SLAVE_QUP_CORE_0 8 +#define SLAVE_QUP_CORE_1 9 +#define SLAVE_QUP_CORE_2 10 +#define SLAVE_QUP_CORE_3 11 +#define SLAVE_QUP_CORE_4 12 +#define SLAVE_QUP_CORE_5 13 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_IPA_CFG 3 +#define SLAVE_IPC_ROUTER_FENCE 4 +#define SLAVE_SOCCP 5 +#define SLAVE_TME_CFG 6 +#define SLAVE_CNOC_CFG 7 +#define SLAVE_DDRSS_CFG 8 +#define SLAVE_IMEM 9 +#define SLAVE_PCIE_0 10 +#define SLAVE_PCIE_1 11 + +#define MASTER_GIC 0 +#define MASTER_GPU_TCU 1 +#define MASTER_SYS_TCU 2 +#define MASTER_APPSS_PROC 3 +#define MASTER_GFX3D 4 +#define MASTER_LPASS_GEM_NOC 5 +#define MASTER_MSS_PROC 6 +#define MASTER_MNOC_HF_MEM_NOC 7 +#define MASTER_MNOC_SF_MEM_NOC 8 +#define MASTER_COMPUTE_NOC 9 +#define MASTER_ANOC_PCIE_GEM_NOC 10 +#define MASTER_QPACE 11 +#define MASTER_SNOC_SF_MEM_NOC 12 +#define MASTER_WLAN_Q6 13 +#define SLAVE_GEM_NOC_CNOC 14 +#define SLAVE_LLCC 15 +#define SLAVE_MEM_NOC_PCIE_SNOC 16 + +#define MASTER_LPIAON_NOC_LLCLPI_NOC 0 +#define SLAVE_LPASS_LPI_CC 1 +#define SLAVE_LLCC_ISLAND 2 +#define SLAVE_SERVICE_LLCLPI_NOC 3 +#define SLAVE_SERVICE_LLCLPI_NOC_CHIPCX 4 + +#define MASTER_LPIAON_NOC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LPASS_LPINOC 0 +#define SLAVE_LPIAON_NOC_LLCLPI_NOC 1 +#define SLAVE_LPIAON_NOC_LPASS_AG_NOC 2 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPICX_NOC_LPIAON_NOC 1 + +#define MASTER_LLCC 0 +#define MASTER_DDR_RT 1 +#define SLAVE_EBI1 2 +#define SLAVE_DDR_RT 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_NRT_ICP_SF 1 +#define MASTER_CAMNOC_RT_CDM_SF 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP 4 +#define MASTER_MDSS_DCP 5 +#define MASTER_CDSP_HCP 6 +#define MASTER_VIDEO_CV_PROC 7 +#define MASTER_VIDEO_EVA 8 +#define MASTER_VIDEO_MVP 9 +#define MASTER_VIDEO_V_PROC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_MNOC_SF_MEM_NOC 12 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_CFG_CENTER 0 +#define MASTER_CFG_EAST 1 +#define MASTER_CFG_MM_HF 2 +#define MASTER_CFG_MM_SF 3 +#define MASTER_CFG_NORTH 4 +#define MASTER_CFG_SOUTH 5 +#define MASTER_CFG_WEST 6 +#define SLAVE_AHB2PHY_SOUTH 7 +#define SLAVE_BOOT_ROM 8 +#define SLAVE_CAMERA_CFG 9 +#define SLAVE_CLK_CTL 10 +#define SLAVE_CRYPTO_CFG 11 +#define SLAVE_DISPLAY_CFG 12 +#define SLAVE_EVA_CFG 13 +#define SLAVE_GFX3D_CFG 14 +#define SLAVE_I2C 15 +#define SLAVE_IMEM_CFG 16 +#define SLAVE_IPC_ROUTER_CFG 17 +#define SLAVE_IRIS_CFG 18 +#define SLAVE_CNOC_MSS 19 +#define SLAVE_PCIE_0_CFG 20 +#define SLAVE_PCIE_1_CFG 21 +#define SLAVE_PRNG 22 +#define SLAVE_QSPI_0 23 +#define SLAVE_QUP_1 24 +#define SLAVE_QUP_2 25 +#define SLAVE_QUP_3 26 +#define SLAVE_QUP_4 27 +#define SLAVE_QUP_5 28 +#define SLAVE_SDCC_2 29 +#define SLAVE_SDCC_4 30 +#define SLAVE_TLMM 31 +#define SLAVE_UFS_MEM_CFG 32 +#define SLAVE_USB3 33 +#define SLAVE_VSENSE_CTRL_CFG 34 +#define SLAVE_PCIE_ANOC_CFG 35 +#define SLAVE_QDSS_CFG 36 +#define SLAVE_QDSS_STM 37 +#define SLAVE_TCSR 38 +#define SLAVE_TCU 39 + +#define MASTER_CNOC_STARDUST 0 +#define SLAVE_STARDUST_CENTER_CFG 1 +#define SLAVE_STARDUST_EAST_CFG 2 +#define SLAVE_STARDUST_MM_HF_CFG 3 +#define SLAVE_STARDUST_MM_SF_CFG 4 +#define SLAVE_STARDUST_NORTH_CFG 5 +#define SLAVE_STARDUST_SOUTH_CFG 6 +#define SLAVE_STARDUST_WEST_CFG 7 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_APSS_NOC 1 +#define MASTER_CNOC_SNOC 2 +#define SLAVE_SNOC_GEM_NOC_SF 3 + +#endif diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 9224974fffc4..366309260121 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -433,6 +433,29 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_RSU_DCMF_STATUS \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_DCMF_STATUS) +/** + * Request INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * + * Sync call used by service driver at EL1 to query QSPI device info from FW + * + * Call register usage: + * a0 INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * a1-7 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 erasesize0 | size0 + * a2 erasesize1 | size1 + * a3 erasesize2 | size2 + * a4 erasesize3 | size3 + * Or + * + * a0 INTEL_SIP_SMC_RSU_ERROR + */ +#define INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO 22 +#define INTEL_SIP_SMC_RSU_GET_DEVICE_INFO \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO) + /** * Request INTEL_SIP_SMC_SERVICE_COMPLETED * Sync call to check if the secure world have completed service request @@ -497,7 +520,7 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) * a3 not used */ #define INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD 60 - #define INTEL_SIP_SMC_MBOX_SEND_CMD \ +#define INTEL_SIP_SMC_MBOX_SEND_CMD \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD) /** @@ -701,6 +724,44 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_ASYNC_POLL \ INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_POLL) +/** + * Request INTEL_SIP_SMC_ASYNC_HWMON_READTEMP + * Async call to request temperature + * + * Call register usage: + * a0 INTEL_SIP_SMC_ASYNC_HWMON_READTEMP + * a1 transaction job id + * a2 Temperature Channel + * a3-a17 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_REJECTED + * or INTEL_SIP_SMC_STATUS_BUSY + * a1-a17 not used + */ +#define INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READTEMP 0xE8 +#define INTEL_SIP_SMC_ASYNC_HWMON_READTEMP \ + INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READTEMP) + +/** + * Request INTEL_SIP_SMC_ASYNC_HWMON_READVOLT + * Async call to request voltage + * + * Call register usage: + * a0 INTEL_SIP_SMC_ASYNC_HWMON_READVOLT + * a1 transaction job id + * a2 Voltage Channel + * a3-a17 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_REJECTED + * or INTEL_SIP_SMC_STATUS_BUSY + * a1-a17 not used + */ +#define INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READVOLT 0xE9 +#define INTEL_SIP_SMC_ASYNC_HWMON_READVOLT \ + INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READVOLT) + /** * Request INTEL_SIP_SMC_ASYNC_RSU_GET_SPT * Async call to get RSU SPT from SDM. diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 3edd93502bf8..af13dacdf5ac 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -128,6 +128,10 @@ struct stratix10_svc_chan; * @COMMAND_RSU_DCMF_STATUS: query firmware for the DCMF status * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * + * @COMMAND_RSU_GET_DEVICE_INFO: query firmware for QSPI device info; + * return status is SVC_STATUS_OK, SVC_STATUS_ERROR, or SVC_STATUS_NO_SUPPORT + * (unsupported command / firmware compatibility path in the service layer). + * * @COMMAND_RSU_GET_SPT_TABLE: query firmware for SPT table * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * @@ -174,6 +178,7 @@ enum stratix10_svc_command_code { COMMAND_RSU_MAX_RETRY, COMMAND_RSU_DCMF_VERSION, COMMAND_RSU_DCMF_STATUS, + COMMAND_RSU_GET_DEVICE_INFO, COMMAND_FIRMWARE_VERSION, COMMAND_RSU_GET_SPT_TABLE, /* for FCS */ @@ -224,7 +229,12 @@ struct stratix10_svc_command_config_type { /** * struct stratix10_svc_cb_data - callback data structure from service layer * @status: the status of sent command - * @kaddr1: address of 1st completed data block + * @kaddr1: address of 1st completed data block, or command-specific payload. + * For COMMAND_RSU_GET_DEVICE_INFO on SVC_STATUS_OK or SVC_STATUS_ERROR, + * points to struct arm_smccc_1_2_regs filled by the SMC/HVC return + * registers (a0 status, a1-a4 packed device words per + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO). On SVC_STATUS_NO_SUPPORT (older + * firmware that does not handle this command), kaddr1 is NULL. * @kaddr2: address of 2nd completed data block * @kaddr3: address of 3rd completed data block */ diff --git a/include/linux/greybus/greybus_id.h b/include/linux/greybus/greybus_id.h index f4c8440093e4..72f330a35569 100644 --- a/include/linux/greybus/greybus_id.h +++ b/include/linux/greybus/greybus_id.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* FIXME - * move this to include/linux/mod_devicetable.h when merging + * move this to include/linux/device-id/greybus.h when merging */ #ifndef __LINUX_GREYBUS_ID_H #define __LINUX_GREYBUS_ID_H #include -#include - struct greybus_bundle_id { __u16 match_flags; diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h index 8d506f1e9df2..4f08204ede3b 100644 --- a/include/linux/iio/common/inv_sensors_timestamp.h +++ b/include/linux/iio/common/inv_sensors_timestamp.h @@ -13,9 +13,9 @@ * @init_period: chip initial period at reset in ns */ struct inv_sensors_timestamp_chip { - uint32_t clock_period; - uint32_t jitter; - uint32_t init_period; + u32 clock_period; + u32 jitter; + u32 init_period; }; /** @@ -24,8 +24,8 @@ struct inv_sensors_timestamp_chip { * @up: interval upper bound */ struct inv_sensors_timestamp_interval { - int64_t lo; - int64_t up; + s64 lo; + s64 up; }; /** @@ -35,9 +35,9 @@ struct inv_sensors_timestamp_interval { * @values: table of all measured values, use for computing the mean */ struct inv_sensors_timestamp_acc { - uint32_t val; + u32 val; size_t idx; - uint32_t values[32]; + u32 values[32]; }; /** @@ -46,6 +46,8 @@ struct inv_sensors_timestamp_acc { * @min_period: minimal acceptable clock period * @max_period: maximal acceptable clock period * @it: interrupts interval timestamps + * @delta: interval timestamps between several interrupts + * @delta_counter: number of data samples in the delta interval * @timestamp: store last timestamp for computing next data timestamp * @mult: current internal period multiplier * @new_mult: new set internal period multiplier (not yet effective) @@ -54,13 +56,15 @@ struct inv_sensors_timestamp_acc { */ struct inv_sensors_timestamp { struct inv_sensors_timestamp_chip chip; - uint32_t min_period; - uint32_t max_period; + u32 min_period; + u32 max_period; struct inv_sensors_timestamp_interval it; - int64_t timestamp; - uint32_t mult; - uint32_t new_mult; - uint32_t period; + struct inv_sensors_timestamp_interval delta; + u32 delta_counter; + s64 timestamp; + u32 mult; + u32 new_mult; + u32 period; struct inv_sensors_timestamp_acc chip_period; }; @@ -68,19 +72,19 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, const struct inv_sensors_timestamp_chip *chip); int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, - uint32_t period, bool fifo); + u32 period, bool fifo); void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - size_t sample_nb, int64_t timestamp); + size_t sample_nb, s64 timestamp); -static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) +static inline s64 inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) { ts->timestamp += ts->period; return ts->timestamp; } void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, + u32 fifo_period, size_t fifo_nb, unsigned int fifo_no); static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts) @@ -88,6 +92,8 @@ static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts) const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL}; ts->it = interval_init; + ts->delta = interval_init; + ts->delta_counter = 0; ts->timestamp = 0; } diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 4e3099defc1d..d8944c9e5e90 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -7,6 +7,9 @@ #ifndef _IIO_TYPES_H_ #define _IIO_TYPES_H_ +#include +#include + #include enum iio_event_info { @@ -34,6 +37,24 @@ enum iio_event_info { #define IIO_VAL_FRACTIONAL_LOG2 11 #define IIO_VAL_CHAR 12 +#define IIO_VAL_DECIMAL64_BASE 32 +#define IIO_VAL_DECIMAL64_MILLI (IIO_VAL_DECIMAL64_BASE + 3) +#define IIO_VAL_DECIMAL64_MICRO (IIO_VAL_DECIMAL64_BASE + 6) +#define IIO_VAL_DECIMAL64_NANO (IIO_VAL_DECIMAL64_BASE + 9) +#define IIO_VAL_DECIMAL64_PICO (IIO_VAL_DECIMAL64_BASE + 12) +#define IIO_VAL_DECIMAL64_FEMTO (IIO_VAL_DECIMAL64_BASE + 15) + +static inline s64 iio_val_s64_compose(s32 val0, s32 val1) +{ + return (s64)(((u64)val1 << 32) | (u32)val0); +} + +static inline void iio_val_s64_decompose(s64 dec64, s32 *val0, s32 *val1) +{ + *val0 = lower_32_bits(dec64); + *val1 = upper_32_bits(dec64); +} + enum iio_available_type { IIO_AVAIL_LIST, IIO_AVAIL_RANGE, diff --git a/include/linux/kstrtox.h b/include/linux/kstrtox.h index 6c9282866770..b41b9736886f 100644 --- a/include/linux/kstrtox.h +++ b/include/linux/kstrtox.h @@ -97,6 +97,9 @@ int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); int __must_check kstrtobool(const char *s, bool *res); +int __must_check kstrtoudec64(const char *s, unsigned int scale, u64 *res); +int __must_check kstrtodec64(const char *s, unsigned int scale, s64 *res); + int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); diff --git a/include/linux/math64.h b/include/linux/math64.h index cc305206d89f..99189410d4bb 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -57,6 +57,20 @@ static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) return dividend / divisor; } +/** + * div64_s64_rem - signed 64bit divide with 64bit divisor and remainder + * @dividend: signed 64bit dividend + * @divisor: signed 64bit divisor + * @remainder: pointer to signed 64bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor + */ +static inline s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: unsigned 64bit dividend @@ -102,6 +116,10 @@ extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); #endif +#ifndef div64_s64_rem +extern s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder); +#endif + #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 4b86ae6f6a82..8616bacd8675 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -117,8 +117,8 @@ struct image_info { /** * struct mhi_link_info - BW requirement - * target_link_speed - Link speed as defined by TLS bits in LinkControl reg - * target_link_width - Link width as defined by NLW bits in LinkStatus reg + * @target_link_speed: Link speed as defined by TLS bits in LinkControl reg + * @target_link_width: Link width as defined by NLW bits in LinkStatus reg */ struct mhi_link_info { unsigned int target_link_speed; @@ -173,6 +173,7 @@ enum mhi_state { MHI_STATE_M3_FAST = 0x6, MHI_STATE_BHI = 0x7, MHI_STATE_SYS_ERR = 0xFF, + /* private: */ MHI_STATE_MAX, }; @@ -227,12 +228,12 @@ enum mhi_db_brst_mode { * @type: Channel type * @ee_mask: Execution Environment mask for this channel * @pollcfg: Polling configuration for burst mode. 0 is default. milliseconds - for UL channels, multiple of 8 ring elements for DL channels + * for UL channels, multiple of 8 ring elements for DL channels * @doorbell: Doorbell mode * @lpm_notify: The channel master requires low power mode notifications * @offload_channel: The client manages the channel completely * @doorbell_mode_switch: Channel switches to doorbell mode on M0 transition - * @wake-capable: Channel capable of waking up the system + * @wake_capable: Channel capable of waking up the system */ struct mhi_channel_config { char *name; @@ -350,7 +351,9 @@ struct mhi_controller_config { * @dev_state: MHI device state * @dev_wake: Device wakeup count * @pending_pkts: Pending packets for the controller - * @M0, M2, M3: Counters to track number of device MHI state changes + * @M0: Counter to track number of device MHI state changes + * @M2: Counter to track number of device MHI state changes + * @M3: Counter to track number of device MHI state changes * @transition_list: List of MHI state transitions * @transition_lock: Lock for protecting MHI state transition list * @wlock: Lock for protecting device wakeup @@ -375,6 +378,7 @@ struct mhi_controller_config { * @bounce_buf: Use of bounce buffer * @fbc_download: MHI host needs to do complete image transfer (optional) * @wake_set: Device wakeup set flag + * @no_m3: Device doesn't support M3 state * @irq_flags: irq flags passed to request_irq (optional) * @mru: the default MRU for the MHI device * @@ -460,6 +464,7 @@ struct mhi_controller { bool bounce_buf; bool fbc_download; bool wake_set; + bool no_m3; unsigned long irq_flags; u32 mru; }; @@ -507,6 +512,7 @@ struct mhi_result { /** * struct mhi_driver - Structure representing a MHI client driver + * @id_table: table of MHI channel names that a driver supports * @probe: CB function for client driver probe function * @remove: CB function for client driver remove function * @ul_xfer_cb: CB function for UL data transfer @@ -538,6 +544,7 @@ struct mhi_controller *mhi_alloc_controller(void); /** * mhi_free_controller - Free the MHI Controller structure + * @mhi_cntrl: MHI controller to free * Free the mhi_controller structure which was previously allocated */ void mhi_free_controller(struct mhi_controller *mhi_cntrl); diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h index 7b40fc8cbe77..f6383a57a872 100644 --- a/include/linux/mhi_ep.h +++ b/include/linux/mhi_ep.h @@ -107,6 +107,7 @@ struct mhi_ep_buf_info { * @write_sync: CB function for writing to host memory synchronously * @read_async: CB function for reading from host memory asynchronously * @write_async: CB function for writing to host memory asynchronously + * @flush_async: CB function for flushing asynchronous read/writes * @mhi_state: MHI Endpoint state * @max_chan: Maximum channels supported by the endpoint controller * @mru: MRU (Maximum Receive Unit) value of the endpoint controller @@ -164,6 +165,7 @@ struct mhi_ep_cntrl { int (*write_sync)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); int (*read_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); int (*write_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); + void (*flush_async)(struct mhi_ep_cntrl *mhi_cntrl); enum mhi_state mhi_state; diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h new file mode 100644 index 000000000000..5273c44688f0 --- /dev/null +++ b/include/linux/misc/tsi.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SBTSI shared data structure and auxiliary bus definitions. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#ifndef _LINUX_MISC_TSI_H_ +#define _LINUX_MISC_TSI_H_ + +#include +#include +#include +#include +#include +#include +#include + +/** + * struct sbtsi_data - driver private data for an AMD SB-TSI device + * @client: underlying I2C client + * @i3cdev: underlying I3C device (when using I3C bus) + * @sbtsi_misc_dev: miscdevice exposing ioctl interface at /dev/sbtsi- + * @lock: mutex protecting concurrent access to the device + * @kref: reference count; keeps @sbtsi_data alive while misc fds are open + * @dev_addr: I2C/I3C device address, used as the auxiliary device instance id + * and name the misc device node + * @ext_range_mode: sensor uses extended temperature range + * @read_order: if set, decimal part must be read before integer part + * @is_i3c: true when the device is accessed over I3C + * @detached: set on driver unbind; open/ioctl return -ENODEV afterward + */ +struct sbtsi_data { + union { + struct i2c_client *client; + struct i3c_device *i3cdev; + }; + struct miscdevice sbtsi_misc_dev; + struct mutex lock; /* protects concurrent access to the device */ + struct kref kref; + u8 dev_addr; + bool ext_range_mode; + bool read_order; + bool is_i3c; + bool detached; +}; + +DEFINE_GUARD(sbtsi, struct sbtsi_data *, mutex_lock(&_T->lock), + mutex_unlock(&_T->lock)) + +/* + * Name of the auxiliary device published on the auxiliary bus by the core + * driver. The full device name is "amd-sbtsi.temp-sensor.". where + * is the auxiliary device instance id. + */ +#define AMD_SBTSI_ADEV "amd-sbtsi" +#define AMD_SBTSI_AUX_HWMON "temp-sensor" + +/** + * sbtsi_xfer - Perform a register read or write transfer on an AMD SB-TSI device. + * + * @data: Pointer to the sbtsi_data structure containing the device context + * @reg: Register address to access. + * @val: Pointer to the value to read into or write from. + * @is_read: If true, performs a read transfer and stores the result in @val. + * If false, performs a write transfer using the value in @val. + * + * Returns 0 on success, or a negative error code on failure. + */ +int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read); + +#endif /* _LINUX_MISC_TSI_H_ */ diff --git a/include/linux/most.h b/include/linux/most.h index 232e01b7f5d2..4aeed08f816e 100644 --- a/include/linux/most.h +++ b/include/linux/most.h @@ -15,7 +15,7 @@ struct module; struct interface_private; /** - * Interface type + * enum most_interface_type - Interface type */ enum most_interface_type { ITYPE_LOOPBACK = 1, @@ -30,7 +30,7 @@ enum most_interface_type { }; /** - * Channel direction. + * enum most_channel_direction - Channel direction. */ enum most_channel_direction { MOST_CH_RX = 1 << 0, @@ -38,7 +38,7 @@ enum most_channel_direction { }; /** - * Channel data type. + * enum most_channel_data_type - Channel data type. */ enum most_channel_data_type { MOST_CH_CONTROL = 1 << 0, @@ -115,6 +115,7 @@ struct most_channel_capability { * @subbuffer_size: size of a subbuffer * @packets_per_xact: number of MOST frames that are packet inside one USB * packet. This is USB specific + * @dbr_size: DBR data buffer size (MediaLB communication only) * * Describes the configuration for a MOST channel. This information is * provided from the MostCore to a HDM (like the Medusa PCIe Interface) as a @@ -189,28 +190,31 @@ struct mbo { }; /** - * Interface instance description. + * struct most_interface - Interface instance description. * * Describes an interface of a MOST device the core driver is bound to. * This structure is allocated and initialized in the HDM. MostCore may not * modify this structure. * * @dev: the actual device + * @driver_dev: struct device for calling snd_card_new() * @mod: module - * @interface Interface type. \sa most_interface_type. - * @description PRELIMINARY. + * @interface: Interface type. \sa most_interface_type. + * @description: PRELIMINARY. * Unique description of the device instance from point of view of the * interface in free text form (ASCII). * It may be a hexadecimal presentation of the memory address for the MediaLB * IP or USB device ID with USB properties for USB interface, etc. - * @num_channels Number of channels and size of the channel_vector. - * @channel_vector Properties of the channels. + * @num_channels: Number of channels and size of the channel_vector. + * @channel_vector: Properties of the channels. * Array index represents channel ID by the driver. - * @configure Callback to change data type for the channel of the + * @dma_alloc: Callback to interface driver to allocation DMA memory. + * @dma_free: Callback to interface driver to free a DMA memory allocation. + * @configure: Callback to change data type for the channel of the * interface instance. May be zero if the instance of the interface is not * configurable. Parameter channel_config describes direction and data * type for the channel, configured by the higher level. The content of - * @enqueue Delivers MBO to the HDM for processing. + * @enqueue: Delivers MBO to the HDM for processing. * After HDM completes Rx- or Tx- operation the processed MBO shall * be returned back to the MostCore using completion routine. * The reason to get the MBO delivered from the MostCore after the channel @@ -218,7 +222,7 @@ struct mbo { * In this case the HDM shall hold MBOs and service the channel as usual. * The HDM must be able to hold at least one MBO for each channel. * The callback returns a negative value on error, otherwise 0. - * @poison_channel Informs HDM about closing the channel. The HDM shall + * @poison_channel: Informs HDM about closing the channel. The HDM shall * cancel all transfers and synchronously or asynchronously return * all enqueued for this channel MBOs using the completion routine. * The callback returns a negative value on error, otherwise 0. @@ -226,7 +230,8 @@ struct mbo { * means of "Message exchange over MDP/MEP" * The call of the function request_netinfo with the parameter on_netinfo as * NULL prohibits use of the previously obtained function pointer. - * @priv Private field used by mostcore to store context information. + * @priv: Private field used by mostcore to store context information. + * @p: &struct interface_private pointer */ struct most_interface { struct device *dev; @@ -255,10 +260,12 @@ struct most_interface { * struct most_component - identifies a loadable component for the mostcore * @list: list_head * @name: component name + * @mod: owning module * @probe_channel: function for core to notify driver about channel connection * @disconnect_channel: callback function to disconnect a certain channel * @rx_completion: completion handler for received packets * @tx_completion: completion handler for transmitted packets + * @cfg_complete: setup completion function called after the device is matched */ struct most_component { struct list_head list; @@ -278,7 +285,7 @@ struct most_component { * most_register_interface - Registers instance of the interface. * @iface: Pointer to the interface instance description. * - * Returns a pointer to the kobject of the generated instance. + * Returns: a pointer to the kobject of the generated instance. * * Note: HDM has to ensure that any reference held on the kobj is * released before deregistering the interface. @@ -286,8 +293,8 @@ struct most_component { int most_register_interface(struct most_interface *iface); /** - * Deregisters instance of the interface. - * @intf_instance Pointer to the interface instance description. + * most_deregister_interface - Deregisters instance of the interface. + * @iface: Pointer to the interface instance description. */ void most_deregister_interface(struct most_interface *iface); void most_submit_mbo(struct mbo *mbo); diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index c9295c707041..d0fb714ca0fd 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -54,6 +54,7 @@ enum iio_chan_type { IIO_ATTENTION, IIO_ALTCURRENT, IIO_COVERAGE, + IIO_VOLUMEFLOW, }; enum iio_modifier { diff --git a/include/uapi/linux/issei.h b/include/uapi/linux/issei.h new file mode 100644 index 000000000000..3bfb89330265 --- /dev/null +++ b/include/uapi/linux/issei.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2023-2026 Intel Corporation + * Intel Silicon Security Engine Interface (ISSEI) Linux driver: + * ISSEI Interface Header + */ +#ifndef _LINUX_ISSEI_H +#define _LINUX_ISSEI_H + +#include +#include + +/* + * This ioctl is used to associate the current file descriptor with a + * FW Client (given by UUID). This opens a communication channel + * between a host client and a FW client. From this point every read and write + * will communicate with the associated FW client. + * The communication between the clients can be terminated by + * IOCTL_ISSEI_DISCONNECT_CLIENT IOCTL or by + * closing the file descriptor (file_operation release()). + * + * The ioctl argument is a struct with a union that contains + * the input parameter and the output parameter for this ioctl. + * + * The input parameter is UUID of the FW Client. + * The output parameter is the properties of the FW client + * (FW protocol version, max message size and client flags). + */ +#define IOCTL_ISSEI_CONNECT_CLIENT \ + _IOWR('H', 0x01, struct issei_connect_client_data) + +/** + * struct issei_client - ISSEI client information structure + * @max_msg_length: maximum message length supported by the firmware client (in bytes) + * @protocol_version: protocol version reported by the firmware client + * @reserved1: reserved + * @flags: flag bitmask reported by the firmware client + * @reserved2: reserved + */ +struct issei_client { + __u32 max_msg_length; + __u8 protocol_version; + __u8 reserved1[3]; + __u32 flags; + __u32 reserved2; +}; + +#define ISSEI_IOCTL_UUID_LEN 16 + +/** + * struct issei_connect_client_data - ioctl Connect Client Data structure + * @in_client_uuid: unique id of the firmware client to connect to (from user space to kernel) + * @out_client_properties: connected firmware client properties (from kernel to user space) + */ +struct issei_connect_client_data { + union { + __u8 in_client_uuid[ISSEI_IOCTL_UUID_LEN]; + struct issei_client out_client_properties; + }; +}; + +/* + * This ioctl is used to terminate association between + * the host client and the FW client. + */ +#define IOCTL_ISSEI_DISCONNECT_CLIENT \ + _IO('H', 0x02) + +#endif /* _LINUX_ISSEI_H */ diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h index 745b3338fc06..8a85f79b0938 100644 --- a/include/uapi/misc/amd-apml.h +++ b/include/uapi/misc/amd-apml.h @@ -73,6 +73,13 @@ struct apml_reg_xfer_msg { __u8 rflag; }; +struct apml_tsi_xfer_msg { + __u8 reg_addr; /* TSI register address offset */ + __u8 data_in_out; /* Register data for read/write */ + __u8 rflag; /* Register read or write */ + __u8 pad; /* Explicit padding */ +}; + /* * AMD sideband interface base IOCTL */ @@ -149,4 +156,20 @@ struct apml_reg_xfer_msg { */ #define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg) +/** + * DOC: SBTSI_IOCTL_REG_XFER_CMD + * + * @Parameters + * + * @struct apml_tsi_xfer_msg + * Pointer to the &struct apml_tsi_xfer_msg that will contain the protocol + * information + * + * @Description + * IOCTL command for APML TSI messages using generic _IOWR + * The IOCTL provides userspace access to AMD sideband TSI register xfer protocol + * - TSI protocol to read/write temperature sensor registers + */ +#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg) + #endif /*_AMD_APML_H_*/ diff --git a/include/uapi/misc/fastrpc.h b/include/uapi/misc/fastrpc.h index c6e2925f47e6..ba1ea5ed426c 100644 --- a/include/uapi/misc/fastrpc.h +++ b/include/uapi/misc/fastrpc.h @@ -16,6 +16,7 @@ #define FASTRPC_IOCTL_INIT_CREATE_STATIC _IOWR('R', 9, struct fastrpc_init_create_static) #define FASTRPC_IOCTL_MEM_MAP _IOWR('R', 10, struct fastrpc_mem_map) #define FASTRPC_IOCTL_MEM_UNMAP _IOWR('R', 11, struct fastrpc_mem_unmap) +#define FASTRPC_IOCTL_SET_OPTION _IOWR('R', 12, struct fastrpc_ioctl_set_option) #define FASTRPC_IOCTL_GET_DSP_INFO _IOWR('R', 13, struct fastrpc_ioctl_capability) /** @@ -67,6 +68,28 @@ enum fastrpc_proc_attr { /* Fastrpc attribute for memory protection of buffers */ #define FASTRPC_ATTR_SECUREMAP (1) +/** + * FASTRPC_POLL_MODE - Enable/disable poll mode for FastRPC invocations + * + * Poll mode is an optimization that allows the CPU to poll shared memory + * for completion instead of waiting for an interrupt-based response. + * This reduces latency for fast-completing operations. + * + * Restrictions: + * - Only supported for USER_PD (User Protection Domain) + * - Only applies to dynamic modules (handle > 20) + * - Static modules always use interrupt-based completion + * + * Values: + * - 0: Disable poll mode (use interrupt-based completion) + * - 1: Enable poll mode (poll shared memory for completion) + */ +#define FASTRPC_POLL_MODE (1) + +/* Values for FASTRPC_POLL_MODE request */ +#define FASTRPC_POLL_MODE_DISABLE 0 +#define FASTRPC_POLL_MODE_ENABLE 1 + struct fastrpc_invoke_args { __u64 ptr; __u64 length; @@ -133,6 +156,12 @@ struct fastrpc_mem_unmap { __s32 reserved[5]; }; +struct fastrpc_ioctl_set_option { + __u32 request_id; /* Request type (e.g., FASTRPC_POLL_MODE) */ + __u32 value; /* Request-specific value */ + __s32 reserved[6]; +}; + struct fastrpc_ioctl_capability { __u32 unused; /* deprecated, ignored by the kernel */ __u32 attribute_id; diff --git a/lib/kstrtox.c b/lib/kstrtox.c index edc4eb7c1bca..bac1c057e1b0 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) * @base: Radix * @p: Where to store result * @max_chars: Maximum amount of characters to convert + * @init: Initial value of the multiply-accumulate result * * Convert non-negative integer string representation in explicitly given * radix to an integer. If overflow occurs, value at @p is set to ULLONG_MAX. @@ -56,12 +58,12 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) */ noinline unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p, - size_t max_chars) + size_t max_chars, unsigned long long init) { unsigned int rv, overflow = 0; unsigned long long res; - res = 0; + res = init; for (rv = 0; rv < max_chars; rv++, s++) { unsigned int c = *s; unsigned int lc = _tolower(c); @@ -94,12 +96,6 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon return rv | overflow; } -noinline -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) -{ - return _parse_integer_limit(s, base, p, INT_MAX); -} - static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) { unsigned long long _res; @@ -399,6 +395,109 @@ int kstrtobool(const char *s, bool *res) } EXPORT_SYMBOL(kstrtobool); +static int _kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + unsigned int rv_int, rv_frac; + u64 _res = 0; + + rv_int = _parse_integer(s, 10, &_res); + if (rv_int & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_int; + + if (*s == '.') + s++; /* skip decimal point */ + + rv_frac = _parse_integer(s, 10, &_res, scale, _res); + if (rv_frac & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_frac; + + /* + * Check input beyond rv_int and rv_frac to cover cases like ".5" with + * scale 0, which is considered a valid input, being parsed as 0. + */ + if (!rv_int && !rv_frac && !isdigit(*s)) + return -EINVAL; + + while (isdigit(*s)) /* truncate digits */ + s++; + + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + + if (_res && ((scale - rv_frac) > 19 /* log10(2^64) = 19.26 */ || + check_mul_overflow(_res, int_pow(10, scale - rv_frac), &_res))) + return -ERANGE; + + *res = _res; + return 0; +} + +/** + * kstrtoudec64() - Convert a string to an unsigned 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 3 with input "123.45" results in 123450. Note that + * trailing zeros in the fractional part input to match the scale are not + * required. Also, digits beyond the specified scale are ignored. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + if (s[0] == '+') + s++; + return _kstrtoudec64(s, scale, res); +} +EXPORT_SYMBOL(kstrtoudec64); + +/** + * kstrtodec64() - Convert a string to a signed 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 4 with input "-3.141592" results in -31415. Note + * that digits beyond the specified scale are ignored. Also, trailing zeros in + * the fractional part input to match the scale are not required. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtodec64(const char *s, unsigned int scale, s64 *res) +{ + u64 tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoudec64(s + 1, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)-tmp > 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoudec64(s, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtodec64); + /* * Since "base" would be a nonsense argument, this open-codes the * _from_user helper instead of using the helper macro below. diff --git a/lib/kstrtox.h b/lib/kstrtox.h index 158c400ca865..73dee79fd8ed 100644 --- a/lib/kstrtox.h +++ b/lib/kstrtox.h @@ -2,10 +2,23 @@ #ifndef _LIB_KSTRTOX_H #define _LIB_KSTRTOX_H +#include + #define KSTRTOX_OVERFLOW (1U << 31) const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *res, - size_t max_chars); -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); + size_t max_chars, unsigned long long init); + +#define _parse_integer0(s, base, res, ...) \ + _parse_integer_limit(s, base, res, INT_MAX, 0) + +#define _parse_integer1(s, base, res, max_chars, ...) \ + _parse_integer_limit(s, base, res, max_chars, 0) + +#define _parse_integer2(s, base, res, max_chars, init, ...) \ + _parse_integer_limit(s, base, res, max_chars, init) + +#define _parse_integer(s, base, res, ...) \ + CONCATENATE(_parse_integer, COUNT_ARGS(__VA_ARGS__))(s, base, res, __VA_ARGS__) #endif diff --git a/lib/math/div64.c b/lib/math/div64.c index d1e92ea24fce..0b10ded09a9b 100644 --- a/lib/math/div64.c +++ b/lib/math/div64.c @@ -158,6 +158,21 @@ u64 div64_u64(u64 dividend, u64 divisor) EXPORT_SYMBOL(div64_u64); #endif +#ifndef div64_s64_rem +s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + s64 quot, t, rem; + + quot = div64_u64_rem(abs(dividend), abs(divisor), (u64 *)&rem); + t = dividend >> 63; + *remainder = (rem ^ t) - t; + t = (dividend ^ divisor) >> 63; + + return (quot ^ t) - t; +} +EXPORT_SYMBOL(div64_s64_rem); +#endif + #ifndef div64_s64 s64 div64_s64(s64 dividend, s64 divisor) { diff --git a/lib/math/test_mul_u64_u64_div_u64.c b/lib/math/test_mul_u64_u64_div_u64.c index 338d014f0c73..d12dc05938fb 100644 --- a/lib/math/test_mul_u64_u64_div_u64.c +++ b/lib/math/test_mul_u64_u64_div_u64.c @@ -157,6 +157,7 @@ static void __exit test_exit(void) #define __div64_32 __div64_32 #define div_s64_rem div_s64_rem #define div64_u64_rem div64_u64_rem +#define div64_s64_rem div64_s64_rem #define div64_u64 div64_u64 #define div64_s64 div64_s64 #define iter_div_u64_rem iter_div_u64_rem diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c index 811128d0df16..9255234752db 100644 --- a/lib/test-kstrtox.c +++ b/lib/test-kstrtox.c @@ -709,6 +709,182 @@ static void __init test_kstrtos8_fail(void) TEST_FAIL(kstrtos8, s8, "%hhd", test_s8_fail); } +static void __init test_kstrtoudec64_ok(void) +{ + DECLARE_TEST_OK(u64, struct test_udec64); + static DEFINE_TEST_OK(struct test_udec64, test_udec64_ok) = { + /* basic: integer.fraction, exact digits */ + {"0.0", 1, 0}, + {"1.5", 1, 15}, + {"1.234", 3, 1234}, + {"42.0", 1, 420}, + /* zero */ + {"0.0", 1, 0}, + {"0.000", 3, 0}, + /* integer only */ + {"0", 1, 0}, + {"42", 3, 42000}, + {"123.", 2, 12300}, + {"1", 1, 10}, + /* fractional only (leading dot) */ + {".5", 1, 5}, + {".5", 0, 0}, + {".123", 3, 123}, + {".001", 3, 1}, + /* zero padding: fewer fractional digits than scale */ + {"1.2", 3, 1200}, + {"1.2", 6, 1200000}, + {"0.01", 3, 10}, + {"0.1", 9, 100000000ULL}, + {"0.01", 9, 10000000}, + /* truncation: more fractional digits than scale */ + {"1.23456", 3, 1234}, + {"3.1415926535", 6, 3141592}, + {"0.999999999", 3, 999}, + {"1.99", 1, 19}, + {"1.234", 0, 1}, + /* trailing newline */ + {"1.5\n", 1, 15}, + {"42\n", 3, 42000}, + /* plus sign */ + {"+1.5", 1, 15}, + {"+.5", 1, 5}, + /* scale progression */ + {"1.", 0, 1}, + {"1.0", 1, 10}, + {"1.00", 2, 100}, + {"1.000", 3, 1000}, + {"1.000000", 6, 1000000}, + {"1.000000000", 9, 1000000000ULL}, + /* max limit check */ + {"18446744073.709551615", 9, ULLONG_MAX}, + {"18446744073709.551615", 6, ULLONG_MAX}, + {"0.18446744073709551615", 20, ULLONG_MAX}, + /* scale > 19: representable when integer part is small */ + {"0.00000000000000000001", 20, 1}, + {"0.1", 20, 10000000000000000000ULL}, + {"0.00000000000000000000001", 23, 1}, + /* truncation with scale > 19 */ + {"0.0000000000000000000012345", 23, 123}, + /* truncation with many excess digits */ + {"0.00000000000000000000000000000000423", 34, 42}, + {"1.99999999999999999999999999999999999", 3, 1999}, + }; + TEST_OK(kstrtoudec64, u64, "%llu", test_udec64_ok); +} + +static void __init test_kstrtoudec64_fail(void) +{ + static DEFINE_TEST_FAIL(test_udec64_fail) = { + /* empty / whitespace */ + {"", 3}, + {"\n", 3}, + /* minus sign (unsigned) */ + {"-1.5", 1}, + {"-0.5", 1}, + /* only a decimal point */ + {".", 3}, + {".", 0}, + /* only a sign */ + {"+", 3}, + /* non-digit characters */ + {"abc", 3}, + {"1.2x", 3}, + /* leading/trailing space */ + {" 1.5", 1}, + {"1.5 ", 1}, + /* overflow */ + {"18446744073710.551615", 6}, + {"99999999999999999999", 1}, + /* overflow with scale > 19 */ + {"1.0", 21}, + {"0.2", 20}, + {"0.18446744073709551616", 20}, + {"1", 20}, + }; + TEST_FAIL(kstrtoudec64, u64, "%llu", test_udec64_fail); +} + +static void __init test_kstrtodec64_ok(void) +{ + DECLARE_TEST_OK(s64, struct test_dec64); + static DEFINE_TEST_OK(struct test_dec64, test_dec64_ok) = { + /* basic positive */ + {"0.0", 1, 0}, + {"1.5", 1, 15}, + {"1.234", 3, 1234}, + /* basic negative */ + {"-1.5", 1, -15}, + {"-1.234", 3, -1234}, + {"-0.5", 1, -5}, + {"-0.001", 3, -1}, + /* zero (signed) */ + {"-0", 1, 0}, + {"-0.0", 1, 0}, + {"0.000", 3, 0}, + /* integer only */ + {"42", 3, 42000}, + {"-42", 3, -42000}, + /* fractional only */ + {".5", 1, 5}, + {"-.5", 1, -5}, + /* zero padding */ + {"1.2", 3, 1200}, + {"-1.2", 3, -1200}, + {"0.01", 3, 10}, + {"-0.01", 3, -10}, + /* truncation */ + {"1.23456", 3, 1234}, + {"-1.23456", 3, -1234}, + {"0.999999999", 3, 999}, + {"-0.999999999", 3, -999}, + /* trailing newline */ + {"1.5\n", 1, 15}, + {"-1.5\n", 1, -15}, + /* plus sign */ + {"+1.5", 1, 15}, + /* limits */ + {"9223372036.854775807", 9, LLONG_MAX}, + {"-9223372036.854775808", 9, LLONG_MIN}, + /* scale > 19 */ + {"0.0", 20, 0}, + {"-0.0", 20, 0}, + {"0.00000000000000000001", 20, 1}, + {"-0.00000000000000000001", 20, -1}, + {"0.009223372036854775807", 21, LLONG_MAX}, + {"-0.009223372036854775808", 21, LLONG_MIN}, + }; + TEST_OK(kstrtodec64, s64, "%lld", test_dec64_ok); +} + +static void __init test_kstrtodec64_fail(void) +{ + static DEFINE_TEST_FAIL(test_dec64_fail) = { + /* empty / whitespace */ + {"", 3}, + {"\n", 3}, + /* no digits after dot */ + {".", 3}, + {"-.", 3}, + /* no digits at all */ + {"+", 3}, + {"-", 3}, + /* non-digit characters */ + {"abc", 3}, + {"-1.2x", 3}, + /* signed overflow */ + {"9223372036.854775808", 9}, + {"-9223372036.854775809", 9}, + {"99999999999999999999", 1}, + /* signed overflow with scale > 19 */ + {"0.1", 20}, + {"-0.1", 20}, + {"0.09223372036854775808", 20}, + {"-0.09223372036854775809", 20}, + }; + TEST_FAIL(kstrtodec64, s64, "%lld", test_dec64_fail); +} + static int __init test_kstrtox_init(void) { test_kstrtoull_ok(); @@ -735,6 +911,12 @@ static int __init test_kstrtox_init(void) test_kstrtou8_fail(); test_kstrtos8_ok(); test_kstrtos8_fail(); + + test_kstrtoudec64_ok(); + test_kstrtoudec64_fail(); + test_kstrtodec64_ok(); + test_kstrtodec64_fail(); + return -EINVAL; } module_init(test_kstrtox_init); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e285e8bc4712..1d95ab9314bf 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -86,7 +86,7 @@ static unsigned long long simple_strntoull(const char *startp, char **endp, unsi cp = _parse_integer_fixup_radix(startp, &base); prefix_chars = cp - startp; if (prefix_chars < max_chars) { - rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars); + rv = _parse_integer(cp, base, &result, max_chars - prefix_chars); /* FIXME */ cp += (rv & ~KSTRTOX_OVERFLOW); } else { diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 82d2faf839f2..4b31aa7f432f 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -95,6 +95,8 @@ #include #include #include +#include +#include #include /* @@ -112,6 +114,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN; const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE; +const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE = GENLMSG_DEFAULT_SIZE; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT; diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index dc2f8de8ec22..440fb7638e3c 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -75,6 +75,7 @@ #include "maple_tree.c" #include "mm.c" #include "mutex.c" +#include "net/genetlink.c" #include "of.c" #include "page.c" #include "pci.c" diff --git a/rust/helpers/net/genetlink.c b/rust/helpers/net/genetlink.c new file mode 100644 index 000000000000..3530b69f6cf7 --- /dev/null +++ b/rust/helpers/net/genetlink.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2026 Google LLC. + */ + +#include + +#ifdef CONFIG_NET + +__rust_helper struct sk_buff *rust_helper_genlmsg_new(size_t payload, gfp_t flags) +{ + return genlmsg_new(payload, flags); +} + +__rust_helper +int rust_helper_genlmsg_multicast(const struct genl_family *family, + struct sk_buff *skb, u32 portid, + unsigned int group, gfp_t flags) +{ + return genlmsg_multicast(family, skb, portid, group, flags); +} + +__rust_helper void rust_helper_genlmsg_cancel(struct sk_buff *skb, void *hdr) +{ + genlmsg_cancel(skb, hdr); +} + +__rust_helper void rust_helper_genlmsg_end(struct sk_buff *skb, void *hdr) +{ + genlmsg_end(skb, hdr); +} + +__rust_helper void rust_helper_nlmsg_free(struct sk_buff *skb) +{ + nlmsg_free(skb); +} + +__rust_helper +int rust_helper_genl_has_listeners(const struct genl_family *family, + struct net *net, unsigned int group) +{ + return genl_has_listeners(family, net, group); +} + +#endif diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 2a4329f98614..8d4b719bd83f 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -290,7 +290,7 @@ impl MiscdeviceVTable { /// # Safety /// /// `kiocb` must be correspond to a valid file that is associated with a - /// `MiscDeviceRegistration`. `iter` must be a valid `struct iov_iter` for writing. + /// `MiscDeviceRegistration`. `iter` must be a valid `struct iov_iter` for reading. unsafe extern "C" fn write_iter( kiocb: *mut bindings::kiocb, iter: *mut bindings::iov_iter, diff --git a/rust/kernel/net.rs b/rust/kernel/net/mod.rs similarity index 85% rename from rust/kernel/net.rs rename to rust/kernel/net/mod.rs index fe415cb369d3..8ecae7577ed2 100644 --- a/rust/kernel/net.rs +++ b/rust/kernel/net/mod.rs @@ -4,3 +4,5 @@ #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)] pub mod phy; + +pub mod netlink; diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs new file mode 100644 index 000000000000..22ef3dde36fa --- /dev/null +++ b/rust/kernel/net/netlink.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2026 Google LLC. + +//! Rust support for generic netlink. +//! +//! Currently only supports exposing multicast groups. +//! +//! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h) + +use kernel::{ + alloc::{self, AllocError}, + error::to_result, + prelude::*, + transmute::AsBytes, + types::Opaque, + ThisModule, +}; + +use core::{ + mem::ManuallyDrop, + ptr::NonNull, // +}; + +/// The default netlink message size. +pub const GENLMSG_DEFAULT_SIZE: usize = bindings::GENLMSG_DEFAULT_SIZE; + +/// A wrapper around `struct sk_buff` for generic netlink messages. +/// +/// This type is intended to be specific for buffers used with netlink only, and other usecases for +/// `struct sk_buff` are out-of-scope for this abstraction. +/// +/// # Invariants +/// +/// The pointer has ownership over a valid `sk_buff`. +pub struct NetlinkSkBuff { + skb: NonNull, +} + +impl NetlinkSkBuff { + /// Creates a new `NetlinkSkBuff` with the given size. + pub fn new(size: usize, flags: alloc::Flags) -> Result { + // SAFETY: `genlmsg_new` only requires its arguments to be valid integers. + let skb = unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; + let skb = NonNull::new(skb).ok_or(AllocError)?; + Ok(NetlinkSkBuff { skb }) + } + + /// Puts a generic netlink header into the `NetlinkSkBuff`. + pub fn genlmsg_put( + self, + portid: u32, + seq: u32, + family: &'static Family, + cmd: u8, + ) -> Result { + let skb = self.skb.as_ptr(); + // SAFETY: The skb and family pointers are valid. + let hdr = unsafe { bindings::genlmsg_put(skb, portid, seq, family.as_raw(), 0, cmd) }; + let hdr = NonNull::new(hdr).ok_or(AllocError)?; + Ok(GenlMsg { skb: self, hdr }) + } +} + +impl Drop for NetlinkSkBuff { + fn drop(&mut self) { + // SAFETY: We have ownership over the `sk_buff`, so we may free it. + unsafe { bindings::nlmsg_free(self.skb.as_ptr()) } + } +} + +/// A generic netlink message being constructed. +/// +/// # Invariants +/// +/// `hdr` references the header in this netlink message. +pub struct GenlMsg { + skb: NetlinkSkBuff, + hdr: NonNull, +} + +impl GenlMsg { + /// Puts an attribute into the message. + #[inline] + fn put(&mut self, attrtype: c_int, value: &T) -> Result + where + T: ?Sized + AsBytes, + { + let skb = self.skb.skb.as_ptr(); + let len = size_of_val(value); + let ptr = core::ptr::from_ref(value).cast::(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is + // readable and initialized for its `size_of` bytes. + to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) }) + } + + /// Puts a `u32` attribute into the message. + #[inline] + pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result { + self.put(attrtype, &value) + } + + /// Puts a string attribute into the message. + #[inline] + pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result { + self.put(attrtype, value.to_bytes_with_nul()) + } + + /// Puts a flag attribute into the message. + #[inline] + pub fn put_flag(&mut self, attrtype: c_int) -> Result { + let skb = self.skb.skb.as_ptr(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and a null pointer is valid + // when the length is zero. + to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::null()) }) + } + + /// Sends the generic netlink message as a multicast message. + #[inline] + pub fn multicast( + self, + family: &'static Family, + portid: u32, + group: u32, + flags: alloc::Flags, + ) -> Result { + let me = ManuallyDrop::new(self); + // SAFETY: The `skb` and `family` pointers are valid. We pass ownership of the `skb` to + // `genlmsg_multicast` by not dropping `self`. + unsafe { + bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr()); + to_result(bindings::genlmsg_multicast( + family.as_raw(), + me.skb.skb.as_ptr(), + portid, + group, + flags.as_raw(), + )) + } + } +} +impl Drop for GenlMsg { + fn drop(&mut self) { + // SAFETY: The `hdr` pointer references the header of this generic netlink message. + unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.as_ptr()) }; + } +} + +/// Flags for a generic netlink family. +struct FamilyFlags { + /// Whether the family supports network namespaces. + netnsok: bool, + /// Whether the family supports parallel operations. + parallel_ops: bool, +} + +impl FamilyFlags { + /// Converts the flags to the bitfield representation used by `genl_family`. + const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1]> { + // The below shifts are verified correct by test_family_flags_bitfield() below. + // + // Although bindgen generates helpers to change bitfields based on the C headers, these + // helpers unfortunately can't be used in const context. Since `Family` needs to be filled + // out at build-time, we use this helper instead. + let mut bits = 0; + if self.netnsok { + bits |= 1 << 0; + } + if self.parallel_ops { + bits |= 1 << 1; + } + // Convert from little endian to the target's endianness. + bits = u8::from_le(bits); + // SAFETY: This bitfield is represented as an u8. + unsafe { core::mem::transmute::>(bits) } + } +} + +/// A generic netlink family. +#[repr(transparent)] +pub struct Family { + inner: Opaque, +} + +// SAFETY: The `Family` type is thread safe. +unsafe impl Sync for Family {} + +impl Family { + /// Creates a new `Family` instance. + /// + /// Intended to be used from const context only. Will panic if provided with invalid arguments. + /// + /// The name must be a nul-terminated string, but it is taken as `&[u8]` so that it can be used + /// more conveniently with the strings generated by bindgen. + pub const fn const_new( + module: &ThisModule, + name: &[u8], + version: u32, + mcgrps: &'static [MulticastGroup], + ) -> Family { + let n_mcgrps = mcgrps.len() as u8; + if n_mcgrps as usize != mcgrps.len() { + panic!("too many mcgrps"); + } + let mut genl_family = bindings::genl_family { + version, + _bitfield_1: FamilyFlags { + netnsok: true, + parallel_ops: true, + } + .into_bitfield(), + module: module.as_ptr(), + mcgrps: mcgrps.as_ptr().cast(), + n_mcgrps, + ..pin_init::zeroed() + }; + if CStr::from_bytes_with_nul(name).is_err() { + panic!("genl_family name not nul-terminated"); + } + if genl_family.name.len() < name.len() { + panic!("genl_family name too long"); + } + let mut i = 0; + while i < name.len() { + genl_family.name[i] = name[i]; + i += 1; + } + Family { + inner: Opaque::new(genl_family), + } + } + + /// Checks if there are any listeners for the given multicast group. + pub fn has_listeners(&self, group: u32) -> bool { + // SAFETY: The family and init_net pointers are valid. + unsafe { + bindings::genl_has_listeners(self.as_raw(), &raw mut bindings::init_net, group) != 0 + } + } + + /// Returns a raw pointer to the underlying `genl_family` structure. + pub fn as_raw(&self) -> *mut bindings::genl_family { + self.inner.get() + } +} + +/// A generic netlink multicast group. +#[repr(transparent)] +pub struct MulticastGroup { + // No Opaque because fully immutable + group: bindings::genl_multicast_group, +} + +// SAFETY: Pure data so thread safe. +unsafe impl Sync for MulticastGroup {} + +impl MulticastGroup { + /// Creates a new `MulticastGroup` instance. + /// + /// Intended to be used from const context only. Will panic if provided with invalid arguments. + pub const fn const_new(name: &CStr) -> MulticastGroup { + let mut group: bindings::genl_multicast_group = pin_init::zeroed(); + + let name = name.to_bytes_with_nul(); + if group.name.len() < name.len() { + panic!("genl_multicast_group name too long"); + } + let mut i = 0; + while i < name.len() { + group.name[i] = name[i]; + i += 1; + } + + MulticastGroup { group } + } +} + +/// A registration of a generic netlink family. +/// +/// This type represents the registration of a [`Family`]. When an instance of this type is +/// dropped, its respective generic netlink family will be unregistered from the system. +/// +/// # Invariants +/// +/// `self.family` always holds a valid reference to an initialized and registered [`Family`]. +pub struct Registration { + family: &'static Family, +} + +impl Family { + /// Registers the generic netlink family with the kernel. + pub fn register(&'static self) -> Result { + // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` struct. + // The `genl_family` struct is static, so it will outlive the registration. + to_result(unsafe { bindings::genl_register_family(self.as_raw()) })?; + Ok(Registration { family: self }) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `self.family.as_raw()` is a valid pointer to a registered `genl_family` struct. + // The `Registration` struct ensures that `genl_unregister_family` is called exactly once + // for this family when it goes out of scope. + unsafe { bindings::genl_unregister_family(self.family.as_raw()) }; + } +} + +#[macros::kunit_tests(rust_netlink)] +mod tests { + use super::*; + + #[test] + fn test_family_flags_bitfield() { + for netnsok in [false, true] { + for parallel_ops in [false, true] { + let mut b_fam = bindings::genl_family { + ..Default::default() + }; + b_fam.set_netnsok(if netnsok { 1 } else { 0 }); + b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 }); + + let c_bitfield = FamilyFlags { + netnsok, + parallel_ops, + } + .into_bitfield(); + + // SAFETY: The bit field is stored as u8. + let b_val: u8 = unsafe { core::mem::transmute(b_fam._bitfield_1) }; + // SAFETY: The bit field is stored as u8. + let c_val: u8 = unsafe { core::mem::transmute(c_bitfield) }; + assert_eq!(b_val, c_val); + } + } + } +} diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs index 5aa0ce9ba01b..dc40bfaa57e6 100644 --- a/rust/kernel/sync/poll.rs +++ b/rust/kernel/sync/poll.rs @@ -5,6 +5,7 @@ //! Utilities for working with `struct poll_table`. use crate::{ + alloc::AllocError, bindings, fs::File, prelude::*, @@ -13,8 +14,13 @@ CondVar, LockClassKey, // }, // + types::Opaque, // +}; +use core::{ + marker::PhantomData, + mem::ManuallyDrop, + ops::Deref, // }; -use core::{marker::PhantomData, ops::Deref}; /// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -70,6 +76,7 @@ pub fn register_wait(&self, file: &File, cv: &PollCondVar) { /// /// [`CondVar`]: crate::sync::CondVar #[pin_data(PinnedDrop)] +#[repr(transparent)] pub struct PollCondVar { #[pin] inner: CondVar, @@ -106,3 +113,67 @@ fn drop(self: Pin<&mut Self>) { synchronize_rcu(); } } + +/// A [`KBox`] that uses `kfree_rcu`. +/// +/// [`KBox`]: PollCondVar +pub struct PollCondVarBox { + inner: ManuallyDrop>>, +} + +#[pin_data] +#[repr(C)] +struct PollCondVarBoxInner { + #[pin] + inner: PollCondVar, + rcu: Opaque, +} + +// SAFETY: PollCondVar is Send +unsafe impl Send for PollCondVarBoxInner {} +// SAFETY: PollCondVar is Sync +unsafe impl Sync for PollCondVarBoxInner {} + +impl PollCondVarBox { + /// Constructs a new boxed [`PollCondVar`]. + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> Result { + let b = KBox::pin_init( + pin_init!(PollCondVarBoxInner { + inner <- PollCondVar::new(name, key), + rcu: Opaque::uninit(), + }), + GFP_KERNEL, + ) + .map_err(|_| AllocError)?; + + Ok(PollCondVarBox { + inner: ManuallyDrop::new(b), + }) + } +} + +impl Deref for PollCondVarBox { + type Target = PollCondVar; + fn deref(&self) -> &PollCondVar { + &self.inner.inner + } +} + +impl Drop for PollCondVarBox { + #[inline] + fn drop(&mut self) { + // SAFETY: ManuallyDrop::take ok because not already taken. + let boxed = unsafe { ManuallyDrop::take(&mut self.inner) }; + + // SAFETY: The code below frees the box without calling the actual destructor of the type, + // but it's okay because it re-implements the destructor using `kfree_rcu()` in place of + // `synchronize_rcu()`. + let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(boxed) }); + + // SAFETY: The pointer points at a valid `wait_queue_head`. + unsafe { bindings::__wake_up_pollfree((*ptr).inner.inner.wait_queue_head.get()) }; + + // SAFETY: This was allocated using `KBox::pin_init`, so it can be freed with `kvfree`. + unsafe { bindings::kvfree_call_rcu((*ptr).rcu.get(), ptr.cast::()) }; + } +} diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index c2b3457b700c..3336df493dec 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -210,6 +210,13 @@ pub fn pid(&self) -> Pid { unsafe { *ptr::addr_of!((*self.as_ptr()).pid) } } + /// Returns the TGID (Thread Group ID / Process ID) of the given task. + pub fn tgid(&self) -> Pid { + // SAFETY: The tgid of a task never changes after initialization, so reading this field is + // not a data race. + unsafe { *ptr::addr_of!((*self.as_ptr()).tgid) } + } + /// Returns the objective real UID of the given task. #[inline] pub fn uid(&self) -> Kuid { diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 1c4aa4292dce..489748ef642c 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py index 6a7ea4787aa1..d2ca842a7849 100644 --- a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py @@ -57,7 +57,7 @@ def parse_inputs_from_commands( try: inputs = matched_parser(single_command) input_files.extend(inputs) - except (CmdParsingError, IndexError) as e: + except (CmdParsingError, IndexError, ValueError) as e: log_error_or_warning( "Skipped parsing command {single_command} because of command parsing error: {error_message}", single_command=single_command, diff --git a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py index a061a748e1bf..d7776f072e03 100644 --- a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py +++ b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py @@ -19,6 +19,31 @@ class TestSavedCmdParser(unittest.TestCase): errors = sbom_logging._error_logger._message_counts # type: ignore self.assertEqual(errors, {}) + # Error handling tests + def test_malformed_shell_quoting(self): + command = 'gcc "unterminated' + with patch.object(sbom_logging, "warning") as warning: + parsed = parse_inputs_from_commands(command, fail_on_unknown_build_command=False) + + self.assertEqual(parsed, []) + warning.assert_called_once_with( + "Skipped parsing command {single_command} because of command parsing error: {error_message}", + single_command=command, + error_message="No closing quotation", + ) + + def test_missing_positional_argument(self): + command = "objcopy" + with patch.object(sbom_logging, "warning") as warning: + parsed = parse_inputs_from_commands(command, fail_on_unknown_build_command=False) + + self.assertEqual(parsed, []) + warning.assert_called_once_with( + "Skipped parsing command {single_command} because of command parsing error: {error_message}", + single_command=command, + error_message="list index out of range", + ) + # Compound command tests def test_dd_cat(self): cmd = "(dd if=arch/x86/boot/setup.bin bs=4k conv=sync status=none; cat arch/x86/boot/vmlinux.bin) >arch/x86/boot/bzImage" diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 964b9a5b2990..eef0af63e3d6 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -311,3 +311,4 @@ module_platform_driver(audio_iio_aux_driver); MODULE_AUTHOR("Herve Codina "); MODULE_DESCRIPTION("IIO ALSA SoC aux driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 6db91b73f25c..d8659973540f 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -701,3 +701,4 @@ module_platform_driver(aries_audio_driver); MODULE_DESCRIPTION("ALSA SoC ARIES WM8994"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:aries-audio-wm8994"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index bc34dbbb50c9..d43af5069baf 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -773,3 +773,4 @@ module_platform_driver(midas_driver); MODULE_AUTHOR("Simon Shields "); MODULE_DESCRIPTION("ASoC support for Midas"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 73c6460a6327..018f775f36bb 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -389,3 +389,4 @@ MODULE_DESCRIPTION("stm32 DFSDM DAI driver"); MODULE_AUTHOR("Arnaud Pouliquen "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index bc3ef4c77c2b..b5c5479b9d78 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -66,6 +66,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", + [IIO_VOLUMEFLOW] = "volumeflow", }; static const char * const iio_ev_type_text[] = { @@ -196,6 +197,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_ATTENTION: case IIO_ALTCURRENT: case IIO_COVERAGE: + case IIO_VOLUMEFLOW: break; default: return false; diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c index e6a37214aa46..1f0dc43bb4c0 100644 --- a/tools/testing/selftests/drivers/ntsync/ntsync.c +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c @@ -8,12 +8,18 @@ #define _GNU_SOURCE #include #include +#include #include +#include #include #include #include #include "kselftest_harness.h" +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + static int read_sem_state(int sem, __u32 *count, __u32 *max) { struct ntsync_sem_args args; @@ -968,7 +974,7 @@ TEST(wake_all) auto_event_args.manual = false; auto_event_args.signaled = true; objs[3] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args); - EXPECT_EQ(0, objs[3]); + EXPECT_LE(0, objs[3]); wait_args.timeout = get_abs_timeout(1000); wait_args.objs = (uintptr_t)objs; @@ -1340,4 +1346,129 @@ TEST(stress_wait) close(stress_device); } +TEST(wait_args_validation) +{ + struct ntsync_sem_args sem_args = { .count = 1, .max = 1 }; + struct ntsync_wait_args wait_args = {0}; + struct timespec timeout; + int fd, fd2, sem, ret; + __u32 index; + + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + ASSERT_GE(fd, 0); + + fd2 = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + ASSERT_GE(fd2, 0); + + sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args); + EXPECT_GE(sem, 0); + + ret = wait_any(fd, 1, &sem, 0, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + ret = wait_all(fd, 1, &sem, 0, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + clock_gettime(CLOCK_MONOTONIC, &timeout); + wait_args.timeout = timeout.tv_sec * 1000000000ULL + timeout.tv_nsec; + wait_args.count = 0; + wait_args.objs = 0; + wait_args.owner = 123; + wait_args.pad = 1; + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + ret = wait_any(fd2, 1, &sem, 123, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + close(sem); + close(fd2); + close(fd); +} + +/* + * Absolute MONOTONIC timeouts must honour the caller's time namespace. + * With a negative monotonic offset, a 100 ms wait must still take ~100 ms + * of namespace time (not return immediately against the host clock). + */ +TEST(wait_any_monotonic_timens) +{ + struct ntsync_sem_args sem_args = {0}; + struct ntsync_wait_args wait_args = {0}; + struct timespec start, end; + char buf[64]; + __u64 elapsed_ns; + int fd, offset_fd, sem, ret, status, len; + pid_t pid; + + if (access("/proc/self/ns/time", F_OK)) + SKIP(return, "Time namespaces are not supported"); + + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + if (fd < 0) + SKIP(return, "/dev/ntsync is not available"); + + ret = unshare(CLONE_NEWTIME); + if (ret) { + close(fd); + if (errno == EPERM) + SKIP(return, "need CAP_SYS_ADMIN for CLONE_NEWTIME"); + ASSERT_EQ(0, ret); + } + + len = snprintf(buf, sizeof(buf), "%d %d 0", CLOCK_MONOTONIC, -10); + offset_fd = open("/proc/self/timens_offsets", O_WRONLY); + ASSERT_LE(0, offset_fd); + ASSERT_EQ(len, write(offset_fd, buf, len)); + close(offset_fd); + + pid = fork(); + ASSERT_LE(0, pid); + if (!pid) { + int obj; + + sem_args.count = 0; + sem_args.max = 1; + sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args); + if (sem < 0) + _exit(1); + + obj = sem; + wait_args.timeout = get_abs_timeout(100); + wait_args.objs = (uintptr_t)&obj; + wait_args.count = 1; + wait_args.owner = 123; + wait_args.index = 0xdeadbeef; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) + _exit(2); + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args); + if (clock_gettime(CLOCK_MONOTONIC, &end)) + _exit(2); + + if (ret != -1 || errno != ETIMEDOUT) + _exit(3); + + elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL + + (end.tv_nsec - start.tv_nsec); + /* Without timens conversion this returns in ~0 ms. */ + if (elapsed_ns < 50 * 1000000ULL) + _exit(4); + if (elapsed_ns > 1000 * 1000000ULL) + _exit(5); + + _exit(0); + } + + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); + + close(fd); +} + TEST_HARNESS_MAIN