* [PATCH v2 1/2] dt-bindings: arm: aspeed: Add Meta Rainiera6 board
From: Neil Cheng @ 2026-05-19 2:38 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, joel, andrew, geert+renesas, magnus.damm
Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel,
linux-renesas-soc, Neil Cheng, Conor Dooley
In-Reply-To: <cover.1779157117.git.neilcheng0417@gmail.com>
Document the new compatibles used on Meta Rainiera6.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Neil Cheng <neilcheng0417@gmail.com>
---
Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
index 8ec7a3e74a21..1a2252eb08f1 100644
--- a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
+++ b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
@@ -95,6 +95,7 @@ properties:
- facebook,greatlakes-bmc
- facebook,harma-bmc
- facebook,minerva-cmc
+ - facebook,rainiera6-bmc
- facebook,santabarbara-bmc
- facebook,yosemite4-bmc
- facebook,yosemite5-bmc
--
2.25.1
^ permalink raw reply related
* [PATCH v2 2/2] ARM: dts: aspeed: rainiera6: Add Meta Rainiera6 BMC
From: Neil Cheng @ 2026-05-19 2:38 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, joel, andrew, geert+renesas, magnus.damm
Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel,
linux-renesas-soc, Neil Cheng
In-Reply-To: <cover.1779157117.git.neilcheng0417@gmail.com>
Add device tree for the Meta (Facebook) Rainiera6 compute node, based on
AST2600 BMC.
Signed-off-by: Neil Cheng <neilcheng0417@gmail.com>
---
arch/arm/boot/dts/aspeed/Makefile | 1 +
.../aspeed/aspeed-bmc-facebook-rainiera6.dts | 1012 +++++++++++++++++
2 files changed, 1013 insertions(+)
create mode 100644 arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-rainiera6.dts
diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile
index 767f7c7652d5..215429af1135 100644
--- a/arch/arm/boot/dts/aspeed/Makefile
+++ b/arch/arm/boot/dts/aspeed/Makefile
@@ -34,6 +34,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
aspeed-bmc-facebook-harma.dtb \
aspeed-bmc-facebook-minerva.dtb \
aspeed-bmc-facebook-minipack.dtb \
+ aspeed-bmc-facebook-rainiera6.dtb \
aspeed-bmc-facebook-santabarbara.dtb \
aspeed-bmc-facebook-tiogapass.dtb \
aspeed-bmc-facebook-wedge40.dtb \
diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-rainiera6.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-rainiera6.dts
new file mode 100644
index 000000000000..b29e08cea254
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-rainiera6.dts
@@ -0,0 +1,1012 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2026 Facebook Inc.
+
+/dts-v1/;
+#include "aspeed-g6.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>
+#include <dt-bindings/i2c/i2c.h>
+
+/ {
+ model = "Facebook Rainier BMC";
+ compatible = "facebook,rainiera6-bmc", "aspeed,ast2600";
+
+ aliases {
+ i2c16 = &i2c5mux0ch0;
+ i2c17 = &i2c5mux0ch1;
+ i2c18 = &i2c5mux1ch0;
+ i2c19 = &i2c5mux1ch1;
+ i2c20 = &i2c6mux0ch0;
+ i2c21 = &i2c6mux0ch1;
+ i2c22 = &i2c6mux0ch2;
+ i2c23 = &i2c6mux0ch3;
+ i2c24 = &i2c8mux0ch0;
+ i2c25 = &i2c8mux0ch1;
+ i2c26 = &i2c8mux0ch2;
+ i2c27 = &i2c8mux0ch3;
+ i2c28 = &i2c26mux0ch0;
+ i2c29 = &i2c26mux0ch1;
+ i2c30 = &i2c26mux0ch2;
+ i2c31 = &i2c26mux0ch3;
+ serial0 = &uart1;
+ serial2 = &uart3;
+ serial3 = &uart4;
+ serial4 = &uart5;
+ };
+
+ chosen {
+ stdout-path = "serial4:57600n8";
+ };
+
+ iio-hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>,
+ <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>,
+ <&adc1 2>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-0 {
+ label = "bmc_heartbeat_amber";
+ gpios = <&gpio0 ASPEED_GPIO(P, 7) GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ led-1 {
+ label = "fp_id_amber";
+ default-state = "off";
+ gpios = <&gpio0 ASPEED_GPIO(B, 5) GPIO_ACTIVE_HIGH>;
+ };
+
+ led-2 {
+ label = "power_fault_amber";
+ default-state = "off";
+ gpios = <&gpio0 ASPEED_GPIO(P, 4) GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x80000000 0x80000000>;
+ };
+
+ p1v8_adc_vref: regulator-p1v8-aux {
+ compatible = "regulator-fixed";
+ regulator-name = "p1v8_adc_vref";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ spi_gpio: spi {
+ compatible = "spi-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sck-gpios = <&gpio0 ASPEED_GPIO(Z, 3) GPIO_ACTIVE_HIGH>;
+ mosi-gpios = <&gpio0 ASPEED_GPIO(Z, 4) GPIO_ACTIVE_HIGH>;
+ miso-gpios = <&gpio0 ASPEED_GPIO(Z, 5) GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio0 ASPEED_GPIO(Z, 0) GPIO_ACTIVE_LOW>;
+ num-chipselects = <1>;
+ status = "okay";
+
+ tpm@0 {
+ compatible = "infineon,slb9670", "tcg,tpm_tis-spi";
+ spi-max-frequency = <33000000>;
+ reg = <0>;
+ };
+ };
+};
+
+&adc0 {
+ vref-supply = <&p1v8_adc_vref>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc0_default &pinctrl_adc1_default
+ &pinctrl_adc2_default &pinctrl_adc3_default
+ &pinctrl_adc4_default &pinctrl_adc5_default
+ &pinctrl_adc6_default &pinctrl_adc7_default>;
+ status = "okay";
+};
+
+&adc1 {
+ aspeed,int-vref-microvolt = <2500000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc10_default>;
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&fmc {
+ status = "okay";
+
+ flash@0 {
+ status = "okay";
+ m25p,fast-read;
+ label = "bmc";
+ spi-max-frequency = <50000000>;
+#include "openbmc-flash-layout-128.dtsi"
+ };
+
+ flash@1 {
+ status = "okay";
+ m25p,fast-read;
+ label = "alt-bmc";
+ spi-max-frequency = <50000000>;
+ };
+};
+
+&gpio0 {
+ gpio-line-names =
+ /*A0-A7*/ "","","","","","","","",
+ /*B0-B7*/ "BATTERY_DETECT","","BMC_I2C1_FPGA_ALERT_L","BMC_READY_R",
+ "IOEXP_INT_3V3_L_R1","FM_ID_LED","","",
+ /*C0-C7*/ "BMC_GPIOC0","","","","PMBUS_REQ_N","PSU_FW_UPDATE_REQ_N","",
+ "BMC_I2C_SSIF_ALERT_L",
+ /*D0-D7*/ "","","","","BMC_GPIOD4","","","",
+ /*E0-E7*/ "BMC_GPIOE0","BMC_GPIOE1","","","","","","",
+ /*F0-F7*/ "","","","","","","","",
+ /*G0-G7*/ "FM_BMC_MUX1_SEL_R","","","","","","FM_DEBUG_PORT_PRSNT_R1_N",
+ "FM_BMC_DBP_PRESENT_R_N",
+ /*H0-H7*/ "","","","","","","","",
+ /*I0-I7*/ "","","","","","FLASH_WP_STATUS_R1","BMC_JTAG_MUX_SEL","",
+ /*J0-J7*/ "","","","","","","","",
+ /*K0-K7*/ "","","","","","","","",
+ /*L0-L7*/ "","","","","","","","",
+ /*M0-M7*/ "PCIE_EP_RST_EN","BMC_FRU_WP","SCM_HPM_STBY_RST_N",
+ "SCM_HPM_STBY_R_EN","STBY_POWER_PG_3V3_R","TH500_SHDN_OK_L","","",
+ /*N0-N7*/ "LED_POSTCODE_0","LED_POSTCODE_1","LED_POSTCODE_2",
+ "LED_POSTCODE_3","LED_POSTCODE_4","LED_POSTCODE_5",
+ "LED_POSTCODE_6","LED_POSTCODE_7",
+ /*O0-O7*/ "RUN_POWER_PG","PWR_BRAKE_L","CHASSIS_AC_LOSS_L",
+ "BSM_PRSNT_R_N","PSU_SMB_ALERT_L","FM_TPM_PRSNT_0_N",
+ "PSU_FW_UPDATING_N","DEBUG_CARD_BYPASS",
+ /*P0-P7*/ "PWR_BTN_BMC_R1_N","IPEX_CABLE_PRSNT_L","ID_RST_BTN_BMC_R_N",
+ "RST_BMC_RSTBTN_OUT_R_N","BMC_PWR_LED","RUN_POWER_EN",
+ "SHDN_FORCE_L","BMC_HEARTBEAT_N",
+ /*Q0-Q7*/ "IRQ_PCH_TPM_SPI_LV3_N","USB_OC0_REAR_R_N","UART_MUX_SEL",
+ "I2C_MUX_RESET_L","RSVD_NV_PLT_DETECT","SPI_TPM_INT_L",
+ "CPU_JTAG_MUX_SELECT","THERM_BB_OVERT_L",
+ /*R0-R7*/ "THERM_BB_WARN_L","SPI_BMC_FPGA_INT_L","CPU_BOOT_DONE",
+ "PMBUS_GNT_L","CHASSIS_PWR_BRK_L","PCIE_WAKE_L","PDB_THERM_OVERT_L",
+ "SHDN_REQ_L",
+ /*S0-S7*/ "","","SYS_BMC_PWRBTN_R_N","FM_TPM_PRSNT_1_N",
+ "FM_BMC_DEBUG_SW_N","UID_LED_N","SYS_FAULT_LED_N",
+ "RUN_POWER_FAULT_L",
+ /*T0-T7*/ "","","","","","","","",
+ /*U0-U7*/ "","","","","","","","",
+ /*V0-V7*/ "L2_RST_REQ_OUT_L","L0L1_RST_REQ_OUT_L","BMC_ID_BEEP_SEL_R1",
+ "BMC_I2C0_FPGA_ALERT_L","SMB_BMC_TMP_ALERT","PWR_LED_N",
+ "SYS_RST_OUT_L","IRQ_TPM_SPI_N",
+ /*W0-W7*/ "","","","","","","","",
+ /*X0-X7*/ "","FM_DBP_CPU_PREQ_GF_N_R1","","","","","","",
+ /*Y0-Y7*/ "","RST_BMC_SELF_HW_R1","FM_FLASH_LATCH_N_R1","",
+ "BMC_GPIOY4_R","BMC_GPIOY5_R","","",
+ /*Z0-Z7*/ "","","","","","","BMC_GPIOZ6_R","BMC_GPIOZ7_R";
+};
+
+&gpio1 {
+ gpio-line-names =
+ /*18A0-18A7*/ "","","","","","","","",
+ /*18B0-18B7*/ "","","","","FM_BOARD_BMC_REV_ID0","FM_BOARD_BMC_REV_ID1",
+ "FM_BOARD_BMC_REV_ID2","",
+ /*18C0-18C7*/ "","","SPI_BMC_BIOS_ROM_IRQ0_R_N","","","","","",
+ /*18D0-18D7*/ "","","","","","","","",
+ /*18E0-18E3*/ "FM_BMC_PROT_LS_EN","AC_PWR_BMC_BTN_R_N","","";
+};
+
+/* Rainiera6 SoC SSIF */
+&i2c1 {
+ status = "okay";
+
+ ssif_bmc: ssif-bmc@10 {
+ compatible = "ssif-bmc";
+ reg = <0x10>;
+ status = "okay";
+ };
+};
+
+/* MCIO 2A I2C */
+&i2c2 {
+ status = "okay";
+};
+
+&i2c4 {
+ multi-master;
+ mctp-controller;
+ clock-frequency = <400000>;
+ status = "okay";
+
+ mctp@10 {
+ compatible = "mctp-i2c-controller";
+ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
+ };
+
+ /* OCP NIC TEMP */
+ temperature-sensor@1f {
+ compatible = "ti,tmp421";
+ reg = <0x1f>;
+ };
+
+ /* OCP NIC FRU EEPROM */
+ eeprom@50 {
+ compatible = "atmel,24c64";
+ reg = <0x50>;
+ };
+};
+
+&i2c5 {
+ status = "okay";
+
+ gpio-expander@22 {
+ compatible = "nxp,pca9535";
+ reg = <0x22>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "JTAG_BMC_IOEXP_MUX_OE", "JTAG_BMC_MCIO_MUX_S1",
+ "JTAG_BMC_MCIO_MUX_S0", "JTAG_IOEXP_BMC_MUX_SEL",
+ "FM_USB_MUX_1_OE_N", "FM_USB_MUX_2_OE_N",
+ "PROGRAMN_CPLD", "",
+ "", "",
+ "", "",
+ "", "",
+ "", "";
+ };
+
+ /* I2C MUX for MCIO 1A */
+ i2c-mux@70 {
+ compatible = "nxp,pca9546";
+ reg = <0x70>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ i2c5mux0ch0: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c5mux0ch1: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
+ /* I2C MUX for MCIO 0A */
+ i2c-mux@77 {
+ compatible = "nxp,pca9546";
+ reg = <0x77>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ i2c5mux1ch0: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c5mux1ch1: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+};
+
+&i2c6 {
+ status = "okay";
+
+ /* I2C MUX for PWRPIC #13 ~ #16 */
+ i2c-mux@77 {
+ compatible = "nxp,pca9546";
+ reg = <0x77>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ /* PWRPIC #13 */
+ i2c6mux0ch0: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* PWRPIC #14 */
+ i2c6mux0ch1: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* PWRPIC #16 */
+ i2c6mux0ch2: i2c@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* PWRPIC #15 */
+ i2c6mux0ch3: i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+};
+
+&i2c7 {
+ multi-master;
+ status = "okay";
+
+ ipmb@10 {
+ compatible = "ipmb-dev";
+ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
+ i2c-protocol;
+ };
+};
+
+&i2c8 {
+ status = "okay";
+
+ power-monitor@14 {
+ compatible = "infineon,xdp710";
+ reg = <0x14>;
+ };
+
+ adc@1d {
+ compatible = "ti,adc128d818";
+ reg = <0x1d>;
+ ti,mode = /bits/ 8 <1>;
+ };
+
+ /* PDB IOEXP0 */
+ pdb_io_expander0: gpio-expander@24 {
+ compatible = "nxp,pca9555";
+ reg = <0x24>;
+ interrupt-parent = <&sgpiom0>;
+ interrupts = <92 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "FM_P52V_AUX_FLT_N","FM_P12V_AUX_ALERT_N",
+ "FM_SLOT1_HSC_FAULT","FM_SLOT2_HSC_FAULT",
+ "FM_SLOT3_HSC_FAULT","FM_SLOT4_HSC_FAULT",
+ "FM_SLOT5_HSC_FAULT","FM_SLOT6_HSC_FAULT",
+ "PRSNT_FAN0","PRSNT_FAN1",
+ "PRSNT_FAN2","PRSNT_FAN3",
+ "","",
+ "","INT_SLOT";
+ };
+
+ /* PDB IOEXP1 */
+ gpio-expander@25 {
+ compatible = "nxp,pca9555";
+ reg = <0x25>;
+ interrupt-parent = <&pdb_io_expander0>;
+ interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "SLOT1_CM_UPDATE","SLOT2_CM_UPDATE",
+ "SLOT3_CM_UPDATE","SLOT4_CM_UPDATE",
+ "SLOT5_CM_UPDATE","SLOT6_CM_UPDATE",
+ "","",
+ "","",
+ "","",
+ "","",
+ "","";
+ };
+
+ power-sensor@40 {
+ compatible = "ti,ina238";
+ reg = <0x40>;
+ shunt-resistor = <1000>;
+ };
+
+ /* PADDLE BD IOEXP */
+ gpio-expander@41 {
+ compatible = "nxp,pca9536";
+ reg = <0x41>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "HSC_OC_GPIO0", "HSC_OC_GPIO1",
+ "HSC_OC_GPIO2", "HSC_OC_GPIO3";
+ };
+
+ power-sensor@42 {
+ compatible = "ti,ina238";
+ reg = <0x42>;
+ shunt-resistor = <1000>;
+ };
+
+ power-monitor@43 {
+ compatible = "lltc,ltc4287";
+ reg = <0x43>;
+ shunt-resistor-micro-ohms = <100>;
+ };
+
+ power-sensor@44 {
+ compatible = "ti,ina238";
+ reg = <0x44>;
+ shunt-resistor = <1000>;
+ };
+
+ power-sensor@45 {
+ compatible = "ti,ina238";
+ reg = <0x45>;
+ shunt-resistor = <1000>;
+ };
+
+ power-monitor@46 {
+ compatible = "mps,mp5998";
+ reg = <0x46>;
+ };
+
+ power-monitor@47 {
+ compatible = "ti,tps25990";
+ reg = <0x47>;
+ ti,rimon-micro-ohms = <430000000>;
+ };
+
+ temperature-sensor@48 {
+ compatible = "ti,tmp75";
+ reg = <0x48>;
+ };
+
+ temperature-sensor@49 {
+ compatible = "ti,tmp75";
+ reg = <0x49>;
+ };
+
+ /* PDB FRU */
+ eeprom@56 {
+ compatible = "atmel,24c128";
+ reg = <0x56>;
+ };
+
+ /* Paddle BD FRU */
+ eeprom@57 {
+ compatible = "atmel,24c128";
+ reg = <0x57>;
+ };
+
+ power-monitor@58 {
+ compatible = "renesas,isl28022";
+ reg = <0x58>;
+ shunt-resistor-micro-ohms = <10000>;
+ };
+
+ power-monitor@59 {
+ compatible = "renesas,isl28022";
+ reg = <0x59>;
+ shunt-resistor-micro-ohms = <10000>;
+ };
+
+ power-monitor@5a {
+ compatible = "renesas,isl28022";
+ reg = <0x5a>;
+ shunt-resistor-micro-ohms = <10000>;
+ };
+
+ power-monitor@5b {
+ compatible = "renesas,isl28022";
+ reg = <0x5b>;
+ shunt-resistor-micro-ohms = <10000>;
+ };
+
+ psu@5c {
+ compatible = "renesas,raa228006";
+ reg = <0x5c>;
+ };
+
+ fan-controller@5e{
+ compatible = "maxim,max31790";
+ reg = <0x5e>;
+ };
+
+ /* I2C MUX for PWRPIC #1, #2, #11, #12 */
+ i2c-mux@77 {
+ compatible = "nxp,pca9546";
+ reg = <0x77>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ /* PWRPIC #1 */
+ i2c8mux0ch0: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* PWRPIC #2 */
+ i2c8mux0ch1: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* PWRPIC #12 (Connector to CXL BD) */
+ i2c8mux0ch2: i2c@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux@70 {
+ compatible = "nxp,pca9546";
+ reg = <0x70>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+ i2c26mux0ch0: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ i2c26mux0ch1: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ i2c26mux0ch2: i2c@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ /* CXL FRU */
+ eeprom@50 {
+ compatible = "atmel,24c64";
+ reg = <0x50>;
+ };
+ };
+ i2c26mux0ch3: i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+ };
+
+ /* PWRPIC #11 */
+ i2c8mux0ch3: i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+};
+
+&i2c9 {
+ status = "okay";
+
+ temperature-sensor@4b {
+ compatible = "ti,tmp75";
+ reg = <0x4b>;
+ };
+
+ /* SCM FRU */
+ eeprom@51 {
+ compatible = "atmel,24c128";
+ reg = <0x51>;
+ };
+
+ /* BSM FRU */
+ eeprom@56 {
+ compatible = "atmel,24c64";
+ reg = <0x56>;
+ };
+};
+
+/* MCIO 0A I2C */
+&i2c10 {
+ status = "okay";
+};
+
+&i2c11 {
+ status = "okay";
+
+ /* I2C11_IOEXP_3 */
+ gpio-expander@20 {
+ compatible = "nxp,pca9535";
+ reg = <0x20>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "IRQ_INA230_FAN0_ALERT_N_CPLD", "IRQ_INA230_FAN1_ALERT_N_CPLD",
+ "IRQ_INA230_FAN2_ALERT_N_CPLD", "IRQ_INA230_FAN3_ALERT_N_CPLD",
+ "IRQ_INA230_P12V_DIMM_0_ALERT_N", "IRQ_INA230_P12V_DIMM_1_ALERT_N",
+ "IRQ_P3V3_E1S_0_FLT_N", "P12V_E1S_0_FAULT_R_N",
+ "IRQ_P3V3_E1S_1_FLT_N", "P12V_E1S_1_FAULT_R_N",
+ "IRQ_P3V3_NIC_FLT_N", "P12V_NIC_FAULT_R_N",
+ "SMB_SENSOR_ALERT_N", "FW_CPLD_RST_RTC_RST_R1",
+ "RTC_CLR", "RTC_U11_ALRT_N";
+ };
+
+ /* I2C11_IOEXP_2 */
+ gpio-expander@21 {
+ compatible = "nxp,pca9535";
+ reg = <0x21>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "FAN_0_PRESENT_CPLD", "FAN_1_PRESENT_CPLD",
+ "FAN_2_PRESENT_CPLD", "FAN_3_PRESENT_CPLD",
+ "FAN_FAIL_L_CPLD", "FULL_SPEED_N_R_CPLD",
+ "P12V_FAN0_PWRGD_CPLD", "P12V_FAN1_PWRGD_CPLD",
+ "P12V_FAN2_PWRGD_CPLD", "P12V_FAN3_PWRGD_CPLD",
+ "FM_P12V_FAN0_FLTB_N_CPLD", "FM_P12V_FAN1_FLTB_N_CPLD",
+ "FM_P12V_FAN2_FLTB_N_CPLD", "FM_P12V_FAN3_FLTB_N_CPLD",
+ "P12V_FAN_EN_R_CPLD", "";
+ };
+
+ /* I2C11_IOEXP_1 */
+ gpio-expander@27 {
+ compatible = "nxp,pca9535";
+ reg = <0x27>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "PWRGD_P12V_SCM", "PWRGD_P5V_STBY",
+ "PWRGD_P3V3_STBY", "PWRGD_P1V8_STBY",
+ "PWRGD_P1V2_STBY", "PWRGD_P1V1_STBY",
+ "PWRGD_P1V0_STBY", "",
+ "", "",
+ "", "",
+ "", "",
+ "", "";
+ };
+
+ power-sensor@40 {
+ compatible = "ti,ina230";
+ reg = <0x40>;
+ shunt-resistor = <1000>;
+ };
+
+ power-sensor@41 {
+ compatible = "ti,ina230";
+ reg = <0x41>;
+ shunt-resistor = <1000>;
+ };
+
+ power-sensor@42 {
+ compatible = "ti,ina230";
+ reg = <0x42>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@43 {
+ compatible = "ti,ina230";
+ reg = <0x43>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@44 {
+ compatible = "ti,ina230";
+ reg = <0x44>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@45 {
+ compatible = "ti,ina230";
+ reg = <0x45>;
+ shunt-resistor = <2000>;
+ };
+
+ adc@49 {
+ compatible = "ti,ads7830";
+ reg = <0x49>;
+ };
+
+ adc@4a {
+ compatible = "ti,ads7830";
+ reg = <0x4a>;
+ };
+
+ adc@4b {
+ compatible = "ti,ads7830";
+ reg = <0x4b>;
+ };
+ rtc@6f {
+ compatible = "nuvoton,nct3018y";
+ reg = <0x6f>;
+ status = "okay";
+ };
+};
+
+/* MCIO 4A I2C */
+&i2c12 {
+ multi-master;
+ mctp-controller;
+ clock-frequency = <400000>;
+ status = "okay";
+
+ mctp@10 {
+ compatible = "mctp-i2c-controller";
+ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
+ };
+};
+
+&i2c13 {
+ status = "okay";
+
+ fan-controller@20 {
+ compatible = "maxim,max31790";
+ reg = <0x20>;
+ };
+
+ power-sensor@40 {
+ compatible = "ti,ina230";
+ reg = <0x40>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@41 {
+ compatible = "ti,ina230";
+ reg = <0x41>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@44 {
+ compatible = "ti,ina230";
+ reg = <0x44>;
+ shunt-resistor = <2000>;
+ };
+
+ power-sensor@45 {
+ compatible = "ti,ina230";
+ reg = <0x45>;
+ shunt-resistor = <2000>;
+ };
+
+ temperature-sensor@48 {
+ compatible = "national,lm75b";
+ reg = <0x48>;
+ };
+
+ temperature-sensor@49 {
+ compatible = "national,lm75b";
+ reg = <0x49>;
+ };
+
+ /* MB FRU */
+ eeprom@51 {
+ compatible = "atmel,24c128";
+ reg = <0x51>;
+ };
+};
+
+/* PROT reserve */
+&i2c14 {
+ status = "okay";
+};
+
+/* MCIO 3A I2C */
+&i2c15 {
+ status = "okay";
+};
+
+&mac2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ncsi3_default>;
+ use-ncsi;
+ status = "okay";
+};
+
+&pinctrl {
+ pinctrl_ncsi3_default: ncsi3_default {
+ function = "RMII3";
+ groups = "NCSI3";
+ };
+};
+
+&sgpiom0 {
+ status = "okay";
+ ngpios = <128>;
+ bus-frequency = <2000000>;
+ gpio-line-names =
+ /*"input pin","output pin"*/
+ "SOC_ERROR_N_CPLD","RST_SOC_SRST_N_CPLD",
+ "SOC_ELASTOPCLK_N","SOC_BMC_READY_CPLD",
+ "SOC_LINKSTOP_N","wIBB_BMC_SRST_OUT",
+ "SOC_POST_COMPLETE_CPLD","I3C_MUX_SEL_DIMM_C0_024",
+ "SOC_RESETREQ","I3C_MUX_SEL_DIMM_C0_135",
+ "SOC_SYS_PWRDN_CPLD","I3C_MUX_SEL_DIMM_C1_024",
+ "SOC_PORQ","I3C_MUX_SEL_DIMM_C1_135",
+ "SOC_HOT_N_CPLD","BOOT_PWRDIS_CPLD",
+ // IOB0-IOB7 bit8-15
+ "PWRGD_V1P8_CPU","HDD0_PERST_N_CPLD",
+ "PWRGD_V1P26_CPLD","HDD1_PERST_N_CPLD",
+ "PWRGD_V1P2","MCIO_1A_PWRDIS_R",
+ "","",
+ "FM_CONFIG_ID","P3V_BAT_SCALED_EN",
+ "wALL_POWER_OK","PERST_CEM0_N_CPLD",
+ "wANDGATE_ALL_POWER_GD","PERST_CEM1_N_CPLD",
+ "wAC_CYCLE_12V","PERST_PLD_TUSB7340_N",
+ // IOC0-IOC7 bit16-23
+ "wAC_CYCLE_54V","FM_USB_MUX_SEL_CPLD",
+ "FM_PLD_CLKS_DEV_EN","SMB_BOOT_RST_N_CPLD",
+ "PWRGD_P1V2_STBY","SMB_MCIO_0A_RST_R_N",
+ "wIBB_BMC_SRST","RST_SMB_NIC_R_N",
+ "PWRGD_P12V_E1S_0","FM_PPS_NIC_IN_BUF_OE_N_R",
+ "PWRGD_P12V_E1S_1","FM_BUF_PPS_NIC_IN_EN_CPLD",
+ "","FM_NIC_PPS_IN_OE_CPLD",
+ "PWRGD_P12V_NIC","FM_PPS_NIC_IN_S0_CPLD",
+ // IOD0-IOD7 bit24-31
+ "wALL_POWER_OK_1","FM_NIC_PPS_IN_S1",
+ "wALL_POWER_OK_2","FM_PPS_NIC_OUT_CPU_OE_N",
+ "PWRGD_EAST_DIMM_CPLD","",
+ "PWRGD_WEST_DIMM_CPLD","FM_BUF_PPS_NIC_OUT_EN_CPLD",
+ "PWRGD_NIC_CPLD","",
+ "","PMBUS_MUX_SEL_C0",
+ "PHOENIX_PWRBTN_N_CPLD","PMBUS_MUX_SEL_C1",
+ "IRQ_INA230_E1S_0_ALERT_N","",
+ // IOE0-IOE7 bit32-39
+ "IRQ_INA230_E1S_1_ALERT_N","PWR_ON_RST_TUSB7340_CPLD",
+ "","RST_PCIE_BOOT_PERST_N_CPLD",
+ "FM_NIC_WAKE_N_CPLD","RST_PCIE_CPLD_NIC_N_CPLD",
+ "FM_TPM_CONN_PRSNT_N","RST_PCIE_MCIO_0A_PERST_N_CPLD",
+ "HDD0_PRSNT_N_CPLD","RST_PCIE_MCIO_0B_PERST_N_CPLD",
+ "IRQ_INA230_P12V_NIC_ALERT_N","RST_PCIE_MCIO_1A_PERST_N_CPLD",
+ "IRQ_INA230_P12V_SCM_ALERT_N","RST_PCIE_MCIO_1A_SA_PERST_N_CPL",
+ "IRQ_PMBUS_ALERT_PWR11_R_N","RST_PCIE_MCIO_1B_PERST_N_CPLD",
+ // IOF0-IOF7 bit40-47
+ "CHASSIS_LEAK_2A_R_N","RST_PCIE_MCIO_2A_PERST_N_CPLD",
+ "CHASSIS_LEAK_3A_R_N","RST_PCIE_MCIO_2B_PERST_N_CPLD",
+ "CHASSIS_LEAK_4A_R_N","RST_PCIE_MCIO_3A_PERST_N_CPLD",
+ "OC_ALERT_PADDLE_R_N","RST_PCIE_MCIO_3B_PERST_N_CPLD",
+ "OC_ALERT_PWR2_R_N","RST_PCIE_MCIO_4A_PERST_N_CPLD",
+ "OC_ALERT_PWR11_R_N","RST_PCIE_MCIO_4B_PERST_N_CPLD",
+ "FM_IOE_ALT_N","RST_PERST1_N_CPLD",
+ "LEAK_DETECT_1_PWR14_R_N","RST_PERST2_N_CPLD",
+ // IOG0-IOG7 bit48-55
+ "LEAK_DETECT_2_PWR14_R_N","RST_PERST3_N_CPLD",
+ "LEAK_DETECT_1_PWR15_R_N","RST_SMB_MUX_MCIO_0A_R_N",
+ "LEAK_DETECT_2_PWR15_R_N","RST_SMB_MUX_MCIO_1A_R_N",
+ "MCIO_0A_SMB_ALERT_N","RST_SOC_EXTWARMRESET_CPLD",
+ "MCIO_1A_SMB_ALERT_N","RST_SOC_PORESET_N_BMC",
+ "MCIO_2A_SMB_ALERT_N","RST_USB_HUB_R_N",
+ "MCIO_2B_SMB_ALERT_N","SMB_MM7_MUX_RESET_N",
+ "MCIO_3A_SMB_ALERT_N","SMB_MUX_RESET_N_CPLD",
+ // IOH0-IOH7 bit56-63
+ "MCIO_3B_SMB_ALERT_N","SOC_I2C_0_ALERT_CPLD",
+ "MCIO_4A_SMB_ALERT_N","SOC_LINKSTOP_OUT_N",
+ "MCIO_4B_SMB_ALERT_N","SPI_TPM_RST_R_N",
+ "MCIO_1A_THERMTRIP_N","",
+ "MCIO_2A_THERMTRIP_N","",
+ "MCIO_3A_THERMTRIP_N","",
+ "MCIO_4A_THERMTRIP_N_CPLD","",
+ "UV_ALERT_PADDLE_R_N","wFM_USB_MUX_OE_N",
+ // IOI0-IOI7 bit64-71
+ "UV_ALERT_PWR2_R_N","wFM_USB_MUX_SEL",
+ "UV_ALERT_PWR11_R_N","",
+ "SOC_PMBUS_0_ALERT_R_CPLD","FM_BIOS_DEBUG_MODE_N",
+ "HDD1_PRSNT_N_CPLD","",
+ "","",
+ "SOC_DRAM_0_HOT_N_CPLD","",
+ "SOC_DRAM_1_HOT_N_CPLD","RST_PLTRST_PLD_B_N",
+ "SOC_DRAM_2_HOT_N_CPLD","FM_TPM_MUX6_SEL",
+ // IOJ0-IOJ7 bit72-79
+ "SOC_DRAM_3_HOT_N_CPLD","CPLD_MUX6_EN_N",
+ "IRQ_P3V3_NIC_FLT_MOS_N_CPLD","",
+ "VRHOT_V0P75_PCIE_VDDQ_N","",
+ "P12V_SCM_FAULT_R_N","",
+ "SOC_I2C_1_ALERT_CPLD","",
+ "","",
+ "SOC_PLATHOT_N_CPLD","",
+ "SOC_THRMTRIP_N_CPLD","",
+ // IOK0-IOK7 bit80-87
+ "VRHOT_VCPUC1_VCPUMC1_N","",
+ "VRHOT_VSYSC0_VSOCC0_N","",
+ "VRHOT_VSYSC1_VSOCC1_N","",
+ "VRHOT_VCPUC0_VCPUMC0_N","",
+ "","",
+ "INT_IOEXP_N","",
+ "RSVD_IOEXP_0A_SB1_R","",
+ "RSVD_IOEXP_0A_SB2_R","",
+ // IOL0-IOL7 bit88-95
+ "IRQ_PMBUS_PWR2_ALERT_R_N","",
+ "FM_BORD_REV_ID0","",
+ "FM_BORD_REV_ID1","",
+ "FM_BORD_REV_ID2","",
+ "FM_VR_TYPE_0","",
+ "FM_VR_TYPE_1","",
+ "","",
+ "MCIO_0B_SMB_ALERT_N","",
+ // IOM0-IOM7 bit96-103
+ "MCIO_1B_SMB_ALERT_N","",
+ "PRSNT_BOOT_N","",
+ "PRSNT_MCIO_1A_N","",
+ "wPRSNT_NIC_N","",
+ "","",
+ "SOC_TEST_MODE0","",
+ "PWRGD_V0P75_PCIE","",
+ "PWRGD_VDDQ","",
+ // ION0-ION7 bit104-111
+ "PWRGD_VCPUC0","",
+ "PWRGD_VCPUMC0","",
+ "PWRGD_VCPUMC1","",
+ "PWRGD_VCPUC1","",
+ "PWRGD_VSYSC0","",
+ "PWRGD_VSOCC0","",
+ "PWRGD_VSYSC1","",
+ "PWRGD_VSOCC1","",
+ // IOO0-IOO7 bit112-119
+ "SOC_PMBUS_1_ALERT_R_CPLD","",
+ "SOC_GPIO_15","",
+ "C0_POSTCODE_0_CPLD","",
+ "C0_POSTCODE_1_CPLD","",
+ "C0_POSTCODE_2_CPLD","",
+ "C0_POSTCODE_3_CPLD","",
+ "C0_POSTCODE_4_CPLD","",
+ "C1_POSTCODE_0_CPLD","",
+ // IOP0-IOP7 bit 120-127
+ "C1_POSTCODE_1_CPLD","",
+ "C1_POSTCODE_2_CPLD","",
+ "C1_POSTCODE_3_CPLD","",
+ "C1_POSTCODE_4_CPLD","",
+ "","",
+ "SOC_GPIO_17","",
+ "SOC_GPIO_18","",
+ "SOC_GPIO_37","";
+};
+
+/* BIOS Flash */
+&spi2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi2_default>;
+ status = "okay";
+
+ flash@0 {
+ m25p,fast-read;
+ label = "pnor";
+ spi-max-frequency = <12000000>;
+ spi-tx-bus-width = <2>;
+ spi-rx-bus-width = <2>;
+ status = "okay";
+ };
+};
+
+&uart1 {
+ status = "okay";
+};
+
+&uart2 {
+ status = "okay";
+};
+
+/* SOL */
+&uart3 {
+ status = "okay";
+};
+
+&uart4 {
+ status = "okay";
+};
+
+/* BMC Console */
+&uart5 {
+ status = "okay";
+};
+
+&wdt1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_wdtrst1_default>;
+ aspeed,reset-type = "soc";
+ aspeed,external-signal;
+ aspeed,ext-push-pull;
+ aspeed,ext-active-high;
+ aspeed,ext-pulse-duration = <256>;
+ status = "okay";
+};
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v7 0/3] Mediatek MT8189 JPEG support
From: Jianhua Lin (林建华) @ 2026-05-19 2:42 UTC (permalink / raw)
To: robh@kernel.org, matthias.bgg@gmail.com, mchehab@kernel.org,
nicolas@ndufresne.ca, conor+dt@kernel.org, krzk+dt@kernel.org,
AngeloGioacchino Del Regno
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
Vince-WL Liu (劉文龍),
linux-media@vger.kernel.org, devicetree@vger.kernel.org,
Jh Hsu (許希孜),
Project_Global_Chrome_Upstream_Group,
linux-arm-kernel@lists.infradead.org,
Sirius Wang (王皓昱)
In-Reply-To: <5b4cf8f7951cc2766901b09f2e886ef73d48671b.camel@ndufresne.ca>
Hi Nicolas
This series depends on commit 7560349ee0d9 ("media: mediatek: jpeg:
support 34bits"), which introduced the 'support_34bit' field in 'struct
mtk_jpeg_variant'. That commit has already been merged via the media
tree and is present in linux-next as of next-20260410.Therefore, this
series is based on linux-next/master rather than media-committers/next
to ensure the dependency is available.
The dt-bindings schema was validated against linux-next tag next-
20260410. JPEG encoder and decoder functionality was verified on MT8189
hardware.
Regards,
Jianhua Lin
On Fri, 2026-04-17 at 09:30 -0400, Nicolas Dufresne wrote:
> Hi,
>
> Le vendredi 17 avril 2026 à 18:05 +0800, Jianhua Lin a écrit :
> > This series is based on tag: next-20260410, linux-next/master
>
> What dependencies justify not submitting based on media-
> committers/next as usual
> ? Its fine to say you tested against linux-next of course, and if its
> only
> working there, its really nice to explain why.
>
> Nicolas
>
> >
> > Changes compared with v6:
> > - Patches 1/3 (dt-bindings: decoder):
> > update the existing `allOf` condition for mediatek,mt8189-jpgdec
> > to
> > make the 'mediatek,larb' property strictly required for MT8189
> > SoC.
> > - Patches 2/3 (dt-bindings: encoder):
> > Add an `allOf` condition to enforce that the `mediatek,larb`
> > property
> > is strictly required when the compatible string contains
> > mediatek,mt8189-jpgenc.
> >
> > Changes compared with v5:
> > - Patches 1/3 (dt-bindings: decoder):
> > - Drop top-level minItems/maxItems for clock-names per
> > Krzysztof's
> > review.
> > - Refine allOf block to strictly enforce clock constraints.
> >
> > Changes compared with v4:
> > - Refines the device tree bindings for JPEG decoder and encoder.
> > - Patches 1/3 (dt-bindings: decoder):
> > Moved the standalone compatible string mediatek,mt8189-jpgdec
> > into the first oneOf entry along with mt2701 and mt8173, as
> > suggested by Rob Herring. This correctly groups all independent
> > ICs and removes the redundant items wrapper.
> > - Patches 2/3 (dt-bindings: encoder):
> > Applied the same logic suggested by Rob Herring to the encoder
> > binding. Restructured the compatible property to clearly
> > distinguish between the standalone IC (mediatek,mt8189-jpgenc)
> > and the ICs that must fallback to mediatek,mtk-jpgenc.
> >
> > Changes compared with v3:
> > - The v4 is resending the cover-letter, because the v3 cover-letter
> > was
> > not sent successfully.
> >
> > Changes compared with v2:
> > - Dropped the dts patch (arm64: dts: mt8188: update JPEG
> > encoder/decoder
> > compatible) as it belongs to a different tree/series.
> > - Patches 1/3 (dt-bindings: decoder):
> > - Changed the MT8189 compatible to be a standalone `const`
> > instead of
> > an `enum`.
> > - Added an `allOf` block with conditional checks to enforce the
> > single
> > clock ("jpgdec") requirement for MT8189, while preserving the
> > two-clock requirement for older SoCs.
> > - Updated commit message to reflect the schema structure changes
> > and
> > hardware differences.
> > - Patches 2/3 (dt-bindings: encoder):
> > - Changed the MT8189 compatible to be a standalone `const`
> > instead of
> > an `enum` inside the `items` list, as it does not fallback to
> > "mediatek,mtk-jpgenc" due to 34-bit IOVA requirements.
> > - Updated commit message to explain the standalone compatible
> > design.
> > - Patches 3/3 (media: mediatek: jpeg):
> > - Refined commit message for better clarity regarding 34-bit IOVA
> > and
> > single clock configuration.
> >
> > Changes compared with v1:
> > - Patches 1/4:
> > - Updating commit message
> > - Patches 2/4, 3/4:
> > - Updating commit message
> > - Adjusted property descriptions acorrding to hardware
> > requirements
> > - Improved formatting for better readability and consistency
> > - Patches 4/4:
> > - Updating commit message
> >
> > Jianhua Lin (3):
> > dt-bindings: media: mediatek-jpeg-decoder: add MT8189 compatible
> > string
> > dt-bindings: media: mediatek-jpeg-encoder: add MT8189 compatible
> > string
> > media: mediatek: jpeg: add compatible for MT8189 SoC
> >
> > .../bindings/media/mediatek-jpeg-decoder.yaml | 48
> > +++++++++++++++----
> > .../bindings/media/mediatek-jpeg-encoder.yaml | 29 ++++++++---
> > .../platform/mediatek/jpeg/mtk_jpeg_core.c | 44
> > +++++++++++++++++
> > 3 files changed, 107 insertions(+), 14 deletions(-)
^ permalink raw reply
* Re: [PATCH v4 2/2] ARM: dts: aspeed: ventura2: Add Meta ventura2 BMC
From: Kyle Hsieh @ 2026-05-19 2:48 UTC (permalink / raw)
To: Andrew Jeffery
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
devicetree, linux-arm-kernel, linux-aspeed, linux-kernel
In-Reply-To: <CAF7HswPn65kvTAyYDj_o=nSn3YhQhKqBaak7nt11x-8bZ7M6vg@mail.gmail.com>
Hi Andrew,
On Tue, May 19, 2026 at 10:38 AM Kyle Hsieh <kylehsieh1995@gmail.com> wrote:
>
> On Mon, May 18, 2026 at 3:18 PM Andrew Jeffery
> <andrew@codeconstruct.com.au> wrote:
> >
> > Hi Kyle,
> >
> > Firstly, are you trying to represent multiple revisions of the hardware
> > design in this devicetree? I'm curious due to the 'legacy' labels
> > below.
> >
> In the previous Ventura hardware generation, these pins were
> implemented as a set of direct, native physical GPIO signals. In the V2
> design, we introduced alternative interfaces and routed these paths
> through the CPLD to convert them into GPIOs before reaching the
> BMC.
>
> We chose to retain the 'legacy' prefix to maintain backward
> compatibility with our existing userspace software stack and scripts
> that transitioned from the previous Ventura platform. Altering these
> labels now would break compatibility with applications that rely on
> these specific naming conventions. I will add comments in the DTS to
> clarify this context.
Please allow me to add a crucial hardware detail to my previous
explanation regarding the 'legacy' prefix.
I missed mentioning the most important point: on this specific ventura2
board, we actually have two distinct sets of IO expanders physically
present at the same time to support both new and older tray interfaces.
The new design utilizes the PCA9698 at gpio@40, which is already
labeled as prsnt_io_expander0. However, to maintain hardware
backward compatibility with older trays, the board also physically
retains the previous PCA9555 expanders at gpio@11.
Therefore, since the prsnt_io_expander0 label is already occupied, the
legacy_ prefix is strictly necessary to avoid
devicetree label collisions and to explicitly differentiate the two
physical hardware paths.
I will make sure this hardware context is clearly documented in the
DTS comments in the v5 patch.
> > On Fri, 2026-04-24 at 17:30 +0800, Kyle Hsieh wrote:
> > > Add linux device tree entry related to the Meta(Facebook) rmc-node.
> > > The system use an AT2600 BMC.
> > > This node is named "ventura2".
> > >
> > > Signed-off-by: Kyle Hsieh <kylehsieh1995@gmail.com>
> > > ---
> > > arch/arm/boot/dts/aspeed/Makefile | 1 +
> > > .../dts/aspeed/aspeed-bmc-facebook-ventura2.dts | 2925 ++++++++++++++++++++
> > > 2 files changed, 2926 insertions(+)
> > >
> > > diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile
> > > index 9adf9278dc94..6b96997629d4 100644
> > > --- a/arch/arm/boot/dts/aspeed/Makefile
> > > +++ b/arch/arm/boot/dts/aspeed/Makefile
> > > @@ -32,6 +32,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
> > > aspeed-bmc-facebook-minipack.dtb \
> > > aspeed-bmc-facebook-santabarbara.dtb \
> > > aspeed-bmc-facebook-tiogapass.dtb \
> > > + aspeed-bmc-facebook-ventura2.dtb \
> > > aspeed-bmc-facebook-wedge40.dtb \
> > > aspeed-bmc-facebook-wedge100.dtb \
> > > aspeed-bmc-facebook-wedge400-data64.dtb \
> > > diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts
> > > new file mode 100644
> > > index 000000000000..8d4ddb473862
> > > --- /dev/null
> > > +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts
> > > @@ -0,0 +1,2925 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +// Copyright (c) 2023 Facebook Inc.
> > > +/dts-v1/;
> > > +
> > > +#include "aspeed-g6.dtsi"
> > > +#include <dt-bindings/i2c/i2c.h>
> > > +#include <dt-bindings/gpio/aspeed-gpio.h>
> > > +
> > > +/ {
> > > + model = "Facebook Ventura2 RMC";
> > > + compatible = "facebook,ventura2-rmc", "aspeed,ast2600";
> > > + aliases {
> > > + serial2 = &uart3;
> > > + serial4 = &uart5;
> > > +
> > > + /*
> > > + * i2c switch 0-0077, pca9548, 8 child channels assigned
> > > + * with bus number 16-23.
> > > + */
> > > + i2c16 = &i2c0mux0ch0;
> > > + i2c17 = &i2c0mux0ch1;
> > > + i2c18 = &i2c0mux0ch2;
> > > + i2c19 = &i2c0mux0ch3;
> > > + i2c20 = &i2c0mux0ch4;
> > > + i2c21 = &i2c0mux0ch5;
> > > + i2c22 = &i2c0mux0ch6;
> > > + i2c23 = &i2c0mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 1-0077, pca9548, 8 child channels assigned
> > > + * with bus number 24-31.
> > > + */
> > > + i2c24 = &i2c1mux0ch0;
> > > + i2c25 = &i2c1mux0ch1;
> > > + i2c26 = &i2c1mux0ch2;
> > > + i2c27 = &i2c1mux0ch3;
> > > + i2c28 = &i2c1mux0ch4;
> > > + i2c29 = &i2c1mux0ch5;
> > > + i2c30 = &i2c1mux0ch6;
> > > + i2c31 = &i2c1mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 4-0077, pca9548, 8 child channels assigned
> > > + * with bus number 32-39.
> > > + */
> > > + i2c32 = &i2c4mux0ch0;
> > > + i2c33 = &i2c4mux0ch1;
> > > + i2c34 = &i2c4mux0ch2;
> > > + i2c35 = &i2c4mux0ch3;
> > > + i2c36 = &i2c4mux0ch4;
> > > + i2c37 = &i2c4mux0ch5;
> > > + i2c38 = &i2c4mux0ch6;
> > > + i2c39 = &i2c4mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 5-0077, pca9548, 8 child channels assigned
> > > + * with bus number 40-47.
> > > + */
> > > + i2c40 = &i2c5mux0ch0;
> > > + i2c41 = &i2c5mux0ch1;
> > > + i2c42 = &i2c5mux0ch2;
> > > + i2c43 = &i2c5mux0ch3;
> > > + i2c44 = &i2c5mux0ch4;
> > > + i2c45 = &i2c5mux0ch5;
> > > + i2c46 = &i2c5mux0ch6;
> > > + i2c47 = &i2c5mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 8-0077, pca9548, 8 child channels assigned
> > > + * with bus number 48-55.
> > > + */
> > > + i2c48 = &i2c8mux0ch0;
> > > + i2c49 = &i2c8mux0ch1;
> > > + i2c50 = &i2c8mux0ch2;
> > > + i2c51 = &i2c8mux0ch3;
> > > + i2c52 = &i2c8mux0ch4;
> > > + i2c53 = &i2c8mux0ch5;
> > > + i2c54 = &i2c8mux0ch6;
> > > + i2c55 = &i2c8mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 11-0077, pca9548, 8 child channels assigned
> > > + * with bus number 56-63.
> > > + */
> > > + i2c56 = &i2c11mux0ch0;
> > > + i2c57 = &i2c11mux0ch1;
> > > + i2c58 = &i2c11mux0ch2;
> > > + i2c59 = &i2c11mux0ch3;
> > > + i2c60 = &i2c11mux0ch4;
> > > + i2c61 = &i2c11mux0ch5;
> > > + i2c62 = &i2c11mux0ch6;
> > > + i2c63 = &i2c11mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 13-0077, pca9548, 8 child channels assigned
> > > + * with bus number 64-71.
> > > + */
> > > + i2c64 = &i2c13mux0ch0;
> > > + i2c65 = &i2c13mux0ch1;
> > > + i2c66 = &i2c13mux0ch2;
> > > + i2c67 = &i2c13mux0ch3;
> > > + i2c68 = &i2c13mux0ch4;
> > > + i2c69 = &i2c13mux0ch5;
> > > + i2c70 = &i2c13mux0ch6;
> > > + i2c71 = &i2c13mux0ch7;
> > > +
> > > + /*
> > > + * i2c switch 15-0077, pca9548, 8 child channels assigned
> > > + * with bus number 72-79.
> > > + */
> > > + i2c72 = &i2c15mux0ch0;
> > > + i2c73 = &i2c15mux0ch1;
> > > + i2c74 = &i2c15mux0ch2;
> > > + i2c75 = &i2c15mux0ch3;
> > > + i2c76 = &i2c15mux0ch4;
> > > + i2c77 = &i2c15mux0ch5;
> > > + i2c78 = &i2c15mux0ch6;
> > > + i2c79 = &i2c15mux0ch7;
> >
> > Can you please add comments justifying why all of these aliases are
> > necessary given a number of them are for busses with no devices
> > described under them?
> These I2C aliases are pre-allocated because these empty channels are
> strictly reserved for future hardware feature expansions, which will
> interface with add-on boards. I will add clear comments in the code to
> justify their necessity in the next patch.
> >
> > > + };
> > > +
> > > + chosen {
> > > + stdout-path = "serial4:57600n8";
> > > + };
> > > +
> > > + fan_leds {
> > > + compatible = "gpio-leds";
> > > +
> > > + led-0 {
> > > + label = "fcb0fan0_ledd1_blue";
> >
> > Given the labels are exposed to userspace and is something applications
> > likely consume, is the double 'd' in led intentional?
> Yes, the double 'd' in "ledd1" is intentional. It aligns with our hardware
> schematic design naming convention, where these specific onboard
> indicators are designated as LEDD1, LEDD2, etc.
> >
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander0 0 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-1 {
> > > + label = "fcb0fan1_ledd2_blue";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander0 1 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-2 {
> > > + label = "fcb0fan2_ledd3_blue";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander1 0 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-3 {
> > > + label = "fcb0fan3_ledd4_blue";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander1 1 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-4 {
> > > + label = "fcb0fan0_ledd1_amber";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander0 4 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-5 {
> > > + label = "fcb0fan1_ledd2_amber";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander0 5 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-6 {
> > > + label = "fcb0fan2_ledd3_amber";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander1 4 GPIO_ACTIVE_LOW>;
> > > + };
> > > +
> > > + led-7 {
> > > + label = "fcb0fan3_ledd4_amber";
> > > + default-state = "off";
> > > + gpios = <&fan_io_expander1 5 GPIO_ACTIVE_LOW>;
> > > + };
> > > + };
> > > +
> >
> > ...
> >
> > > +
> > > +&fmc {
> > > + status = "okay";
> > > + flash@0 {
> > > + status = "okay";
> > > + m25p,fast-read;
> > > + label = "bmc";
> > > + spi-max-frequency = <50000000>;
> > > + #include "openbmc-flash-layout-128.dtsi"
> > > + };
> > > + flash@1 {
> > > + status = "okay";
> > > + m25p,fast-read;
> > > + label = "alt-bmc";
> > > + spi-max-frequency = <50000000>;
> >
> > Perhaps include the alternate flash layout dtsi here?
> The `flash@1` (`alt-bmc`) node is intentionally left unpartitioned
> without the layout DTSI.
>
> In our dual-flash design, `flash@1` serves as the secondary/backup
> flash chip. Keeping it without sub-partitions allows the kernel and
> userspace tools to treat this flash as a single, contiguous raw MTD
> device. This is required by our firmware update mechanism, which
> flashes a single, full-size composite image directly to the entire
> backup flash.
>
> This structure follows the existing upstream pattern established in
> `aspeed-bmc-facebook-yosemite5.dts`.
> >
> > > + };
> > > +};
> > > +
> > > +&peci0 {
> >
> > Can you please order the nodes alphabetically. P is not between F and
> > G.
> I will ensure all nodes are strictly sorted in alphabetical order in
> the next version.
> >
> > > + status = "okay";
> > > +};
> > > +
> > >
> >
> > ...
> >
> > > +
> > > +&i2c10 {
> > > + status = "okay";
> > > +
> > > + legacy_prsnt_io_expander0: gpio@11 {
> >
> > Why 'legacy'?
> >
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x11>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <40 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PRSNT1_N_BUF_R", "TRAY_PRSNT2_N_BUF_R",
> > > + "TRAY_PRSNT3_N_BUF_R", "TRAY_PRSNT4_N_BUF_R",
> > > + "TRAY_PRSNT5_N_BUF_R", "TRAY_PRSNT6_N_BUF_R",
> > > + "TRAY_PRSNT7_N_BUF_R", "TRAY_PRSNT8_N_BUF_R",
> > > + "TRAY_PRSNT9_N_BUF_R", "TRAY_PRSNT10_N_BUF_R",
> > > + "TRAY_PRSNT11_N_BUF_R", "TRAY_PRSNT12_N_BUF_R",
> > > + "TRAY_PRSNT13_N_BUF_R", "TRAY_PRSNT14_N_BUF_R",
> > > + "TRAY_PRSNT15_N_BUF_R", "TRAY_PRSNT16_N_BUF_R";
> > > + };
> > > +
> > > + legacy_prsnt_io_expander1: gpio@12 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x12>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <40 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PRSNT17_N_BUF_R", "TRAY_PRSNT18_N_BUF_R",
> > > + "TRAY_PRSNT19_N_BUF_R", "TRAY_PRSNT20_N_BUF_R",
> > > + "TRAY_PRSNT21_N_BUF_R", "TRAY_PRSNT22_N_BUF_R",
> > > + "TRAY_PRSNT23_N_BUF_R", "TRAY_PRSNT24_N_BUF_R",
> > > + "TRAY_PRSNT25_N_BUF_R", "TRAY_PRSNT26_N_BUF_R",
> > > + "TRAY_PRSNT27_N_BUF_R", "TRAY_PRSNT28_N_BUF_R",
> > > + "TRAY_PRSNT29_N_BUF_R", "TRAY_PRSNT30_N_BUF_R",
> > > + "TRAY_PRSNT31_N_BUF_R", "TRAY_PRSNT32_N_BUF_R";
> > > + };
> > > +
> > > + legacy_prsnt_io_expander2: gpio@13 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x13>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <40 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PRSNT33_N_BUF_R", "TRAY_PRSNT34_N_BUF_R",
> > > + "TRAY_PRSNT35_N_BUF_R", "TRAY_PRSNT36_N_BUF_R",
> > > + "TRAY_PRSNT37_N_BUF_R", "TRAY_PRSNT38_N_BUF_R",
> > > + "TRAY_PRSNT39_N_BUF_R", "TRAY_PRSNT40_N_BUF_R",
> > > + "", "",
> > > + "", "",
> > > + "", "",
> > > + "", "";
> > > + };
> > > +
> > > + power-monitor@14 {
> > > + compatible = "infineon,xdp710";
> > > + reg = <0x14>;
> > > + };
> > > +
> > > + legacy_pwrgd_io_expander1: gpio@15 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x15>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <42 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PWRGD17_N_BUF_R", "TRAY_PWRGD18_N_BUF_R",
> > > + "TRAY_PWRGD19_N_BUF_R", "TRAY_PWRGD20_N_BUF_R",
> > > + "TRAY_PWRGD21_N_BUF_R", "TRAY_PWRGD22_N_BUF_R",
> > > + "TRAY_PWRGD23_N_BUF_R", "TRAY_PWRGD24_N_BUF_R",
> > > + "TRAY_PWRGD25_N_BUF_R", "TRAY_PWRGD26_N_BUF_R",
> > > + "TRAY_PWRGD27_N_BUF_R", "TRAY_PWRGD28_N_BUF_R",
> > > + "TRAY_PWRGD29_N_BUF_R", "TRAY_PWRGD30_N_BUF_R",
> > > + "TRAY_PWRGD31_N_BUF_R", "TRAY_PWRGD32_N_BUF_R";
> > > + };
> > > +
> > > + legacy_pwrgd_io_expander2: gpio@16 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x16>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <42 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PWRGD33_N_BUF_R", "TRAY_PWRGD34_N_BUF_R",
> > > + "TRAY_PWRGD35_N_BUF_R", "TRAY_PWRGD36_N_BUF_R",
> > > + "TRAY_PWRGD37_N_BUF_R", "TRAY_PWRGD38_N_BUF_R",
> > > + "TRAY_PWRGD39_N_BUF_R", "TRAY_PWRGD40_N_BUF_R",
> > > + "", "",
> > > + "", "",
> > > + "", "",
> > > + "", "";
> > > + };
> > > +
> > > + legacy_leak_io_expander0: gpio@17 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x17>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <46 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_LEAK_DETECT1_N_BUF_R", "TRAY_LEAK_DETECT2_N_BUF_R",
> > > + "TRAY_LEAK_DETECT3_N_BUF_R", "TRAY_LEAK_DETECT4_N_BUF_R",
> > > + "TRAY_LEAK_DETECT5_N_BUF_R", "TRAY_LEAK_DETECT6_N_BUF_R",
> > > + "TRAY_LEAK_DETECT7_N_BUF_R", "TRAY_LEAK_DETECT8_N_BUF_R",
> > > + "TRAY_LEAK_DETECT9_N_BUF_R", "TRAY_LEAK_DETECT10_N_BUF_R",
> > > + "TRAY_LEAK_DETECT11_N_BUF_R", "TRAY_LEAK_DETECT12_N_BUF_R",
> > > + "TRAY_LEAK_DETECT13_N_BUF_R", "TRAY_LEAK_DETECT14_N_BUF_R",
> > > + "TRAY_LEAK_DETECT15_N_BUF_R", "TRAY_LEAK_DETECT16_N_BUF_R";
> > > + };
> > > +
> > > + legacy_leak_io_expander1: gpio@18 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x18>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <46 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_LEAK_DETECT17_N_BUF_R", "TRAY_LEAK_DETECT18_N_BUF_R",
> > > + "TRAY_LEAK_DETECT19_N_BUF_R", "TRAY_LEAK_DETECT20_N_BUF_R",
> > > + "TRAY_LEAK_DETECT21_N_BUF_R", "TRAY_LEAK_DETECT22_N_BUF_R",
> > > + "TRAY_LEAK_DETECT23_N_BUF_R", "TRAY_LEAK_DETECT24_N_BUF_R",
> > > + "TRAY_LEAK_DETECT25_N_BUF_R", "TRAY_LEAK_DETECT26_N_BUF_R",
> > > + "TRAY_LEAK_DETECT27_N_BUF_R", "TRAY_LEAK_DETECT28_N_BUF_R",
> > > + "TRAY_LEAK_DETECT29_N_BUF_R", "TRAY_LEAK_DETECT30_N_BUF_R",
> > > + "TRAY_LEAK_DETECT31_N_BUF_R", "TRAY_LEAK_DETECT32_N_BUF_R";
> > > + };
> > > +
> > > + legacy_leak_io_expander2: gpio@19 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x19>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <46 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_LEAK_DETECT33_N_BUF_R", "TRAY_LEAK_DETECT34_N_BUF_R",
> > > + "TRAY_LEAK_DETECT35_N_BUF_R", "TRAY_LEAK_DETECT36_N_BUF_R",
> > > + "TRAY_LEAK_DETECT37_N_BUF_R", "TRAY_LEAK_DETECT38_N_BUF_R",
> > > + "TRAY_LEAK_DETECT39_N_BUF_R", "TRAY_LEAK_DETECT40_N_BUF_R",
> > > + "", "",
> > > + "", "",
> > > + "", "",
> > > + "", "";
> > > + };
> > > +
> > > + legacy_small_leak_io_expander0: gpio@1a {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x1a>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <44 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_SMALL_LEAK1_N_BUF_R", "TRAY_SMALL_LEAK2_N_BUF_R",
> > > + "TRAY_SMALL_LEAK3_N_BUF_R", "TRAY_SMALL_LEAK4_N_BUF_R",
> > > + "TRAY_SMALL_LEAK5_N_BUF_R", "TRAY_SMALL_LEAK6_N_BUF_R",
> > > + "TRAY_SMALL_LEAK7_N_BUF_R", "TRAY_SMALL_LEAK8_N_BUF_R",
> > > + "TRAY_SMALL_LEAK9_N_BUF_R", "TRAY_SMALL_LEAK10_N_BUF_R",
> > > + "TRAY_SMALL_LEAK11_N_BUF_R", "TRAY_SMALL_LEAK12_N_BUF_R",
> > > + "TRAY_SMALL_LEAK13_N_BUF_R", "TRAY_SMALL_LEAK14_N_BUF_R",
> > > + "TRAY_SMALL_LEAK15_N_BUF_R", "TRAY_SMALL_LEAK16_N_BUF_R";
> > > + };
> > > +
> > > + legacy_small_leak_io_expander1: gpio@1b {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x1b>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <44 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_SMALL_LEAK17_N_BUF_R", "TRAY_SMALL_LEAK18_N_BUF_R",
> > > + "TRAY_SMALL_LEAK19_N_BUF_R", "TRAY_SMALL_LEAK20_N_BUF_R",
> > > + "TRAY_SMALL_LEAK21_N_BUF_R", "TRAY_SMALL_LEAK22_N_BUF_R",
> > > + "TRAY_SMALL_LEAK23_N_BUF_R", "TRAY_SMALL_LEAK24_N_BUF_R",
> > > + "TRAY_SMALL_LEAK25_N_BUF_R", "TRAY_SMALL_LEAK26_N_BUF_R",
> > > + "TRAY_SMALL_LEAK27_N_BUF_R", "TRAY_SMALL_LEAK28_N_BUF_R",
> > > + "TRAY_SMALL_LEAK29_N_BUF_R", "TRAY_SMALL_LEAK30_N_BUF_R",
> > > + "TRAY_SMALL_LEAK31_N_BUF_R", "TRAY_SMALL_LEAK32_N_BUF_R";
> > > + };
> > > +
> > > + legacy_small_leak_io_expander2: gpio@1c {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x1c>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <44 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_SMALL_LEAK33_N_BUF_R", "TRAY_SMALL_LEAK34_N_BUF_R",
> > > + "TRAY_SMALL_LEAK35_N_BUF_R", "TRAY_SMALL_LEAK36_N_BUF_R",
> > > + "TRAY_SMALL_LEAK37_N_BUF_R", "TRAY_SMALL_LEAK38_N_BUF_R",
> > > + "TRAY_SMALL_LEAK39_N_BUF_R", "TRAY_SMALL_LEAK40_N_BUF_R",
> > > + "", "",
> > > + "", "",
> > > + "", "",
> > > + "", "";
> > > + };
> > > +
> > > + legacy_pwrgd_io_expander0: gpio@28 {
> > > + compatible = "nxp,pca9555";
> > > + reg = <0x28>;
> > > + gpio-controller;
> > > + #gpio-cells = <2>;
> > > + interrupt-parent = <&sgpiom0>;
> > > + interrupts = <42 IRQ_TYPE_LEVEL_LOW>;
> > > +
> > > + gpio-line-names =
> > > + "TRAY_PWRGD1_N_BUF_R", "TRAY_PWRGD2_N_BUF_R",
> > > + "TRAY_PWRGD3_N_BUF_R", "TRAY_PWRGD4_N_BUF_R",
> > > + "TRAY_PWRGD5_N_BUF_R", "TRAY_PWRGD6_N_BUF_R",
> > > + "TRAY_PWRGD7_N_BUF_R", "TRAY_PWRGD8_N_BUF_R",
> > > + "TRAY_PWRGD9_N_BUF_R", "TRAY_PWRGD10_N_BUF_R",
> > > + "TRAY_PWRGD11_N_BUF_R", "TRAY_PWRGD12_N_BUF_R",
> > > + "TRAY_PWRGD13_N_BUF_R", "TRAY_PWRGD14_N_BUF_R",
> > > + "TRAY_PWRGD15_N_BUF_R", "TRAY_PWRGD16_N_BUF_R";
> > > + };
> > > +
> >
> > ...
> >
> > > +
> > > +&mdio0 {
> > > + status = "okay";
> > > +};
> > > +
> > > +&peci0 {
> > > + status = "okay";
> > > +};
> >
> > Ah, so the earlier peci node is redundant. Can you please remove it?
> Got it. I will remove the redundant peci0 node and ensure all nodes
> are strictly sorted in alphabetical order in the next version.
> >
> > Andrew
^ permalink raw reply
* RE: Re: [PATCH 2/5] dt-bindings: connector: Add fsl,io-connector binding
From: Chancel Liu @ 2026-05-19 2:56 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Frank Li, s.hauer@pengutronix.de, festevam@gmail.com,
mturquette@baylibre.com, sboyd@kernel.org, kernel@pengutronix.de,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
linux-clk@vger.kernel.org, Chancel Liu (OSS)
In-Reply-To: <d2b13e51-1684-4acb-a5ed-294c8aa95906@kernel.org>
> >>> +description:
> >>> + The NXP I/O connector represents a physically present I/O
> >>> +connector on the
> >>> + base board. It acts as a nexus that exposes a constrained set of
> >>> +I/O
> >>> + resources, such as GPIOs, clocks, PWMs and interrupts, through
> >>> +fixed
> >>> + electrical wiring. All actual hardware providers reside on the base
> board.
> >>> + The connector node only defines index-based mappings to those
> >> providers.
> >>> +
> >>> +properties:
> >>> + compatible:
> >>> + const: fsl,io-connector
> >>
> >> Everything is IO. Everything is connector, so your compatible does
> >> not match requirements from writing bindings.
> >>
> >
> > Yes, this compatible is too generic. I will rename the compatible to
> > fsl,aud-io-connector.
>
> aud is not much better. Which boards have it? What's the pinout? What's
> standard? Is it described anywhere? If so, provide reference to spec/docs.
>
This is not an industry standard electrical interface. This connector
is present on i.MX95-19x19-EVK and i.MX952-EVK. For example, the
"i.MX 95 19mm x 19mm Evaluation Kit" homepage[1] publicly documents an
audio board connection through which IMX-AUD-IO card is connected. The
detailed user manual (UM12022) is listed as official documentation[2],
but it is behind an NXP login, so it is not suitable as a public
reference for upstream. Therefore I list it here to illustrate it's
mechanism:
+-----------------------------+
| Base Board |
| +-----+ +---------+ | +---------+
| | SPI +------+ | | | |
| +-----+ | | | GPIO MAP | |
| | +--|-----------+ |
| +-----+ | | | | |
| | I2C +------+ | | | |
| +-----+ | | | CLOCK MAP | AUD-IO |
| |connector+--|-----------+ CARD |
| +-----+ | | | | |
| | I2S +------+ | | | |
| +-----+ | | | | |
| | | | INT MAP | |
| +-----+ | +--|-----------+ |
| | I/O +------+ | | | |
| +-----+ +---------+ | +---------+
+-----------------------------+
[1]https://www.nxp.com/design/design-center/development-boards-and-designs/IMX95LPD5EVK-19
[2]https://docs.nxp.com/bundle/UM12022/page/topics/pcie_interface1.html
> >
> >>> +
> >>> + gpio-controller: true
> >>> +
> >>> + '#gpio-cells':
> >>> + const: 2
> >>> +
> >>> + gpio-map:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-matrix
> >>
> >> You do not need to redefine the types. You need constraints, though.
> >>
> >
> > OK. I will add proper constraints.
> >
> >>> +
> >>> + gpio-map-mask:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-array
> >>> +
> >>> + gpio-map-pass-thru:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-array
> >>> +
> >>> + '#clock-cells':
> >>> + const: 1
> >>> +
> >>> + clock-map:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-matrix
> >>> +
> >>> + clock-map-mask:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-array
> >>> +
> >>> + clock-map-pass-thru:
> >>> + $ref: /schemas/types.yaml#/definitions/uint32-array
> >>
> >> I do not see these defined anywhere. I also checked cover letter for
> >> references for pulls to dtschema.
> >>
> >
> > Nexus nodes are already in the device-tree specification:
> > https://github.com/devicetree-org/devicetree-specification/blob/v0.4/source/chapter2-devicetree-basics.rst#nexus-nodes-and-specifier-mapping
> > For reference, current kernel has supported it:
> > * Nexus OF support:
> > commit bd6f2fd5a1d5 ("of: Support parsing phandle argument lists
> > through a nexus node")
> > * GPIO adoption:
> > commit c11e6f0f04db ("gpio: Support gpio nexus dt bindings")
> > * PWM adoption:
> > commit e71e46a6f19c ("pwm: Add support for pwm nexus dt bindings")
> > Clock adoption is ongoing:
> > https://lore.kernel.org/all/20260327-schneider-v7-0-rc1-crypto-v1-10-5e6ff7853994@bootlin.com/
>
> DT spec only mentions nexuses, but it is only a spec. Each property from
> the spec must be defined in dtschema or kernel bindings.
>
> I do not see any dependency mentioned in the cover letter, so how do you
> think we can figure out where is this definition of clock nexus?
>
>
I initially tried to add support for clock nexus dt bindings in patch 1,
but I noticed there is already an ongoing series doing the same thing.
([PATCH 10/16] clk: Add support for clock nexus dt bindings). Since that
work is in progress, I think it's better not duplicate it here.
I think we can add a dedicated binding defining the clock nexus
properties(#clock-cells, clock-map, clock-map-mask, clock-map-pass-thru),
in reference of existing pwm-nexus-node.yaml.
In the next revision I will also update the cover letter to explicitly
mention that clock nexus support is ongoing.
> Best regards,
> Krzysztof
Regards,
Chancel Liu
^ permalink raw reply
* [soc:soc/dt] BUILD SUCCESS 6cf3cf025848f2b13ddf1db0577a5f178492a3ff
From: kernel test robot @ 2026-05-19 3:04 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linux-arm-kernel, arm
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git soc/dt
branch HEAD: 6cf3cf025848f2b13ddf1db0577a5f178492a3ff Merge tag 'pxa1908-dt-for-7.2' of https://codeberg.org/pxa1908-mainline/linux into soc/dt
elapsed time: 724m
configs tested: 175
configs skipped: 148
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc alldefconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc defconfig gcc-15.2.0
arc randconfig-001-20260519 clang-23
arc randconfig-002-20260519 clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm randconfig-001-20260519 clang-23
arm randconfig-002-20260519 clang-23
arm randconfig-003-20260519 clang-23
arm randconfig-004-20260519 clang-23
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260519 gcc-8.5.0
arm64 randconfig-002-20260519 gcc-8.5.0
arm64 randconfig-003-20260519 gcc-8.5.0
arm64 randconfig-004-20260519 gcc-8.5.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260519 gcc-8.5.0
csky randconfig-002-20260519 gcc-8.5.0
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260519 clang-23
hexagon randconfig-001-20260519 gcc-10.5.0
hexagon randconfig-002-20260519 clang-23
hexagon randconfig-002-20260519 gcc-10.5.0
i386 allmodconfig clang-20
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 buildonly-randconfig-001-20260519 gcc-12
i386 buildonly-randconfig-002-20260519 gcc-12
i386 buildonly-randconfig-003-20260519 gcc-12
i386 buildonly-randconfig-004-20260519 gcc-12
i386 buildonly-randconfig-005-20260519 gcc-12
i386 buildonly-randconfig-006-20260519 gcc-12
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260519 gcc-14
i386 randconfig-002-20260519 gcc-14
i386 randconfig-003-20260519 gcc-14
i386 randconfig-004-20260519 gcc-14
i386 randconfig-005-20260519 gcc-14
i386 randconfig-006-20260519 gcc-14
i386 randconfig-007-20260519 gcc-14
i386 randconfig-011-20260519 gcc-14
i386 randconfig-012-20260519 gcc-14
i386 randconfig-013-20260519 gcc-14
i386 randconfig-014-20260519 gcc-14
i386 randconfig-015-20260519 gcc-14
i386 randconfig-016-20260519 gcc-14
i386 randconfig-017-20260519 gcc-14
loongarch allmodconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260519 clang-23
loongarch randconfig-001-20260519 gcc-10.5.0
loongarch randconfig-002-20260519 clang-23
loongarch randconfig-002-20260519 gcc-10.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
m68k m5249evb_defconfig gcc-15.2.0
m68k m5407c3_defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips bcm47xx_defconfig clang-18
nios2 allmodconfig clang-23
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 randconfig-001-20260519 gcc-10.5.0
nios2 randconfig-002-20260519 gcc-10.5.0
openrisc allmodconfig clang-23
openrisc allnoconfig clang-23
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260519 gcc-8.5.0
parisc randconfig-002-20260519 gcc-8.5.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc randconfig-001-20260519 gcc-8.5.0
powerpc randconfig-002-20260519 gcc-8.5.0
powerpc64 randconfig-001-20260519 gcc-8.5.0
powerpc64 randconfig-002-20260519 gcc-8.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260519 gcc-13.4.0
riscv randconfig-002-20260519 gcc-13.4.0
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260519 gcc-13.4.0
s390 randconfig-002-20260519 gcc-13.4.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh defconfig gcc-14
sh randconfig-001-20260519 gcc-13.4.0
sh randconfig-002-20260519 gcc-13.4.0
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260519 gcc-14.3.0
sparc randconfig-002-20260519 gcc-14.3.0
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260519 gcc-14.3.0
sparc64 randconfig-002-20260519 gcc-14.3.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260519 gcc-14.3.0
um randconfig-002-20260519 gcc-14.3.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260519 gcc-14
x86_64 buildonly-randconfig-002-20260519 gcc-14
x86_64 buildonly-randconfig-003-20260519 gcc-14
x86_64 buildonly-randconfig-004-20260519 gcc-14
x86_64 buildonly-randconfig-005-20260519 gcc-14
x86_64 buildonly-randconfig-006-20260519 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260519 clang-20
x86_64 randconfig-002-20260519 clang-20
x86_64 randconfig-003-20260519 clang-20
x86_64 randconfig-004-20260519 clang-20
x86_64 randconfig-005-20260519 clang-20
x86_64 randconfig-006-20260519 clang-20
x86_64 randconfig-011-20260519 clang-20
x86_64 randconfig-012-20260519 clang-20
x86_64 randconfig-013-20260519 clang-20
x86_64 randconfig-014-20260519 clang-20
x86_64 randconfig-015-20260519 clang-20
x86_64 randconfig-016-20260519 clang-20
x86_64 randconfig-071-20260519 gcc-14
x86_64 randconfig-072-20260519 gcc-14
x86_64 randconfig-073-20260519 gcc-14
x86_64 randconfig-074-20260519 gcc-14
x86_64 randconfig-075-20260519 gcc-14
x86_64 randconfig-076-20260519 gcc-14
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa randconfig-001-20260519 gcc-14.3.0
xtensa randconfig-002-20260519 gcc-14.3.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v3 2/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc1-pinctrl
From: Billy Tsai @ 2026-05-19 3:14 UTC (permalink / raw)
To: Conor Dooley
Cc: Linus Walleij, Tony Lindgren, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Bartosz Golaszewski,
Lee Jones, Ryan Chen, patrickw3@meta.com,
linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, BMC-SW, openbmc@lists.ozlabs.org,
Andrew Jeffery, linux-clk@vger.kernel.org
In-Reply-To: <20260515-verify-awhile-cb0c72d49e91@spud>
> > + properties:
> > + function:
> > + enum:
> > + - ADC0
> > + - ADC1
> It'd be nice if you could use the other enum format I think so that
> there's not 700 lines taken up by functions/groups/pins.
I considered the inline format, but kept the one-item-per-line style
intentionally: with 200+ entries in each enum, any future addition or
rename produces a clean single-line diff. The inline format would
require re-wrapping the entire list on every change, making those diffs
significantly harder to review.
> Otherwise, I really don't like this approach but it seems to be standard
> on aspeed so whatever.
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> pw-bot: not-applicable
Thank you for the review and the ack.
Billy Tsai
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: allwinner: A133: add support for Baijie Helper A133 board
From: Chen-Yu Tsai @ 2026-05-19 3:33 UTC (permalink / raw)
To: Paul Kocialkowski, Alexander Sverdlin
Cc: linux-sunxi, Andre Przywara, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <aguIvpKQM18kX97T@shepard>
On Tue, May 19, 2026 at 5:46 AM Paul Kocialkowski <paulk@sys-base.io> wrote:
>
> Hi Alexander,
>
> On Mon 18 May 26, 22:05, Alexander Sverdlin wrote:
> > Hi Paul,
> >
> > thanks for the review!
> >
> > On Mon, 2026-05-18 at 13:52 +0200, Paul Kocialkowski wrote:
> > >
> > > > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> > > > new file mode 100644
> > > > index 000000000000..65b094f30bf5
> > > > --- /dev/null
> > > > +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> >
> > []
> >
> > > You should add:
> > >
> > > chosen {
> > > stdout-path = "serial0:115200n8";
> > > };
> >
> > I actually have it in .dts, but it's theoretically possible to deploy
> > the core board in a way that serial0 is *not* a console, so the above
> > probably will not be valid in all cases in .dtsi.
>
> Yes I figured out later that it was in the board dts file and I initially
> assumed it was missing entirely.
>
> In practice both options are fine, although the reference software does
> hardcode UART0 as debug serial.
>
> > > > +®_dcdc2 {
> > > > + regulator-always-on;
> > > > + regulator-min-microvolt = <500000>;
> > > > + regulator-max-microvolt = <1300000>;
> > >
> > > Should be:
> > > regulator-min-microvolt = <900000>;
> > > regulator-max-microvolt = <1300000>;
> >
> > 0.81..1.2v according to A133 Datasheet Revision 1.1 Jul.14, 2020?
>
> I guess the initial values are taken from the allwinner-perf1 board dts.
>
> The 900 mV-1.3 V range matches the CPU OPPs (although it really only goes up
> to 1.13 V). Maybe down to 810 mV does work, but we don't have an OPP for it.
> I think I took these values from the reference BSP for the board.
>
> Also it would be good to add:
>
> regulator-name = "vdd-cpux";
>
> > >
> > > > +®_dcdc4 {
> > > > + regulator-always-on;
> > > > + regulator-min-microvolt = <500000>;
> > > > + regulator-max-microvolt = <1300000>;
> > > > + regulator-name = "vdd-sys";
> > >
> > > Should be:
> > > regulator-min-microvolt = <810000>;
> > > regulator-max-microvolt = <990000>;
> > > regulator-name = "vcc-usb-sys";
> >
> > I'm a bit puzzled here: datasheet says 0.9..1.0v
> > and it has no "Typ" value, similar to VDD_CPU, but
> > VDD_SYS is not part of OPP tables, so who is going
> > to adjust this? Or shall it be just
No one really. As long as the current voltage is within the range,
the kernel will be happy. But ideally the DT just describes the
acceptable range, and the bootloader programs the correct recommended
voltage.
> > regulator-min-microvolt = <950000>;
> > regulator-max-microvolt = <950000>;
> >
> > ?
>
> Yes the reference BSP runs it at 950 mV, LGTM.
>
> >
> > >
> > > > +};
> > > > +
> > > > +®_dcdc5 {
> > > > + regulator-always-on;
> > > > + regulator-min-microvolt = <800000>;
> > > > + regulator-max-microvolt = <1840000>;
> > > > + regulator-name = "vcc-dram";
> > >
> > > Should be:
> > > regulator-min-microvolt = <1100000>;
> > > regulator-max-microvolt = <1100000>;
> > > regulator-name = "vcc-dram-2";
> > >
> > > ALDO2 is the main DRAM supply, this is the second one.
> >
> > Core schematics mentions 1.1V/1.2/1.35/1.5 on this rail...
> > Currently U-Boot has CONFIG_AXP_DCDC5_VOLT=1100, but potentially
> > this is adjustable, right? At some point LPDDR4 chips they
> > are soldering today will be unavailable. And in the current
> > market it will happen rather sooner than later...
>
> It is part of the LPDDR4 spec that the main voltage should be 1.8 V and
> the second and I/O buffer ones should be 1.1 V. See JESD209-4D Table 180 —
> Recommended DC Operating Conditions.
>
> Maybe they jsut copied this comment from a reference design that allows for
> other types of DRAM too. In any case their BSP hardcodes 1.1 V anyway.
>
> > >
> > > > +};
> > > > +
> > > > +/* DCDC6 unused */
> > > > +
> > > > +®_dldo1 {
> > > > + regulator-min-microvolt = <700000>;
> > > > + regulator-max-microvolt = <3300000>;
> > > > + regulator-enable-ramp-delay = <1000>;
> > >
> > > Should be:
> > > regulator-min-microvolt = <1800000>;
> > > regulator-max-microvolt = <1800000>;
> > > regulator-name = "vcc-pg";
> >
> > Do suggest to drop vendor's
> >
> > regulator-enable-ramp-delay = <1000>;
> >
> > in all cases?
>
> Well we generally don't have the delays in the axp regulator definitions and
> it works well without them, but I guess they don't hurt either.
We should probably keep them if we know that they are properly measured
values, and not just cargo-culted.
> In practice many drivers will have a delay after a regulator power on anyway
> because we generally expect that hardware needs some time to power up,
> in addition to the regulator. So all in all it's rarely critical.
Those are two different things. Devices generally say "wait x ms" after
power rail is at full voltage. The enable ramp delay is the time it
takes the rail to get to full voltage. The latter depends on board
design (capacitor rating) and PMIC settings.
ChenYu
> >
> > > >
> > > > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> > > > new file mode 100644
> > > > index 000000000000..ccbca5d0a40c
> > > > --- /dev/null
> > > > +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> >
> > []
> >
> > > > + aliases {
> > > > + serial0 = &uart0;
> > >
> > > The is best added to the core dtsi.
> > >
> > > > + };
> > > > +
> > > > + chosen {
> > > > + stdout-path = "serial0:115200n8";
> > >
> > > Ditto.
> >
> > But it only physically materializes in Helperboard, the carrier.
> > Potentially this one can be left floating or used for something else.
>
> Yes fair enough, I'm happy with having it on the helperboard dts file.
>
> All the best,
>
> Paul
>
> --
> Paul Kocialkowski,
>
> Independent contractor - sys-base - https://www.sys-base.io/
> Free software developer - https://www.paulk.fr/
>
> Expert in multimedia, graphics and embedded hardware support with Linux.
^ permalink raw reply
* [PATCH v4 00/24] iommu/arm-smmu-v3: Quarantine device upon ATC invalidation timeout
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
Hi all,
This series addresses a critical vulnerability and stability issue where an
unresponsive PCIe device failing to process ATC (Address Translation Cache)
invalidation requests leads to silent data corruption and continuous SMMU
CMDQ error spam.
[ As Jason pointed out, because this series fundamentally introduces a new
RAS feature to quarantine and recover from hardware faults and relies on
a recently accepted SMMU driver rework, it is not treated as a standard
bug fix. Thus, most of the patches here don't carry a "Fixes" tag. ]
Currently, when an ATC invalidation times out, the SMMUv3 driver skips the
CMDQ_ERR_CERROR_ATC_INV_IDX error. This leaves the device's ATS cache state
desynchronized from the SMMU: the device cache may retain stale ATC entries
for memory pages that the OS has already reclaimed and reassigned, creating
a direct vector for data corruption. Furthermore, the driver might continue
issuing ATC_INV commands, resulting in constant CMDQ errors:
unexpected global error reported (0x00000001), this could be serious
CMDQ error (cons 0x0302bb84): ATC invalidate timeout
unexpected global error reported (0x00000001), this could be serious
CMDQ error (cons 0x0302bb88): ATC invalidate timeout
unexpected global error reported (0x00000001), this could be serious
CMDQ error (cons 0x0302bb8c): ATC invalidate timeout
...
To resolve this, introduce a mechanism to quarantine a broken device in the
SMMUv3 driver and the IOMMU core. To achieve this, add preparatory changes:
- Pass in PCI reset result to pci_dev_reset_iommu_done()
- Co-clear pending CMDQ_ERR from the cmdq issuer under a raw_spinlock_t,
so an ATC_INV timeout flagged in cmdq->atc_sync_timeouts is definitive
when the issuer reads its bit after CMD_SYNC poll
- Introduce a reset_device_done op, allowing the core to signal the driver
when the physical hardware has been cleanly recovered (e.g., via AER or
a manual reset) so the quarantine can be lifted
- Utilize a per-group_device WQ via an iommu_report_device_broken() helper
On the SMMUv3 driver side, retry the timedout ATC_INV batch to identify the
faulty device(s). Perform a surgical STE update, and flag the ATS as broken
to reject further ATS/ATC requests at HW level and suppress timeout spam.
This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_atc_timeout-v4
Changelog
v4:
* Rebase on Joerg's IOMMU "fixes" branch
* Rebase on Jason's SMMUv3 cmd_ent series
https://lore.kernel.org/all/0-v2-47b2bf710ad5+716ac-smmu_no_cmdq_ent_jgg@nvidia.com/
* [PCI] Don't suspend IOMMU in probe mode
* [iommu] kfree_rcu() iommu_group
* [iommu] Convert gdev->blocked to enum gdev_blocked
* [iommu] Use disable_work_sync() to fix UAF and ref leak
* [iommu] Gate done() transitions to preserve BLOCKED_BROKEN
* [iommu] Decrement recovery_cnt when unplugging a blocked gdev
* [iommu] Drop racy dev_has_iommu() in iommu_report_device_broken()
* [iommu] Add gdev->broken_pending to skip worker after racing recovery
* [smmuv3] Add master->ats_invs scratch
* [smmuv3] Add arm_smmu_cmdq_batch_issue() wrapper
* [smmuv3] Force per-flush sync for has_ats batches
* [smmuv3] Serialize STE.EATS and ats_broken updates
* [smmuv3] Co-clear pending CMDQ_ERR from cmdq issuer
* [smmuv3] Add invs and has_ats to arm_smmu_cmdq_batch
* [smmuv3] Move arm_smmu_invs_for_each_entry to header
* [smmuv3] Set master->ats_broken after clearing STE.EATS
* [smmuv3] Issue CFGI_STE via arm_smmu_cmdq_issue_cmd_with_sync()
* [smmuv3] Keep "smmu" pointer in arm_smmu_inv but add "master" for ATS
v3:
https://lore.kernel.org/all/cover.1776381841.git.nicolinc@nvidia.com/
* Rebase on arm/smmu/updates branch + bug fix
* Update commit messages and inline comments
* [iommu] Drop unnecessary ops validation
* [iommu] Add missed function stub when !CONFIG_IOMMU_API
* [iommu] Change iommu_report_device_broken() to per gdev
* [iommu] Separate quarantine from pci_dev_reset_prepare()
* [iommu] Check reset failure in pci_dev_reset_iommu_done()
* [smmuv3] Fix STE update with try_cmpxchg64()
* [smmuv3] Fix "continue" bug when skipping ATC commands
* [smmuv3] Replace atomic_t prod_err with a lockless bitmap
* [smmuv3] Drop master->invs_domain; disable ATS per-master directly
* [smmuv3] Return -EIO for ATC timeout v.s. -ETIMEDOUT for poll timeout
* [smmuv3] Replace INV_TYPE_ATS_DISABLED with per-master ats_broken flag
v2:
https://lore.kernel.org/all/cover.1773774441.git.nicolinc@nvidia.com/
* Rebase on arm_smmu_invs-v13 series
* Bisect batched atc invalidation commands
* Drop the direct pci_reset_function() call
* Move the work queue from SMMUv3 to the core
* Proceed a surgical STE update to disable EATS
* Wait for pci_dev_reset_iommu_done() to signal a recovery
v1:
https://lore.kernel.org/all/cover.1772686998.git.nicolinc@nvidia.com/
Thanks
Nicolin
Nicolin Chen (24):
PCI: Don't suspend IOMMU when probing reset capability
PCI: Propagate FLR return values to callers
iommu: Convert gdev->blocked from bool to enum gdev_blocked
iommu: Pass in reset result to pci_dev_reset_iommu_done()
iommu: Add reset_device_done callback for hardware fault recovery
iommu: Defer iommu_group free via kfree_rcu()
iommu: Defer __iommu_group_free_device() to be outside group->mutex
iommu: Change group->devices to RCU-protected list
iommu: Add group pointer to struct group_device
iommu: Add __iommu_group_block_device helper
iommu: Add iommu_report_device_broken() to quarantine a broken device
iommu/arm-smmu-v3: Mark ATC invalidate timeouts via lockless bitmap
iommu/arm-smmu-v3: Skip remaining GERROR causes on SFM
iommu/arm-smmu-v3: Introduce per-cmdq cmdq_err_handler callback
iommu/arm-smmu-v3: Co-clear pending CMDQ_ERR when CMD_SYNC times out
iommu/arm-smmu-v3: Co-clear pending CMDQ_ERR when queue_has_space()
fails
iommu/arm-smmu-v3: Add master in arm_smmu_inv for ATS entries
iommu/arm-smmu-v3: Introduce master->ats_broken flag
iommu/arm-smmu-v3: Add invs and has_ats to struct arm_smmu_cmdq_batch
iommu/arm-smmu-v3: Introduce arm_smmu_cmdq_batch_issue() wrapper
iommu/arm-smmu-v3: Move arm_smmu_invs_for_each_entry to header
iommu/arm-smmu-v3: Introduce master->ats_invs
iommu/arm-smmu-v3: Serialize STE.EATS and ats_broken updates
iommu/arm-smmu-v3: Block ATS upon an ATC invalidation timeout
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 72 +++-
include/linux/iommu.h | 18 +-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 387 ++++++++++++++---
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 36 +-
drivers/iommu/iommu.c | 406 ++++++++++++++----
drivers/pci/pci-acpi.c | 2 +-
drivers/pci/pci.c | 21 +-
drivers/pci/quirks.c | 43 +-
8 files changed, 820 insertions(+), 165 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v4 02/24] PCI: Propagate FLR return values to callers
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
A reset failure implies that the device might be unreliable. E.g. its ATC
might still retain stale entries. Thus, the IOMMU layer cannot trust this
device to resume its ATS function that can lead to memory corruption. So,
the pci_dev_reset_iommu_done() won't recover the device's IOMMU pathway if
the device reset fails.
The quirk functions in the pci_dev_reset_methods array invoke pcie_flr(),
but do not check the return value. Propagate them correctly.
Also propagate device-internal ack timeouts in reset_hinic_vf_dev().
Note: this change does not introduce any early return on failure, keeping
everything status quo. It only propagates the error code properly.
Given that these functions have been running okay, and the return values
will only be needed for incoming work. This is not treated as bug fix.
Suggested-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/pci/quirks.c | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index a344abd745947..6cded18c9a687 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3955,7 +3955,7 @@ static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, bool probe)
* supported.
*/
if (!probe)
- pcie_flr(dev);
+ return pcie_flr(dev);
return 0;
}
@@ -4013,6 +4013,7 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)
{
u16 old_command;
u16 msix_flags;
+ int ret;
/*
* If this isn't a Chelsio T4-based device, return -ENOTTY indicating
@@ -4058,16 +4059,15 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)
PCI_MSIX_FLAGS_ENABLE |
PCI_MSIX_FLAGS_MASKALL);
- pcie_flr(dev);
+ ret = pcie_flr(dev);
/*
* Restore the configuration information (BAR values, etc.) including
- * the original PCI Configuration Space Command word, and return
- * success.
+ * the original PCI Configuration Space Command word.
*/
pci_restore_state(dev);
pci_write_config_word(dev, PCI_COMMAND, old_command);
- return 0;
+ return ret;
}
#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed
@@ -4150,9 +4150,7 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
pci_iounmap(dev, bar);
- pcie_flr(dev);
-
- return 0;
+ return pcie_flr(dev);
}
/*
@@ -4164,14 +4162,17 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
*/
static int delay_250ms_after_flr(struct pci_dev *dev, bool probe)
{
+ int ret;
+
if (probe)
return pcie_reset_flr(dev, PCI_RESET_PROBE);
- pcie_reset_flr(dev, PCI_RESET_DO_RESET);
+ ret = pcie_reset_flr(dev, PCI_RESET_DO_RESET);
+ /* Settle the device even on a failed FLR */
msleep(250);
- return 0;
+ return ret;
}
#define PCI_DEVICE_ID_HINIC_VF 0x375E
@@ -4187,6 +4188,7 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
unsigned long timeout;
void __iomem *bar;
u32 val;
+ int ret;
if (probe)
return 0;
@@ -4207,12 +4209,13 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
val = val | HINIC_VF_FLR_PROC_BIT;
iowrite32be(val, bar + HINIC_VF_OP);
- pcie_flr(pdev);
+ ret = pcie_flr(pdev);
/*
* The device must recapture its Bus and Device Numbers after FLR
* in order generate Completions. Issue a config write to let the
- * device capture this information.
+ * device capture this information. Note that pcie_flr() can fail
+ * after the reset is asserted. So, recapture it unconditionally.
*/
pci_write_config_word(pdev, PCI_VENDOR_ID, 0);
@@ -4230,11 +4233,12 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
goto reset_complete;
pci_warn(pdev, "Reset dev timeout, FLR ack reg: %#010x\n", val);
+ ret = -ETIMEDOUT;
reset_complete:
pci_iounmap(pdev, bar);
- return 0;
+ return ret;
}
static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
--
2.43.0
^ permalink raw reply related
* [PATCH v4 01/24] PCI: Don't suspend IOMMU when probing reset capability
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
reset_method_store() in drivers/pci/pci-sysfs.c discovers supported reset
methods by calling reset_fn(pdev, PCI_RESET_PROBE, ...) without holding a
device_lock, since the probe path is expected to query the device's reset
capability without changing device state.
However, pci_reset_bus_function() and __pci_dev_specific_reset() violate
that contract after pci_dev_reset_iommu_prepare/done() were added, which
moves the device into a blocking domain and abruptly aborts any in-flight
DMA. Doing this for a probe -- a state-query call that does not even hold
device_lock -- can cause driver timeouts and data loss on a DMAing device.
The peer reset helpers all handle this correctly: they short-circuit on a
probe input before touching the IOMMU.
Skip pci_dev_reset_iommu_prepare()/_done() entirely when probe is set. The
inner reset routines already implement their own probe semantics, and they
perform the capability checks and return without changing device state.
Fixes: f5b16b802174 ("PCI: Suspend iommu function prior to resetting a device")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/pci/pci.c | 13 ++++++++-----
drivers/pci/quirks.c | 13 ++++++++-----
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d34266651ad09..d0af8b5eca2ce 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4914,10 +4914,12 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
if (bridge && pcie_is_cxl(bridge) && cxl_sbr_masked(bridge))
return -ENOTTY;
- rc = pci_dev_reset_iommu_prepare(dev);
- if (rc) {
- pci_err(dev, "failed to stop IOMMU for a PCI reset: %d\n", rc);
- return rc;
+ if (!probe) {
+ rc = pci_dev_reset_iommu_prepare(dev);
+ if (rc) {
+ pci_err(dev, "failed to stop IOMMU for a PCI reset: %d\n", rc);
+ return rc;
+ }
}
rc = pci_dev_reset_slot_function(dev, probe);
@@ -4926,7 +4928,8 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
rc = pci_parent_bus_reset(dev, probe);
done:
- pci_dev_reset_iommu_done(dev);
+ if (!probe)
+ pci_dev_reset_iommu_done(dev);
return rc;
}
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index caaed1a01dc02..a344abd745947 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4260,14 +4260,17 @@ static int __pci_dev_specific_reset(struct pci_dev *dev, bool probe,
{
int ret;
- ret = pci_dev_reset_iommu_prepare(dev);
- if (ret) {
- pci_err(dev, "failed to stop IOMMU for a PCI reset: %d\n", ret);
- return ret;
+ if (!probe) {
+ ret = pci_dev_reset_iommu_prepare(dev);
+ if (ret) {
+ pci_err(dev, "failed to stop IOMMU for a PCI reset: %d\n", ret);
+ return ret;
+ }
}
ret = i->reset(dev, probe);
- pci_dev_reset_iommu_done(dev);
+ if (!probe)
+ pci_dev_reset_iommu_done(dev);
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 06/24] iommu: Defer iommu_group free via kfree_rcu()
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
dev->iommu_group will be read in an ISR-context to look up a group_device
for fault reporting, in which case mutex cannot be used. For that read to
be safe, two things are needed:
(1) The iommu_group memory that dev->iommu_group points to must outlive
any in-flight rcu_read_lock section. Add rcu_head to iommu_group and
switch iommu_group_release() to calling kfree_rcu().
(2) The publication of dev->iommu_group must pair with rcu_dereference()
at the upcoming reader (cannot hold mutex but rcu_read_lock), so the
writers must use rcu_assign_pointer().
Existing readers do not use rcu_dereference(); they retain their current
synchronization model. Apply a __rcu __force cast at the writer sites to
satisfy sparse without forcing every reader to convert.
New reader added by the subsequent change uses rcu_dereference() only, to
reach group->devices for a list lookup. And it does not touch group->name
and other fields. The kfree_rcu() here is supposed to keep group->devices
alive across the read-side critical section; other fields will not affect
the reader.
Note: this change alone does not yet make group->devices iteration safe
under rcu_read_lock(); a subsequent change will convert the group_device
list to RCU and switch struct group_device to kfree_rcu().
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/iommu.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e68c7b142ad5a..6727b6f7797bd 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -71,8 +71,12 @@ struct iommu_group {
*/
unsigned int recovery_cnt;
void *owner;
+ struct rcu_head rcu;
};
+#define dev_iommu_group_rcu(dev) \
+ (*((struct iommu_group __rcu __force **)&(dev)->iommu_group))
+
enum gdev_blocked {
BLOCKED_NO = 0, /* Not blocked */
BLOCKED_RESETTING, /* PCI reset in flight */
@@ -531,7 +535,7 @@ static int iommu_init_device(struct device *dev)
ret = PTR_ERR(group);
goto err_unlink;
}
- dev->iommu_group = group;
+ rcu_assign_pointer(dev_iommu_group_rcu(dev), group);
dev->iommu->max_pasids = dev_iommu_get_max_pasids(dev);
if (ops->is_attach_deferred)
@@ -613,7 +617,7 @@ static void iommu_deinit_device(struct device *dev)
}
/* Caller must put iommu_group */
- dev->iommu_group = NULL;
+ rcu_assign_pointer(dev_iommu_group_rcu(dev), NULL);
module_put(ops->owner);
dev_iommu_free(dev);
#ifdef CONFIG_IOMMU_DMA
@@ -772,7 +776,7 @@ static void __iommu_group_remove_device(struct device *dev)
if (dev_has_iommu(dev))
iommu_deinit_device(dev);
else
- dev->iommu_group = NULL;
+ rcu_assign_pointer(dev_iommu_group_rcu(dev), NULL);
break;
}
mutex_unlock(&group->mutex);
@@ -1059,7 +1063,7 @@ static void iommu_group_release(struct kobject *kobj)
WARN_ON(group->blocking_domain);
kfree(group->name);
- kfree(group);
+ kfree_rcu(group, rcu);
}
static const struct kobj_type iommu_group_ktype = {
@@ -1344,7 +1348,7 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev)
return PTR_ERR(gdev);
iommu_group_ref_get(group);
- dev->iommu_group = group;
+ rcu_assign_pointer(dev_iommu_group_rcu(dev), group);
mutex_lock(&group->mutex);
list_add_tail(&gdev->list, &group->devices);
--
2.43.0
^ permalink raw reply related
* [PATCH v4 04/24] iommu: Pass in reset result to pci_dev_reset_iommu_done()
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
IOMMU drivers handle ATC cache maintenance. They may encounter ATC-related
errors (e.g., ATC invalidation timeout), indicating that the ATC cache may
have stale entries that can corrupt the memory. In this case, IOMMU driver
has no choice but to block the device's ATS function and wait for a device
recovery.
The pci_dev_reset_iommu_done() called at the end of a reset function could
serve as a reliable signal to the IOMMU subsystem that the physical device
cache is completely clean. However, the function is called unconditionally
even if the reset operation had actually failed, which would re-attach the
faulty device back to a normal translation domain. And this will leave the
system highly exposed, creating vulnerabilities for data corruption:
IOMMU blocks RID/ATS
pci_reset_function():
pci_dev_reset_iommu_prepare(); // Block RID/ATS
__reset(); // Failed (ATC is still stale)
pci_dev_reset_iommu_done(); // Unblock RID/ATS (ah-ha)
Instead, pass in @reset_result to pci_dev_reset_iommu_done() from callers:
IOMMU blocks RID/ATS
pci_reset_function():
pci_dev_reset_iommu_prepare(); // Block RID/ATS
rc = __reset();
pci_dev_reset_iommu_done(rc); // Unblock or quarantine
On a successful reset, done() restores the device to its RID/PASID domains
and decrements group->recovery_cnt. On failure, the device remains blocked,
and concurrent domain attachment will be rejected until a successful reset.
Note: -ENOTTY is overloaded with different meanings by PCI reset functions.
Some of them indicate "reset was not attempted", while others indicate "try
the next reset method and the current method failed". IOMMU that must react
these two outcomes separately has no choice but to keep the device blocked
on -ENOTTY as well. Leave an inline FIXME and warning.
This introduces a new situation where a blocked device is being unplugged.
Decrement the group->recovery_cnt accordingly.
Suggested-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
include/linux/iommu.h | 5 ++--
drivers/iommu/iommu.c | 62 ++++++++++++++++++++++++++++++++++++++++--
drivers/pci/pci-acpi.c | 2 +-
drivers/pci/pci.c | 10 +++----
drivers/pci/quirks.c | 2 +-
5 files changed, 69 insertions(+), 12 deletions(-)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e587d4ac4d331..e191d30d228ac 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1195,7 +1195,7 @@ void iommu_free_global_pasid(ioasid_t pasid);
/* PCI device reset functions */
int pci_dev_reset_iommu_prepare(struct pci_dev *pdev);
-void pci_dev_reset_iommu_done(struct pci_dev *pdev);
+void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result);
#else /* CONFIG_IOMMU_API */
struct iommu_ops {};
@@ -1525,7 +1525,8 @@ static inline int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
return 0;
}
-static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
+static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev,
+ int reset_result)
{
}
#endif /* CONFIG_IOMMU_API */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index c40f4bfc93352..6c92b7a2b14cc 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -76,6 +76,7 @@ struct iommu_group {
enum gdev_blocked {
BLOCKED_NO = 0, /* Not blocked */
BLOCKED_RESETTING, /* PCI reset in flight */
+ BLOCKED_RESET_FAILED, /* PCI reset failed */
};
struct group_device {
@@ -763,6 +764,9 @@ static void __iommu_group_remove_device(struct device *dev)
if (device->dev != dev)
continue;
+ /* Must drop the recovery_cnt when removing a blocked device */
+ if (device->blocked && !WARN_ON(group->recovery_cnt == 0))
+ group->recovery_cnt--;
list_del(&device->list);
__iommu_group_free_device(group, device);
if (dev_has_iommu(dev))
@@ -4036,7 +4040,12 @@ EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, "IOMMUFD_INTERNAL");
* reset is finished, pci_dev_reset_iommu_done() can restore everything.
*
* Caller must use pci_dev_reset_iommu_prepare() with pci_dev_reset_iommu_done()
- * before/after the core-level reset routine, to decrement the recovery_cnt.
+ * before/after the core-level reset routine. On a successful reset, done() will
+ * decrement group->recovery_cnt and restore domains. On a failure, recovery_cnt
+ * is left intact and the device stays blocked.
+ *
+ * Callers must skip pci_dev_reset_iommu_prepare/done() entirely when no reset
+ * is attempted (e.g. probe mode).
*
* Return: 0 on success or negative error code if the preparation failed.
*
@@ -4066,6 +4075,10 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
if (gdev->reset_depth++)
return 0;
+ /* Device might be already blocked for a quarantine */
+ if (gdev->blocked)
+ return 0;
+
ret = __iommu_group_alloc_blocking_domain(group);
if (ret) {
gdev->reset_depth--;
@@ -4147,20 +4160,28 @@ static bool group_device_dma_alias_is_blocked(struct iommu_group *group,
/**
* pci_dev_reset_iommu_done() - Restore IOMMU after a PCI device reset is done
* @pdev: PCI device that has finished a reset routine
+ * @reset_result: Return code from the reset routine
*
* After a PCIe device finishes a reset routine, it wants to restore its IOMMU
* activity, including new translation and cache invalidation, by re-attaching
* all RID/PASID of the device back to the domains retained in the core-level
* structure.
*
- * Caller must pair it with a successful pci_dev_reset_iommu_prepare().
+ * This is a pairing function for pci_dev_reset_iommu_prepare(). Caller passes
+ * the reset return value to @reset_result. On a failed reset, the device will
+ * remain blocked as a quarantine measure, with group->recovery_cnt intact, to
+ * protect system memory until a subsequent successful reset.
+ *
+ * Callers must skip pci_dev_reset_iommu_prepare/done() entirely when no reset
+ * is attempted (e.g. probe mode).
*
* Note that, although unlikely, there is a risk that re-attaching domains might
* fail due to some unexpected happening like OOM.
*/
-void pci_dev_reset_iommu_done(struct pci_dev *pdev)
+void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
{
struct iommu_group *group = pdev->dev.iommu_group;
+ enum gdev_blocked old_gdev_blocked;
struct group_device *gdev;
unsigned long pasid;
void *entry;
@@ -4183,6 +4204,37 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
if (WARN_ON(!group->blocking_domain))
return;
+ /*
+ * A reset failure implies that the device might be unreliable. E.g. its
+ * device cache might retain stale entries, which might result in memory
+ * corruption. Thus, do not unblock the device until a successful reset.
+ */
+ if (reset_result) {
+ /*
+ * FIXME: the int-return values from the PCI reset functions are
+ * not consistent: some reset functions use -ENOTTY to indicate
+ * "no reset was attempted" (in which case IOMMU should revert a
+ * prepare), while others use -ENOTTY to indicate "reset failed;
+ * try the next reset method" (in which case IOMMU should keep
+ * the device blocked). Without fixing the PCI return result, we
+ * cannot tell the difference between the two cases. Warn it.
+ */
+ if (reset_result == -ENOTTY)
+ dev_warn_ratelimited(
+ &pdev->dev,
+ "Reset may have been skipped. Keep it blocked conservatively\n");
+ else
+ dev_err_ratelimited(
+ &pdev->dev,
+ "Reset failed. Keep it blocked to protect memory\n");
+ if (gdev->blocked == BLOCKED_RESETTING)
+ gdev->blocked = BLOCKED_RESET_FAILED;
+ return;
+ }
+
+ if (WARN_ON(!gdev->blocked))
+ return;
+
if (group_device_dma_alias_is_blocked(group, gdev)) {
/*
* FIXME: DMA aliased devices share the same RID, which would be
@@ -4213,6 +4265,7 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
* the correct domain in iommu_driver_get_domain_for_dev() that might be
* called in a set_dev_pasid callback function.
*/
+ old_gdev_blocked = gdev->blocked;
gdev->blocked = BLOCKED_NO;
/*
@@ -4234,6 +4287,9 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
if (!WARN_ON(group->recovery_cnt == 0))
group->recovery_cnt--;
+
+ if (old_gdev_blocked > BLOCKED_RESETTING)
+ pci_info(pdev, "Device is unblocked after successful reset\n");
}
EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_done);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 4d0f2cb6c695b..280d7193cb4ca 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -977,7 +977,7 @@ int pci_dev_acpi_reset(struct pci_dev *dev, bool probe)
ret = -ENOTTY;
}
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, ret);
return ret;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d0af8b5eca2ce..b71e3e10c7b52 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4355,7 +4355,7 @@ int pcie_flr(struct pci_dev *dev)
ret = pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
done:
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, ret);
return ret;
}
EXPORT_SYMBOL_GPL(pcie_flr);
@@ -4433,7 +4433,7 @@ static int pci_af_flr(struct pci_dev *dev, bool probe)
ret = pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
done:
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, ret);
return ret;
}
@@ -4487,7 +4487,7 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
pci_dev_d3_sleep(dev);
ret = pci_dev_wait(dev, "PM D3hot->D0", PCIE_RESET_READY_POLL_MS);
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, ret);
return ret;
}
@@ -4929,7 +4929,7 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
rc = pci_parent_bus_reset(dev, probe);
done:
if (!probe)
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, rc);
return rc;
}
@@ -4974,7 +4974,7 @@ static int cxl_reset_bus_function(struct pci_dev *dev, bool probe)
pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
reg);
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, rc);
return rc;
}
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 6cded18c9a687..39b1c6250a4d0 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4274,7 +4274,7 @@ static int __pci_dev_specific_reset(struct pci_dev *dev, bool probe,
ret = i->reset(dev, probe);
if (!probe)
- pci_dev_reset_iommu_done(dev);
+ pci_dev_reset_iommu_done(dev, ret);
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 03/24] iommu: Convert gdev->blocked from bool to enum gdev_blocked
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
The gdev->blocked flag tracks whether a device is individually being held
in the group->blocking_domain while group->domain is retained. Up to now,
a PCI reset in flight is the only producer, so a bool suffices.
Subsequent changes will add more reasons to keep a device blocked, e.g. a
failed-reset case that must not auto-unblock, or a driver-side quarantine
for a hardware fault. These reasons are cleared by different events, which
a single bool cannot encode.
Convert "bool blocked" into "enum gdev_blocked blocked", provisioned with
two initial values: BLOCKED_NO and BLOCKED_RESETTING, for the existing use
cases. All readers keep the "if (gdev->blocked)" form, as BLOCKED_NO == 0.
This is a pure type change with no behavior change. Follow-on changes will
add new enum values along with their producers.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/iommu.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e7bd28cc77eeb..c40f4bfc93352 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -73,16 +73,20 @@ struct iommu_group {
void *owner;
};
+enum gdev_blocked {
+ BLOCKED_NO = 0, /* Not blocked */
+ BLOCKED_RESETTING, /* PCI reset in flight */
+};
+
struct group_device {
struct list_head list;
struct device *dev;
char *name;
/*
* Device is blocked for a pending recovery while its group->domain is
- * retained. This can happen when:
- * - Device is undergoing a reset
+ * retained.
*/
- bool blocked;
+ enum gdev_blocked blocked;
unsigned int reset_depth;
};
@@ -4083,7 +4087,7 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
* the correct domain in iommu_driver_get_domain_for_dev() that might be
* called in a set_dev_pasid callback function.
*/
- gdev->blocked = true;
+ gdev->blocked = BLOCKED_RESETTING;
/*
* Stage PASID domains at blocking_domain while retaining pasid_array.
@@ -4209,7 +4213,7 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
* the correct domain in iommu_driver_get_domain_for_dev() that might be
* called in a set_dev_pasid callback function.
*/
- gdev->blocked = false;
+ gdev->blocked = BLOCKED_NO;
/*
* Re-attach PASID domains back to the domains retained in pasid_array.
--
2.43.0
^ permalink raw reply related
* [PATCH v4 07/24] iommu: Defer __iommu_group_free_device() to be outside group->mutex
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
__iommu_group_remove_device() holds group->mutex across the entire call to
__iommu_group_free_device() that performs sysfs removals, tracing, and the
final kfree(). But in fact, most of these operations don't really need the
group->mutex.
Subsequent changes will introduce sleepable operations to this function:
+ synchronize_rcu() to defer the gdev->dev put past a grace period.
+ disable_work_sync() to cancel a future broken_work.
Neither should run while holding group->mutex. Thus, move them outside.
Separate the assertion from __iommu_group_free_device() to another helper
__iommu_group_empty_assert_owner_cnt(). While moving it, revise the inline
comment a bit to make it clearer.
Defer the __iommu_group_free_device() until the mutex is released.
This is a preparatory refactor with no functional change.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/iommu.c | 35 +++++++++++++++++++++++------------
1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 6727b6f7797bd..2f8f3ea13f490 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -634,6 +634,19 @@ static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
DEFINE_MUTEX(iommu_probe_device_lock);
+static void __iommu_group_empty_assert_owner_cnt(struct iommu_group *group)
+{
+ lockdep_assert_held(&group->mutex);
+ /*
+ * If the group has become empty, the ownership must have been released,
+ * and the current domain must be set back to the default domain (which
+ * itself can be NULL).
+ */
+ if (list_empty(&group->devices))
+ WARN_ON(group->owner_cnt ||
+ group->domain != group->default_domain);
+}
+
static int __iommu_probe_device(struct device *dev, struct list_head *group_list)
{
struct iommu_group *group;
@@ -707,10 +720,12 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
err_remove_gdev:
list_del(&gdev->list);
- __iommu_group_free_device(group, gdev);
+ __iommu_group_empty_assert_owner_cnt(group);
err_put_group:
iommu_deinit_device(dev);
mutex_unlock(&group->mutex);
+ if (!IS_ERR(gdev))
+ __iommu_group_free_device(group, gdev);
iommu_group_put(group);
return ret;
@@ -739,20 +754,13 @@ static void __iommu_group_free_device(struct iommu_group *group,
{
struct device *dev = grp_dev->dev;
+ lockdep_assert_not_held(&group->mutex);
+
sysfs_remove_link(group->devices_kobj, grp_dev->name);
sysfs_remove_link(&dev->kobj, "iommu_group");
trace_remove_device_from_group(group->id, dev);
- /*
- * If the group has become empty then ownership must have been
- * released, and the current domain must be set back to NULL or
- * the default domain.
- */
- if (list_empty(&group->devices))
- WARN_ON(group->owner_cnt ||
- group->domain != group->default_domain);
-
kfree(grp_dev->name);
kfree(grp_dev);
}
@@ -761,7 +769,7 @@ static void __iommu_group_free_device(struct iommu_group *group,
static void __iommu_group_remove_device(struct device *dev)
{
struct iommu_group *group = dev->iommu_group;
- struct group_device *device;
+ struct group_device *device, *to_free = NULL;
mutex_lock(&group->mutex);
for_each_group_device(group, device) {
@@ -772,15 +780,18 @@ static void __iommu_group_remove_device(struct device *dev)
if (device->blocked && !WARN_ON(group->recovery_cnt == 0))
group->recovery_cnt--;
list_del(&device->list);
- __iommu_group_free_device(group, device);
+ __iommu_group_empty_assert_owner_cnt(group);
if (dev_has_iommu(dev))
iommu_deinit_device(dev);
else
rcu_assign_pointer(dev_iommu_group_rcu(dev), NULL);
+ to_free = device;
break;
}
mutex_unlock(&group->mutex);
+ if (to_free)
+ __iommu_group_free_device(group, to_free);
/*
* Pairs with the get in iommu_init_device() or
* iommu_group_add_device()
--
2.43.0
^ permalink raw reply related
* [PATCH v4 11/24] iommu: Add iommu_report_device_broken() to quarantine a broken device
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
When an IOMMU hardware detects an error due to a faulty device (e.g. an ATS
invalidation timeout), IOMMU drivers may quarantine the device by disabling
specific hardware features or dropping translation capabilities.
However, the core-level states of the faulty device are out of sync, as the
device can still be attached to a translation domain or even potentially be
moved to a new domain that might overwrite the driver-level quarantine.
Given that such an error can likely be triggered from an ISR, introduce an
asynchronous broken_work per group_device, and provide a helper function to
allow the driver to initiate a quarantine in the core. __dev_to_gdev_rcu()
is required here to safely iterate the group_device list.
Note: gdev and gdev->group teardown will be blocked by disable_work_sync(),
so it's completely safe for the worker thread to access the group pointer.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
include/linux/iommu.h | 11 +++-
drivers/iommu/iommu.c | 139 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+), 1 deletion(-)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 6c124e9e9af8b..c088c8e8c1e2b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -631,7 +631,10 @@ __iommu_copy_struct_to_user(const struct iommu_user_data *dst_data,
* group and attached to the groups domain
* @reset_device_done: Notify the driver that a device has reset successfully.
* Note that the core invokes the callback function while
- * holding the group->mutex
+ * holding the group->mutex. Before returning, the driver
+ * must drain or filter pre-reset fault reports so that
+ * subsequent calls to iommu_report_device_broken() will
+ * reflect only post-reset faults.
* @device_group: find iommu group for a particular device
* @get_resv_regions: Request list of reserved regions for a device
* @of_xlate: add OF master IDs to iommu grouping
@@ -896,6 +899,8 @@ static inline struct iommu_device *__iommu_get_iommu_dev(struct device *dev)
#define iommu_get_iommu_dev(dev, type, member) \
container_of(__iommu_get_iommu_dev(dev), type, member)
+void iommu_report_device_broken(struct device *dev);
+
static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
{
*gather = (struct iommu_iotlb_gather) {
@@ -1211,6 +1216,10 @@ struct iommu_iotlb_gather {};
struct iommu_dirty_bitmap {};
struct iommu_dirty_ops {};
+static inline void iommu_report_device_broken(struct device *dev)
+{
+}
+
static inline bool device_iommu_capable(struct device *dev, enum iommu_cap cap)
{
return false;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b150d22d8015f..6e5a7e38c5e67 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -82,6 +82,7 @@ enum gdev_blocked {
BLOCKED_NO = 0, /* Not blocked */
BLOCKED_RESETTING, /* PCI reset in flight */
BLOCKED_RESET_FAILED, /* PCI reset failed */
+ BLOCKED_BROKEN, /* Driver flagged a fault */
};
struct group_device {
@@ -95,6 +96,9 @@ struct group_device {
*/
enum gdev_blocked blocked;
unsigned int reset_depth;
+ struct work_struct broken_work;
+ /* Transient state before broken_worker thread starts */
+ bool broken_pending;
};
/* Iterate over each struct group_device in a struct iommu_group */
@@ -117,6 +121,25 @@ static struct group_device *__dev_to_gdev(struct device *dev)
return NULL;
}
+/* Caller must be inside rcu_read_lock(). */
+static struct group_device *__dev_to_gdev_rcu(struct device *dev)
+{
+ struct iommu_group *group;
+ struct group_device *gdev;
+
+ lockdep_assert(rcu_read_lock_held());
+
+ group = rcu_dereference(dev_iommu_group_rcu(dev));
+ if (!group)
+ return NULL;
+
+ for_each_group_device(group, gdev) {
+ if (gdev->dev == dev)
+ return gdev;
+ }
+ return NULL;
+}
+
struct iommu_group_attribute {
struct attribute attr;
ssize_t (*show)(struct iommu_group *group, char *buf);
@@ -180,6 +203,7 @@ static ssize_t iommu_group_store_type(struct iommu_group *group,
static struct group_device *iommu_group_alloc_device(struct iommu_group *group,
struct device *dev);
static void __iommu_group_free_device(struct group_device *grp_dev);
+static void iommu_group_broken_worker(struct work_struct *work);
static void iommu_domain_init(struct iommu_domain *domain, unsigned int type,
const struct iommu_ops *ops);
@@ -762,6 +786,12 @@ static void __iommu_group_free_device(struct group_device *grp_dev)
sysfs_remove_link(group->devices_kobj, grp_dev->name);
sysfs_remove_link(&dev->kobj, "iommu_group");
+ /*
+ * Disable (not just cancel) broken_work to prevent UAF; otherwise a
+ * concurrent schedule_work() from iommu_report_device_broken() would
+ * queue onto an about-to-be-freed gdev.
+ */
+ disable_work_sync(&grp_dev->broken_work);
trace_remove_device_from_group(group->id, dev);
kfree(grp_dev->name);
@@ -1308,6 +1338,7 @@ static struct group_device *iommu_group_alloc_device(struct iommu_group *group,
device->group = group;
/* Keep dev alive for any in-flight RCU reader of grp_dev->dev. */
get_device(dev);
+ INIT_WORK(&device->broken_work, iommu_group_broken_worker);
ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group");
if (ret)
@@ -4301,6 +4332,14 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
*/
if (ops->reset_device_done)
ops->reset_device_done(&pdev->dev);
+ /*
+ * Clear broken_pending after the reset_device_done callback to
+ * neutralize any stale pre-reset report. Until this moment, a
+ * driver may call iommu_report_device_broken() on a pre-reset
+ * fault. Legitimate post-reset reports can only fire after the
+ * re-attach below, so this clear cannot hide them.
+ */
+ WRITE_ONCE(gdev->broken_pending, false);
/*
* Re-attach RID domain back to group->domain
@@ -4346,6 +4385,106 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
}
EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_done);
+static void iommu_group_broken_worker(struct work_struct *work)
+{
+ struct group_device *gdev =
+ container_of(work, struct group_device, broken_work);
+ struct iommu_group *group = gdev->group;
+ struct device *dev = gdev->dev;
+
+ guard(mutex)(&group->mutex);
+
+ /*
+ * iommu_deinit_device() frees dev->iommu under group->mutex. Bail
+ * out if the device has already been removed from IOMMU handling.
+ */
+ if (!dev_has_iommu(dev))
+ return;
+
+ /*
+ * A successful reset between schedule_work() and now would have cleared
+ * broken_pending. Skip as the device has already been recovered.
+ */
+ if (!READ_ONCE(gdev->broken_pending))
+ return;
+
+ /*
+ * Quarantine the device completely. For a PCI device, it will be lifted
+ * upon a pci_dev_reset_iommu_done(pdev, reset_result=0) call indicating
+ * a device recovery.
+ *
+ * For a non-PCI device, currently it has no recovery framework tied to
+ * the IOMMU subsystem. Quarantine it indefinitely until a recovery path
+ * is introduced.
+ */
+ if (WARN_ON(__iommu_group_block_device(gdev, BLOCKED_BROKEN)))
+ return;
+
+ dev_warn(dev, "IOMMU has quarantined the device\n");
+ WRITE_ONCE(gdev->broken_pending, false);
+}
+
+/**
+ * iommu_report_device_broken() - Report a broken device to quarantine it
+ * @dev: Device that has encountered an unrecoverable IOMMU-related error
+ *
+ * When an IOMMU driver detects a critical error caused by a device (e.g. an ATC
+ * invalidation timeout), this function should be used to quarantine the device
+ * at the IOMMU core level.
+ *
+ * The quarantine moves the device's RID and PASIDs to group->blocking_domain to
+ * prevent any further DMA/ATS activity that can potentially corrupt the system
+ * memory due to stale device cache entries.
+ *
+ * This function must not be called from a reset_device_done callback: setting
+ * broken_pending there would race the clear in pci_dev_reset_iommu_done() that
+ * follows. Otherwise, this function is safe to call from any context (including
+ * interrupt handlers), as the actual quarantine is done in an asynchronous work
+ * thread. The caller should have already taken driver-level measures (e.g., ATS
+ * disabled in HW) to contain the fault promptly before calling this function.
+ *
+ * An asynchronous reset can occur while the driver is handling an IOMMU-related
+ * error. The driver is responsible for calling this only for post-reset faults.
+ * A queued or delayed pre-reset fault must be drained or filtered by the driver
+ * before delivery. Otherwise, a stale report would falsely quarantine a freshly
+ * recovered device.
+ *
+ * For PCI devices, the quarantine will be lifted by a successful device reset
+ * via pci_dev_reset_iommu_done(). For non-PCI devices, the quarantine remains
+ * in effect indefinitely until a recovery mechanism is introduced.
+ *
+ * If the device is concurrently being removed or has already been removed from
+ * the IOMMU subsystem, this function will silently return without any action.
+ */
+void iommu_report_device_broken(struct device *dev)
+{
+ struct group_device *gdev;
+
+ /*
+ * We cannot hold group->mutex here. Rely on iommu_group_broken_worker()
+ * to validate dev_has_iommu(). The iommu_group memory is RCU-protected
+ * via kfree_rcu() in iommu_group_release(), and group->devices is an
+ * RCU-protected list, so the lookup runs entirely under rcu_read_lock.
+ *
+ * Note the device might have been concurrently removed from the group
+ * (list_del_rcu) before iommu_deinit_device() cleared the dev->iommu.
+ */
+ rcu_read_lock();
+ gdev = __dev_to_gdev_rcu(dev);
+ if (gdev) {
+ /*
+ * Narrow chance we re-set broken_pending right after a concurrent
+ * worker cleared it. Benign: the worker we are queueing here will
+ * read it true and clear it again (skipping if already blocked);
+ * pci_dev_reset_iommu_done() also clears it on a successful reset.
+ */
+ WRITE_ONCE(gdev->broken_pending, true);
+ schedule_work(&gdev->broken_work);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(iommu_report_device_broken);
+
#if IS_ENABLED(CONFIG_IRQ_MSI_IOMMU)
/**
* iommu_dma_prepare_msi() - Map the MSI page in the IOMMU domain
--
2.43.0
^ permalink raw reply related
* [PATCH v4 09/24] iommu: Add group pointer to struct group_device
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
Though group pointer is in general available at dev->iommu_group, it would
be NULLed by iommu_deinit_device() holding group->mutex.
To introduce an asynchronous worker that would hold the mutex as well, its
disable_work_sync() can only get called afterwards outside the mutex. Then,
using dev->iommu_group would crash the kernel.
Add a group pointer to the gdev to prepare for that. No functional change.
Drop group arguments next to gdev in function parameters.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/iommu.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 4116b28258bde..f745083c032d6 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -84,6 +84,7 @@ enum gdev_blocked {
};
struct group_device {
+ struct iommu_group *group;
struct list_head list;
struct device *dev;
char *name;
@@ -177,8 +178,7 @@ static ssize_t iommu_group_store_type(struct iommu_group *group,
const char *buf, size_t count);
static struct group_device *iommu_group_alloc_device(struct iommu_group *group,
struct device *dev);
-static void __iommu_group_free_device(struct iommu_group *group,
- struct group_device *grp_dev);
+static void __iommu_group_free_device(struct group_device *grp_dev);
static void iommu_domain_init(struct iommu_domain *domain, unsigned int type,
const struct iommu_ops *ops);
@@ -727,7 +727,7 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
iommu_deinit_device(dev);
mutex_unlock(&group->mutex);
if (!IS_ERR(gdev))
- __iommu_group_free_device(group, gdev);
+ __iommu_group_free_device(gdev);
iommu_group_put(group);
return ret;
@@ -751,9 +751,9 @@ int iommu_probe_device(struct device *dev)
return 0;
}
-static void __iommu_group_free_device(struct iommu_group *group,
- struct group_device *grp_dev)
+static void __iommu_group_free_device(struct group_device *grp_dev)
{
+ struct iommu_group *group = grp_dev->group;
struct device *dev = grp_dev->dev;
lockdep_assert_not_held(&group->mutex);
@@ -797,7 +797,7 @@ static void __iommu_group_remove_device(struct device *dev)
mutex_unlock(&group->mutex);
if (to_free)
- __iommu_group_free_device(group, to_free);
+ __iommu_group_free_device(to_free);
/*
* Pairs with the get in iommu_init_device() or
* iommu_group_add_device()
@@ -1304,6 +1304,7 @@ static struct group_device *iommu_group_alloc_device(struct iommu_group *group,
return ERR_PTR(-ENOMEM);
device->dev = dev;
+ device->group = group;
/* Keep dev alive for any in-flight RCU reader of grp_dev->dev. */
get_device(dev);
@@ -4161,9 +4162,9 @@ static int group_device_cmp_dma_alias(struct pci_dev *dev, u16 alias,
&alias);
}
-static bool group_device_dma_alias_is_blocked(struct iommu_group *group,
- struct group_device *gdev)
+static bool group_device_dma_alias_is_blocked(struct group_device *gdev)
{
+ struct iommu_group *group = gdev->group;
struct group_device *sibling;
lockdep_assert_held(&group->mutex);
@@ -4263,7 +4264,7 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
if (WARN_ON(!gdev->blocked))
return;
- if (group_device_dma_alias_is_blocked(group, gdev)) {
+ if (group_device_dma_alias_is_blocked(gdev)) {
/*
* FIXME: DMA aliased devices share the same RID, which would be
* convoluted to handle, as "gdev->blocked" is not sufficient:
--
2.43.0
^ permalink raw reply related
* [PATCH v4 14/24] iommu/arm-smmu-v3: Introduce per-cmdq cmdq_err_handler callback
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
A subsequent change will need arm_smmu_cmdq_issue_cmdlist() to co-clear a
pending CMDQ_ERR after a CMD_SYNC poll timeout. And this needs to be done
for both smmu->cmdq and tegra241-cmdq.
Add a cmdq_err_handler and a paired cmdq_err_lock to struct arm_smmu_cmdq.
arm_smmu_gerror_handler() and tegra241_vintf0_handle_error() now take the
per-cmdq cmdq_err_lock when acking CMDQ_ERR.
tegra241_vintf0_handle_error() also checks (gerror ^ gerrorn) inside the
cmdq_err_lock since a concurrent cmdq_err_handler may have already acked
the error. arm_smmu_gerror_handler() already covers this via its existing
early-exit on no-active-bits.
Impl functions and caller will be added in the subsequent change.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 12 ++++++++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 18 ++++++++++++++----
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 15 ++++++++++++---
3 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 46f9e292a1cc8..604f7edf54158 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -695,6 +695,10 @@ struct arm_smmu_queue_poll {
bool wfe;
};
+struct arm_smmu_cmdq;
+typedef void (*arm_smmu_cmdq_err_fn)(struct arm_smmu_device *smmu,
+ struct arm_smmu_cmdq *cmdq);
+
struct arm_smmu_cmdq {
struct arm_smmu_queue q;
atomic_long_t *valid_map;
@@ -702,6 +706,10 @@ struct arm_smmu_cmdq {
atomic_t lock;
unsigned long *atc_sync_timeouts;
bool (*supports_cmd)(struct arm_smmu_cmd *cmd);
+
+ /* Drain a pending CMDQ_ERR; will hold cmdq_err_lock with irqsave */
+ arm_smmu_cmdq_err_fn cmdq_err_handler;
+ raw_spinlock_t cmdq_err_lock;
};
static inline bool arm_smmu_cmdq_supports_cmd(struct arm_smmu_cmdq *cmdq,
@@ -1163,8 +1171,8 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
size_t dwords, const char *name);
-int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
- struct arm_smmu_cmdq *cmdq);
+int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq,
+ arm_smmu_cmdq_err_fn cmdq_err_handler);
static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master)
{
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index d9fe48989fcd7..fc0757359b783 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2255,13 +2255,18 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
{
u32 gerror, gerrorn, active;
struct arm_smmu_device *smmu = dev;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&smmu->cmdq.cmdq_err_lock, flags);
gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
active = gerror ^ gerrorn;
- if (!(active & GERROR_ERR_MASK))
+ if (!(active & GERROR_ERR_MASK)) {
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
return IRQ_NONE; /* No errors pending */
+ }
dev_warn(smmu->dev,
"unexpected global error reported (0x%08x), this could be serious\n",
@@ -2270,6 +2275,8 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
if (active & GERROR_SFM_ERR) {
/* SMMU is being disabled, so other errors don't matter */
writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+ /* Release before arm_smmu_device_disable() that sleeps */
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
arm_smmu_device_disable(smmu);
return IRQ_HANDLED;
@@ -2297,6 +2304,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
arm_smmu_cmdq_skip_err(smmu);
writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
return IRQ_HANDLED;
}
@@ -4357,13 +4365,15 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
return 0;
}
-int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
- struct arm_smmu_cmdq *cmdq)
+int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq,
+ arm_smmu_cmdq_err_fn cmdq_err_handler)
{
unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
atomic_set(&cmdq->owner_prod, 0);
atomic_set(&cmdq->lock, 0);
+ raw_spin_lock_init(&cmdq->cmdq_err_lock);
+ cmdq->cmdq_err_handler = cmdq_err_handler;
cmdq->valid_map = (atomic_long_t *)devm_bitmap_zalloc(smmu->dev, nents,
GFP_KERNEL);
@@ -4389,7 +4399,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
if (ret)
return ret;
- ret = arm_smmu_cmdq_init(smmu, &smmu->cmdq);
+ ret = arm_smmu_cmdq_init(smmu, &smmu->cmdq, NULL);
if (ret)
return ret;
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 67be62a6e7640..fb2f8f68fa344 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -319,10 +319,19 @@ static void tegra241_vintf0_handle_error(struct tegra241_vintf *vintf)
while (map) {
unsigned long lidx = __ffs64(map);
struct tegra241_vcmdq *vcmdq = vintf->lvcmdqs[lidx];
- u32 gerror = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR));
+ struct arm_smmu_cmdq *cmdq = &vcmdq->cmdq;
+ unsigned long flags;
+ u32 gerror, gerrorn;
- __arm_smmu_cmdq_skip_err(&vintf->cmdqv->smmu, &vcmdq->cmdq);
+ raw_spin_lock_irqsave(&cmdq->cmdq_err_lock, flags);
+ gerror = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR));
+ gerrorn = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERRORN));
+
+ if ((gerror ^ gerrorn) & GERROR_CMDQ_ERR)
+ __arm_smmu_cmdq_skip_err(&vintf->cmdqv->smmu,
+ cmdq);
writel(gerror, REG_VCMDQ_PAGE0(vcmdq, GERRORN));
+ raw_spin_unlock_irqrestore(&cmdq->cmdq_err_lock, flags);
map &= ~BIT_ULL(lidx);
}
}
@@ -643,7 +652,7 @@ static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
q->q_base = q->base_dma & VCMDQ_ADDR;
q->q_base |= FIELD_PREP(VCMDQ_LOG2SIZE, q->llq.max_n_shift);
- return arm_smmu_cmdq_init(smmu, cmdq);
+ return arm_smmu_cmdq_init(smmu, cmdq, NULL);
}
/* VINTF Logical VCMDQ Resource Helpers */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 17/24] iommu/arm-smmu-v3: Add master in arm_smmu_inv for ATS entries
From: Nicolin Chen @ 2026-05-19 3:39 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
Storing the master pointer allows backtracking it from an ATS invalidation
entry, which will be useful when handling ATC invalidation timeouts.
Don't simply swap the "smmu" pointer for the "master": a non-ATS entry may
be shared across multiple devices (masters). An ATS entry is okay since it
is tied to a unique SID.
Master must outlive any concurrent RCU reader iterating the domain->invs,
because inv->master is dereferenced inside the read-side critical section.
Add a synchronize_rcu() in arm_smmu_release_device() before freeing master.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 14 +++++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 604f7edf54158..df6e539f75274 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -738,6 +738,7 @@ enum arm_smmu_inv_type {
struct arm_smmu_inv {
struct arm_smmu_device *smmu;
+ struct arm_smmu_master *master; /* INV_TYPE_ATS* */
u8 type;
u8 size_opcode;
u8 nsize_opcode;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 0e4f34ed036c6..cde2ff2dcc49b 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3211,6 +3211,7 @@ arm_smmu_master_build_inv(struct arm_smmu_master *master,
case INV_TYPE_ATS_FULL:
cur->size_opcode = cur->nsize_opcode = CMDQ_OP_ATC_INV;
cur->ssid = ssid;
+ cur->master = master;
break;
}
@@ -4168,9 +4169,6 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
for (i = 0; i < fwspec->num_ids; i++)
rb_erase(&master->streams[i].node, &smmu->streams);
mutex_unlock(&smmu->streams_mutex);
-
- kfree(master->streams);
- kfree(master->build_invs);
}
static struct iommu_device *arm_smmu_probe_device(struct device *dev)
@@ -4244,6 +4242,16 @@ static void arm_smmu_release_device(struct device *dev)
arm_smmu_remove_master(master);
if (arm_smmu_cdtab_allocated(&master->cd_table))
arm_smmu_free_cd_tables(master);
+
+ /*
+ * The iommu core detaches @dev from every iommu domain before invoking
+ * release_device. So the updated domain->invs no longer references the
+ * @master; IOW, new RCU readers cannot reach it. Wait one grace period
+ * for in-flight readers to drop their references.
+ */
+ synchronize_rcu();
+ kfree(master->streams);
+ kfree(master->build_invs);
kfree(master);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 12/24] iommu/arm-smmu-v3: Mark ATC invalidate timeouts via lockless bitmap
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
An ATC invalidation timeout is a fatal error. While the SMMUv3 hardware is
aware of the timeout via a GERROR interrupt, the driver thread issuing the
commands lacks a direct mechanism to verify whether its specific batch was
the cause or not, as polling the CMD_SYNC status doesn't natively return a
failure code, making it very difficult to coordinate per-device recovery.
Introduce an atc_sync_timeouts bitmap in the cmdq structure to bridge this
gap. When the ISR detects an ATC timeout, set the bit corresponding to the
physical CMDQ index of the faulting CMD_SYNC command.
On the issuer side, after polling completes (or times out), test and clear
its dedicated bit. If set, return -EIO to trigger device quarantine.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 42 ++++++++++++++++++++-
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 16353596e08ad..46f9e292a1cc8 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -700,6 +700,7 @@ struct arm_smmu_cmdq {
atomic_long_t *valid_map;
atomic_t owner_prod;
atomic_t lock;
+ unsigned long *atc_sync_timeouts;
bool (*supports_cmd)(struct arm_smmu_cmd *cmd);
};
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 9be589d14a3bd..1065301a54eeb 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -343,7 +343,10 @@ void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
* at the CMD_SYNC. Attempt to complete other pending commands
* by repeating the CMD_SYNC, though we might well end up back
* here since the ATC invalidation may still be pending.
+ *
+ * Mark the faulty batch in the bitmap for the issuer to match.
*/
+ set_bit(Q_IDX(&q->llq, cons), cmdq->atc_sync_timeouts);
return;
case CMDQ_ERR_CERROR_ILL_IDX:
default:
@@ -750,6 +753,14 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
queue_write(Q_ENT(&cmdq->q, prod), cmd_sync.data,
ARRAY_SIZE(cmd_sync.data));
+ /*
+ * Clear any stale ATC-timeout bit left in the slot from a prior
+ * wraparound, before the slot becomes visible to the SMMU. Must
+ * do this prior to step 3 to prevent potentially races with the
+ * GERROR ISR calling set_bit() for our own CMD_SYNC.
+ */
+ clear_bit(Q_IDX(&llq, prod), cmdq->atc_sync_timeouts);
+
/*
* In order to determine completion of our CMD_SYNC, we must
* ensure that the queue can't wrap twice without us noticing.
@@ -796,9 +807,33 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
/* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */
if (sync) {
+ u32 sync_prod;
+
llq.prod = queue_inc_prod_n(&llq, n);
+ sync_prod = llq.prod;
ret = arm_smmu_cmdq_poll_until_sync(smmu, cmdq, &llq);
- if (ret) {
+
+ /*
+ * Test atc_sync_timeouts first and see if there is ATC timeout
+ * resulted from this cmdlist. Return -EIO to separate from the
+ * ARM_SMMU_POLL_TIMEOUT_US software timeout.
+ *
+ * FIXME possible unhandled ATC invalidation timeout scenario:
+ * PCI Completion Timeout can be set to a range longer than the
+ * ARM_SMMU_POLL_TIMEOUT_US software timeout. -ETIMEDOUT can be
+ * returned by arm_smmu_cmdq_poll_until_sync() while the ATC_INV
+ * is still pending and not yet reflected in GERROR, so the bit
+ * on atc_sync_timeouts is not set. In this case, we can hardly
+ * do anything here, since the command queue HW is still pending
+ * on the ATC command.
+ */
+ if (test_and_clear_bit(Q_IDX(&llq, sync_prod),
+ cmdq->atc_sync_timeouts)) {
+ dev_err_ratelimited(smmu->dev,
+ "CMD_SYNC for ATC_INV timeout at prod=0x%08x\n",
+ sync_prod);
+ ret = -EIO;
+ } else if (ret) {
dev_err_ratelimited(smmu->dev,
"CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n",
llq.prod,
@@ -4332,6 +4367,11 @@ int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
if (!cmdq->valid_map)
return -ENOMEM;
+ cmdq->atc_sync_timeouts =
+ devm_bitmap_zalloc(smmu->dev, nents, GFP_KERNEL);
+ if (!cmdq->atc_sync_timeouts)
+ return -ENOMEM;
+
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 21/24] iommu/arm-smmu-v3: Move arm_smmu_invs_for_each_entry to header
From: Nicolin Chen @ 2026-05-19 3:39 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
A subsequent change will use this helper in a lockless context. Since this
this is a macro, move it to the header file so other functions can use it.
Also, add READ_ONCE to invs->num_invs for lockless use.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 28 +++++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 ---------------------
2 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 2074814534fef..b5ace01c05a5d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -803,6 +803,34 @@ struct arm_smmu_invs {
struct arm_smmu_inv inv[] __counted_by(max_invs);
};
+/* Invalidation array manipulation functions */
+static inline struct arm_smmu_inv *
+arm_smmu_invs_iter_next(struct arm_smmu_invs *invs, size_t next, size_t *idx)
+{
+ while (true) {
+ if (next >= READ_ONCE(invs->num_invs)) {
+ *idx = next;
+ return NULL;
+ }
+ if (!READ_ONCE(invs->inv[next].users)) {
+ next++;
+ continue;
+ }
+ *idx = next;
+ return &invs->inv[next];
+ }
+}
+
+/**
+ * arm_smmu_invs_for_each_entry - Iterate over all non-trash entries in invs
+ * @invs: the base invalidation array
+ * @idx: a stack variable of 'size_t', to store the array index
+ * @cur: a stack variable of 'struct arm_smmu_inv *'
+ */
+#define arm_smmu_invs_for_each_entry(invs, idx, cur) \
+ for (cur = arm_smmu_invs_iter_next(invs, 0, &(idx)); cur; \
+ cur = arm_smmu_invs_iter_next(invs, idx + 1, &(idx)))
+
static inline struct arm_smmu_invs *arm_smmu_invs_alloc(size_t num_invs)
{
struct arm_smmu_invs *new_invs;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4f2b23b1e8163..c95297acf2cfe 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -979,34 +979,6 @@ static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused
*/
}
-/* Invalidation array manipulation functions */
-static inline struct arm_smmu_inv *
-arm_smmu_invs_iter_next(struct arm_smmu_invs *invs, size_t next, size_t *idx)
-{
- while (true) {
- if (next >= invs->num_invs) {
- *idx = next;
- return NULL;
- }
- if (!READ_ONCE(invs->inv[next].users)) {
- next++;
- continue;
- }
- *idx = next;
- return &invs->inv[next];
- }
-}
-
-/**
- * arm_smmu_invs_for_each_entry - Iterate over all non-trash entries in invs
- * @invs: the base invalidation array
- * @idx: a stack variable of 'size_t', to store the array index
- * @cur: a stack variable of 'struct arm_smmu_inv *'
- */
-#define arm_smmu_invs_for_each_entry(invs, idx, cur) \
- for (cur = arm_smmu_invs_iter_next(invs, 0, &(idx)); cur; \
- cur = arm_smmu_invs_iter_next(invs, idx + 1, &(idx)))
-
static int arm_smmu_inv_cmp(const struct arm_smmu_inv *inv_l,
const struct arm_smmu_inv *inv_r)
{
--
2.43.0
^ permalink raw reply related
* [PATCH v4 10/24] iommu: Add __iommu_group_block_device helper
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
Move the RID/PASID blocking routine into a separate helper, which will be
reused by a new function to quarantine the device but does not bother the
gdev->reset_depth counter.
Also, document the severity ordering at enum gdev_blocked.
No functional changes.
Suggested-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/iommu.c | 106 ++++++++++++++++++++++++------------------
1 file changed, 60 insertions(+), 46 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index f745083c032d6..b150d22d8015f 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -77,6 +77,7 @@ struct iommu_group {
#define dev_iommu_group_rcu(dev) \
(*((struct iommu_group __rcu __force **)&(dev)->iommu_group))
+/* A bigger number indicates a higher severity */
enum gdev_blocked {
BLOCKED_NO = 0, /* Not blocked */
BLOCKED_RESETTING, /* PCI reset in flight */
@@ -4053,6 +4054,62 @@ int iommu_replace_group_handle(struct iommu_group *group,
}
EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, "IOMMUFD_INTERNAL");
+/* Caller can use this function on a blocked @gdev just to update the @reason */
+static int __iommu_group_block_device(struct group_device *gdev,
+ enum gdev_blocked reason)
+{
+ struct iommu_group *group = gdev->group;
+ unsigned long pasid;
+ void *entry;
+ int ret;
+
+ lockdep_assert_held(&group->mutex);
+
+ /* Device might be already blocked for a quarantine */
+ if (gdev->blocked) {
+ /* Escalate the severity */
+ gdev->blocked = max(gdev->blocked, reason);
+ return 0;
+ }
+
+ ret = __iommu_group_alloc_blocking_domain(group);
+ if (ret)
+ return ret;
+
+ /* Stage RID domain at blocking_domain while retaining group->domain */
+ if (group->domain != group->blocking_domain) {
+ ret = __iommu_attach_device(group->blocking_domain, gdev->dev,
+ group->domain);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Update gdev->blocked upon the domain change, as it is used to return
+ * the correct domain in iommu_driver_get_domain_for_dev() that might be
+ * called in a set_dev_pasid callback function.
+ */
+ gdev->blocked = reason;
+
+ /*
+ * Stage PASID domains at blocking_domain while retaining pasid_array.
+ *
+ * The pasid_array is mostly fenced by group->mutex, except one reader
+ * in iommu_attach_handle_get(), so it's safe to read without xa_lock.
+ */
+ if (gdev->dev->iommu->max_pasids > 0) {
+ xa_for_each_start(&group->pasid_array, pasid, entry, 1) {
+ struct iommu_domain *pasid_dom =
+ pasid_array_entry_to_domain(entry);
+
+ iommu_remove_dev_pasid(gdev->dev, pasid, pasid_dom);
+ }
+ }
+
+ group->recovery_cnt++;
+ return 0;
+}
+
/**
* pci_dev_reset_iommu_prepare() - Block IOMMU to prepare for a PCI device reset
* @pdev: PCI device that is going to enter a reset routine
@@ -4086,8 +4143,6 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
{
struct iommu_group *group = pdev->dev.iommu_group;
struct group_device *gdev;
- unsigned long pasid;
- void *entry;
int ret;
if (!pci_ats_supported(pdev) || !dev_has_iommu(&pdev->dev))
@@ -4102,49 +4157,9 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
if (gdev->reset_depth++)
return 0;
- /* Device might be already blocked for a quarantine */
- if (gdev->blocked)
- return 0;
-
- ret = __iommu_group_alloc_blocking_domain(group);
- if (ret) {
+ ret = __iommu_group_block_device(gdev, BLOCKED_RESETTING);
+ if (ret)
gdev->reset_depth--;
- return ret;
- }
-
- /* Stage RID domain at blocking_domain while retaining group->domain */
- if (group->domain != group->blocking_domain) {
- ret = __iommu_attach_device(group->blocking_domain, &pdev->dev,
- group->domain);
- if (ret) {
- gdev->reset_depth--;
- return ret;
- }
- }
-
- /*
- * Update gdev->blocked upon the domain change, as it is used to return
- * the correct domain in iommu_driver_get_domain_for_dev() that might be
- * called in a set_dev_pasid callback function.
- */
- gdev->blocked = BLOCKED_RESETTING;
-
- /*
- * Stage PASID domains at blocking_domain while retaining pasid_array.
- *
- * The pasid_array is mostly fenced by group->mutex, except one reader
- * in iommu_attach_handle_get(), so it's safe to read without xa_lock.
- */
- if (pdev->dev.iommu->max_pasids > 0) {
- xa_for_each_start(&group->pasid_array, pasid, entry, 1) {
- struct iommu_domain *pasid_dom =
- pasid_array_entry_to_domain(entry);
-
- iommu_remove_dev_pasid(&pdev->dev, pasid, pasid_dom);
- }
- }
-
- group->recovery_cnt++;
return ret;
}
EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_prepare);
@@ -4256,8 +4271,7 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
dev_err_ratelimited(
&pdev->dev,
"Reset failed. Keep it blocked to protect memory\n");
- if (gdev->blocked == BLOCKED_RESETTING)
- gdev->blocked = BLOCKED_RESET_FAILED;
+ WARN_ON(__iommu_group_block_device(gdev, BLOCKED_RESET_FAILED));
return;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 13/24] iommu/arm-smmu-v3: Skip remaining GERROR causes on SFM
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
When the SMMU enters Service Failure Mode (SFM), arm_smmu_device_disable()
clears CR0 and the SMMU stops processing requests entirely. The remaining
GERROR causes (MSI write aborts, PRIQ/EVTQ aborts, CMDQ_ERR) are moot at
that point: the cmdq is dead so arm_smmu_cmdq_skip_err() would just twiddle
bookkeeping for a queue nobody's reading, and the per-cause dev_warn lines
add little diagnostic value beyond the SFM message itself.
After arm_smmu_device_disable(), ack GERRORN and return. This intentionally
duplicates the last two lines of the function because this SFM path will be
slightly different.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 1065301a54eeb..d9fe48989fcd7 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2268,8 +2268,11 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
active);
if (active & GERROR_SFM_ERR) {
+ /* SMMU is being disabled, so other errors don't matter */
+ writel(gerror, smmu->base + ARM_SMMU_GERRORN);
dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
arm_smmu_device_disable(smmu);
+ return IRQ_HANDLED;
}
if (active & GERROR_MSI_GERROR_ABT_ERR)
--
2.43.0
^ permalink raw reply related
* [PATCH v4 05/24] iommu: Add reset_device_done callback for hardware fault recovery
From: Nicolin Chen @ 2026-05-19 3:38 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
When an IOMMU hardware detects an error due to a faulty device (e.g. an ATS
invalidation timeout), IOMMU drivers may quarantine the device by disabling
specific hardware features or dropping translation capabilities.
To recover from these states, the IOMMU driver needs a reliable signal that
the underlying physical hardware has been cleanly reset (e.g., via PCIe AER
or a sysfs Function Level Reset) so as to lift the quarantine.
Introduce a reset_device_done callback in struct iommu_ops. Trigger it from
the existing pci_dev_reset_iommu_done() path to notify the underlying IOMMU
driver that the device's internal state has been sanitized, when the result
indicates a successful physical reset.
As the initial use case, this will be used by ATS-capable PCI devices.
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
include/linux/iommu.h | 4 ++++
drivers/iommu/iommu.c | 12 ++++++++++++
2 files changed, 16 insertions(+)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e191d30d228ac..6c124e9e9af8b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -629,6 +629,9 @@ __iommu_copy_struct_to_user(const struct iommu_user_data *dst_data,
* @release_device: Remove device from iommu driver handling
* @probe_finalize: Do final setup work after the device is added to an IOMMU
* group and attached to the groups domain
+ * @reset_device_done: Notify the driver that a device has reset successfully.
+ * Note that the core invokes the callback function while
+ * holding the group->mutex
* @device_group: find iommu group for a particular device
* @get_resv_regions: Request list of reserved regions for a device
* @of_xlate: add OF master IDs to iommu grouping
@@ -686,6 +689,7 @@ struct iommu_ops {
struct iommu_device *(*probe_device)(struct device *dev);
void (*release_device)(struct device *dev);
void (*probe_finalize)(struct device *dev);
+ void (*reset_device_done)(struct device *dev);
struct iommu_group *(*device_group)(struct device *dev);
/* Request/Free a list of reserved regions for a device */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 6c92b7a2b14cc..e68c7b142ad5a 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -4182,12 +4182,14 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
{
struct iommu_group *group = pdev->dev.iommu_group;
enum gdev_blocked old_gdev_blocked;
+ const struct iommu_ops *ops;
struct group_device *gdev;
unsigned long pasid;
void *entry;
if (!pci_ats_supported(pdev) || !dev_has_iommu(&pdev->dev))
return;
+ ops = dev_iommu_ops(&pdev->dev);
guard(mutex)(&group->mutex);
@@ -4249,6 +4251,16 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result)
"DMA-aliased sibling may be prematurely unblocked\n");
}
+ /*
+ * A PCI device might have been in an error state, so the IOMMU driver
+ * had to quarantine the device by disabling specific hardware features
+ * or dropping translation capability. Here notify the IOMMU driver as
+ * a reliable signal that the faulty PCI device has been cleanly reset
+ * so now it can lift its quarantine and restore full functionality.
+ */
+ if (ops->reset_device_done)
+ ops->reset_device_done(&pdev->dev);
+
/*
* Re-attach RID domain back to group->domain
*
--
2.43.0
^ permalink raw reply related
* [PATCH v4 22/24] iommu/arm-smmu-v3: Introduce master->ats_invs
From: Nicolin Chen @ 2026-05-19 3:39 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Bjorn Helgaas,
Jason Gunthorpe
Cc: Rafael J . Wysocki, Len Brown, Pranjal Shrivastava, Mostafa Saleh,
Lu Baolu, Kevin Tian, linux-arm-kernel, iommu, linux-kernel,
linux-acpi, linux-pci, vsethi, Shuai Xue
In-Reply-To: <cover.1779161849.git.nicolinc@nvidia.com>
Similar to master->build_invs used by a per-domain invalidation, add a new
master->ats_invs to be used by arm_smmu_atc_inv_master().
Since arm_smmu_cmdq_batch_init_cmd() now takes an invs pointer, pass it in.
This will be useful by arm_smmu_cmdq_batch_issue() to backtrack the master
pointer from a timed out ATC invalidation command in a subsequent change.
Also replace the streams loop with arm_smmu_invs_for_each_entry() as it is
initialized (except ssid) upon allocation.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 46 ++++++++++++++++++---
2 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index b5ace01c05a5d..186efcbed1ea9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1045,6 +1045,8 @@ struct arm_smmu_master {
* iommu_group mutex.
*/
struct arm_smmu_invs *build_invs;
+ /* Scratch memory for arm_smmu_atc_inv_master() to build an ATS array */
+ struct arm_smmu_invs *ats_invs;
struct arm_smmu_vmaster *vmaster; /* use smmu->streams_mutex */
/* Locked by the iommu core using the group mutex */
struct arm_smmu_ctx_desc_cfg cd_table;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index c95297acf2cfe..9591e4ab2b14a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2407,21 +2407,28 @@ arm_smmu_atc_inv_to_cmd(u32 sid, int ssid, unsigned long iova, size_t size)
static int arm_smmu_atc_inv_master(struct arm_smmu_master *master,
ioasid_t ssid)
{
- int i;
+ struct arm_smmu_invs *invs = master->ats_invs;
struct arm_smmu_cmd cmd;
struct arm_smmu_cmdq_batch cmds;
+ struct arm_smmu_inv *inv;
+ size_t i;
+
+ /* No concurrent user on master->ats_invs */
+ iommu_group_mutex_assert(master->dev);
/* Do not issue ATC_INV that will definitely time out */
if (READ_ONCE(master->ats_broken))
return 0;
cmd = arm_smmu_make_cmd_atc_inv_all(0, IOMMU_NO_PASID);
- arm_smmu_cmdq_batch_init_cmd(master->smmu, &cmds, &cmd, NULL);
- for (i = 0; i < master->num_streams; i++)
+ arm_smmu_cmdq_batch_init_cmd(master->smmu, &cmds, &cmd, invs);
+
+ arm_smmu_invs_for_each_entry(invs, i, inv) {
+ inv->ssid = ssid;
arm_smmu_cmdq_batch_add_cmd(
master->smmu, &cmds,
- arm_smmu_make_cmd_atc_inv_all(master->streams[i].id,
- ssid));
+ arm_smmu_make_cmd_atc_inv_all(inv->id, ssid));
+ }
return arm_smmu_cmdq_batch_submit(master->smmu, &cmds);
}
@@ -4087,6 +4094,18 @@ static int arm_smmu_stream_id_cmp(const void *_l, const void *_r)
return cmp_int(*l, *r);
}
+static void arm_smmu_master_init_ats_inv(struct arm_smmu_master *master,
+ struct arm_smmu_inv *inv, u32 sid)
+{
+ inv->id = sid;
+ inv->users = 1;
+ inv->master = master;
+ inv->smmu = master->smmu;
+ inv->type = INV_TYPE_ATS;
+ inv->size_opcode = CMDQ_OP_ATC_INV;
+ inv->nsize_opcode = CMDQ_OP_ATC_INV;
+}
+
static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
struct arm_smmu_master *master)
{
@@ -4105,11 +4124,19 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
/* Base case has 1 ASID entry or maximum 2 VMID entries */
master->build_invs = arm_smmu_invs_alloc(2);
} else {
+ master->ats_invs = arm_smmu_invs_alloc(fwspec->num_ids);
+ if (!master->ats_invs) {
+ kfree(master->streams);
+ return -ENOMEM;
+ }
+ master->ats_invs->has_ats = true;
+
/* ATS case adds num_ids of entries, on top of the base case */
master->build_invs = arm_smmu_invs_alloc(2 + fwspec->num_ids);
}
if (!master->build_invs) {
kfree(master->streams);
+ kfree(master->ats_invs);
return -ENOMEM;
}
@@ -4125,6 +4152,13 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
sizeof(master->streams[0]), arm_smmu_stream_id_cmp,
NULL);
+ if (master->ats_invs) {
+ for (i = 0; i < fwspec->num_ids; i++)
+ arm_smmu_master_init_ats_inv(master,
+ &master->ats_invs->inv[i],
+ master->streams[i].id);
+ }
+
mutex_lock(&smmu->streams_mutex);
for (i = 0; i < fwspec->num_ids; i++) {
struct arm_smmu_stream *new_stream = &master->streams[i];
@@ -4159,6 +4193,7 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
for (i--; i >= 0; i--)
rb_erase(&master->streams[i].node, &smmu->streams);
kfree(master->streams);
+ kfree(master->ats_invs);
kfree(master->build_invs);
}
mutex_unlock(&smmu->streams_mutex);
@@ -4261,6 +4296,7 @@ static void arm_smmu_release_device(struct device *dev)
*/
synchronize_rcu();
kfree(master->streams);
+ kfree(master->ats_invs);
kfree(master->build_invs);
kfree(master);
}
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox