* [PATCH v3 08/14] arm64: dts: st: add sai1 pins for stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add the sai1 pins used on MicroGEA-STM32MP257-RMM board.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index eab8ebe71660..ab1e62cf2bfc 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -520,6 +520,51 @@ pins {
};
};
+ /omit-if-no-ref/
+ sai1a_pins_a: sai1a-0 {
+ pins1 {
+ pinmux = <STM32_PINMUX('D', 9, AF3)>, /* SAI1_SD_A */
+ <STM32_PINMUX('D', 8, AF3)>, /* SAI1_FS_A */
+ <STM32_PINMUX('D', 10, AF3)>; /* SAI1_SCK_A */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <1>;
+ };
+ pins2 {
+ pinmux = <STM32_PINMUX('D', 11, AF3)>; /* SAI1_MCLK_A */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <2>;
+ };
+ };
+
+ /omit-if-no-ref/
+ sai1a_sleep_pins_a: sai1a-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 9, ANALOG)>, /* SAI1_SD_A */
+ <STM32_PINMUX('D', 8, ANALOG)>, /* SAI1_FS_A */
+ <STM32_PINMUX('D', 10, ANALOG)>, /* SAI1_SCK_A */
+ <STM32_PINMUX('D', 11, ANALOG)>; /* SAI1_MCLK_A */
+ };
+ };
+
+ /omit-if-no-ref/
+ sai1b_pins_a: sai1b-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 4, AF4)>; /* SAI1_SD_B */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ /omit-if-no-ref/
+ sai1b_sleep_pins_a: sai1b-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 4, ANALOG)>; /* SAI1_SD_B */
+ };
+ };
+
/omit-if-no-ref/
sdmmc1_b4_pins_a: sdmmc1-b4-0 {
pins1 {
--
2.43.0
^ permalink raw reply related
* [PATCH v3 06/14] arm64: dts: st: add can1 pins for stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add the can1 pins used on MicroGEA-STM32MP257-RMM board.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index 05bd07a0a561..4be01a6574c7 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -331,6 +331,28 @@ pins {
};
};
+ /omit-if-no-ref/
+ m_can1_pins_a: m-can1-0 {
+ pins1 {
+ pinmux = <STM32_PINMUX('B', 9, AF7)>; /* CAN1_TX */
+ slew-rate = <1>;
+ drive-push-pull;
+ bias-disable;
+ };
+ pins2 {
+ pinmux = <STM32_PINMUX('B', 11, AF7)>; /* CAN1_RX */
+ bias-disable;
+ };
+ };
+
+ /omit-if-no-ref/
+ m_can1_sleep_pins_a: m-can1-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('B', 9, ANALOG)>, /* CAN1_TX */
+ <STM32_PINMUX('B', 11, ANALOG)>; /* CAN1_RX */
+ };
+ };
+
/omit-if-no-ref/
ospi_port1_clk_pins_a: ospi-port1-clk-0 {
pins {
--
2.43.0
^ permalink raw reply related
* [PATCH v3 07/14] arm64: dts: st: add pwm2/pwm4 pins for stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add the pwm2 and pwm4 pins used on MicroGEA-STM32MP257-RMM board.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index 4be01a6574c7..eab8ebe71660 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -433,6 +433,23 @@ pins {
};
};
+ /omit-if-no-ref/
+ pwm2_pins_a: pwm2-0 {
+ pins {
+ pinmux = <STM32_PINMUX('I', 7, AF7)>; /* TIM2_CH1 */
+ bias-pull-down;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ /omit-if-no-ref/
+ pwm2_sleep_pins_a: pwm2-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('I', 7, ANALOG)>; /* TIM2_CH1 */
+ };
+ };
+
/omit-if-no-ref/
pwm3_pins_a: pwm3-0 {
pins {
@@ -450,6 +467,23 @@ pins {
};
};
+ /omit-if-no-ref/
+ pwm4_pins_a: pwm4-0 {
+ pins {
+ pinmux = <STM32_PINMUX('A', 12, AF7)>; /* TIM4_CH1 */
+ bias-pull-down;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ /omit-if-no-ref/
+ pwm4_sleep_pins_a: pwm4-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('A', 12, ANALOG)>; /* TIM4_CH1 */
+ };
+ };
+
/omit-if-no-ref/
pwm8_pins_a: pwm8-0 {
pins {
--
2.43.0
^ permalink raw reply related
* [PATCH v3 05/14] arm64: dts: st: add ltdc pins for stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add the LTDC pins used on MicroGEA-STM32MP257-RMM board.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index db485b9ed904..05bd07a0a561 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -260,6 +260,77 @@ pins {
};
};
+ /omit-if-no-ref/
+ ltdc_pins_a: ltdc-0 {
+ pins {
+ pinmux = <STM32_PINMUX('C', 6, AF14)>, /* LCD_CLK */
+ <STM32_PINMUX('G', 2, AF13)>, /* LCD_HSYNC */
+ <STM32_PINMUX('G', 1, AF13)>, /* LCD_VSYNC */
+ <STM32_PINMUX('C', 5, AF14)>, /* LCD_DE */
+ <STM32_PINMUX('H', 4, AF10)>, /* LCD_R0 */
+ <STM32_PINMUX('F', 7, AF13)>, /* LCD_R1 */
+ <STM32_PINMUX('C', 11, AF13)>, /* LCD_R2 */
+ <STM32_PINMUX('A', 1, AF11)>, /* LCD_R3 */
+ <STM32_PINMUX('B', 15, AF13)>, /* LCD_R4 */
+ <STM32_PINMUX('G', 3, AF13)>, /* LCD_R5 */
+ <STM32_PINMUX('A', 10, AF12)>, /* LCD_R6 */
+ <STM32_PINMUX('G', 7, AF13)>, /* LCD_R7 */
+ <STM32_PINMUX('F', 8, AF13)>, /* LCD_G0 */
+ <STM32_PINMUX('H', 5, AF10)>, /* LCD_G1 */
+ <STM32_PINMUX('C', 9, AF13)>, /* LCD_G2 */
+ <STM32_PINMUX('C', 10, AF13)>, /* LCD_G3 */
+ <STM32_PINMUX('A', 6, AF10)>, /* LCD_G4 */
+ <STM32_PINMUX('G', 11, AF13)>, /* LCD_G5 */
+ <STM32_PINMUX('G', 12, AF13)>, /* LCD_G6 */
+ <STM32_PINMUX('A', 9, AF12)>, /* LCD_G7 */
+ <STM32_PINMUX('F', 6, AF13)>, /* LCD_B0 */
+ <STM32_PINMUX('A', 3, AF11)>, /* LCD_B1 */
+ <STM32_PINMUX('G', 15, AF13)>, /* LCD_B2 */
+ <STM32_PINMUX('I', 0, AF13)>, /* LCD_B3 */
+ <STM32_PINMUX('I', 1, AF13)>, /* LCD_B4 */
+ <STM32_PINMUX('A', 7, AF10)>, /* LCD_B5 */
+ <STM32_PINMUX('F', 5, AF13)>, /* LCD_B6 */
+ <STM32_PINMUX('I', 4, AF13)>; /* LCD_B7 */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ /omit-if-no-ref/
+ ltdc_sleep_pins_a: ltdc-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('C', 6, ANALOG)>, /* LCD_CLK */
+ <STM32_PINMUX('G', 2, ANALOG)>, /* LCD_HSYNC */
+ <STM32_PINMUX('G', 1, ANALOG)>, /* LCD_VSYNC */
+ <STM32_PINMUX('C', 5, ANALOG)>, /* LCD_DE */
+ <STM32_PINMUX('H', 4, ANALOG)>, /* LCD_R0 */
+ <STM32_PINMUX('F', 7, ANALOG)>, /* LCD_R1 */
+ <STM32_PINMUX('C', 11, ANALOG)>, /* LCD_R2 */
+ <STM32_PINMUX('A', 1, ANALOG)>, /* LCD_R3 */
+ <STM32_PINMUX('B', 15, ANALOG)>, /* LCD_R4 */
+ <STM32_PINMUX('G', 3, ANALOG)>, /* LCD_R5 */
+ <STM32_PINMUX('A', 10, ANALOG)>, /* LCD_R6 */
+ <STM32_PINMUX('G', 7, ANALOG)>, /* LCD_R7 */
+ <STM32_PINMUX('F', 8, ANALOG)>, /* LCD_G0 */
+ <STM32_PINMUX('H', 5, ANALOG)>, /* LCD_G1 */
+ <STM32_PINMUX('C', 9, ANALOG)>, /* LCD_G2 */
+ <STM32_PINMUX('C', 10, ANALOG)>, /* LCD_G3 */
+ <STM32_PINMUX('A', 6, ANALOG)>, /* LCD_G4 */
+ <STM32_PINMUX('G', 11, ANALOG)>, /* LCD_G5 */
+ <STM32_PINMUX('G', 12, ANALOG)>, /* LCD_G6 */
+ <STM32_PINMUX('A', 9, ANALOG)>, /* LCD_G7 */
+ <STM32_PINMUX('F', 6, ANALOG)>, /* LCD_B0 */
+ <STM32_PINMUX('A', 3, ANALOG)>, /* LCD_B1 */
+ <STM32_PINMUX('G', 15, ANALOG)>, /* LCD_B2 */
+ <STM32_PINMUX('I', 0, ANALOG)>, /* LCD_B3 */
+ <STM32_PINMUX('I', 1, ANALOG)>, /* LCD_B4 */
+ <STM32_PINMUX('A', 7, ANALOG)>, /* LCD_B5 */
+ <STM32_PINMUX('F', 5, ANALOG)>, /* LCD_B6 */
+ <STM32_PINMUX('I', 4, ANALOG)>; /* LCD_B7 */
+ };
+ };
+
/omit-if-no-ref/
ospi_port1_clk_pins_a: ospi-port1-clk-0 {
pins {
--
2.43.0
^ permalink raw reply related
* [PATCH v3 04/14] arm64: dts: st: add i2c1 pins for stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add the i2c1 pins used on MicroGEA-STM32MP257-RMM board.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index 456ece7f8ebc..db485b9ed904 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -203,6 +203,25 @@ pins {
};
};
+ /omit-if-no-ref/
+ i2c1_pins_a: i2c1-0 {
+ pins {
+ pinmux = <STM32_PINMUX('G', 13, AF9)>, /* I2C1_SCL */
+ <STM32_PINMUX('A', 2, AF10)>; /* I2C1_SDA */
+ bias-disable;
+ drive-open-drain;
+ slew-rate = <0>;
+ };
+ };
+
+ /omit-if-no-ref/
+ i2c1_sleep_pins_a: i2c1-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('G', 13, ANALOG)>, /* I2C1_SCL */
+ <STM32_PINMUX('A', 2, ANALOG)>; /* I2C1_SDA */
+ };
+ };
+
/omit-if-no-ref/
i2c2_pins_a: i2c2-0 {
pins {
--
2.43.0
^ permalink raw reply related
* [PATCH v3 03/14] arm64: dts: st: add CAN1 support on stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
The controller is compliant with ISO 11898-1: 2015 (CAN protocol
specification version 2.0 part A, B) and CAN FD protocol specification
version 1.0.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v2)
Changes in v2:
- Add resets property to dts CAN node. Suggested by Sashiko.
arch/arm64/boot/dts/st/stm32mp253.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp253.dtsi b/arch/arm64/boot/dts/st/stm32mp253.dtsi
index eeceb086252b..7e82f01fdc10 100644
--- a/arch/arm64/boot/dts/st/stm32mp253.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp253.dtsi
@@ -43,6 +43,22 @@ &optee {
};
&rifsc {
+ m_can1: can@402d0000 {
+ compatible = "bosch,m_can";
+ reg = <0x402d0000 0x400>, <0x40310000 0xd50>;
+ reg-names = "m_can", "message_ram";
+ interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "int0", "int1";
+ clocks = <&rcc CK_BUS_FDCAN>, <&rcc CK_KER_FDCAN>;
+ clock-names = "hclk", "cclk";
+ resets = <&rcc FDCAN_R>;
+ bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>;
+ access-controllers = <&rifsc 56>;
+ power-domains = <&CLUSTER_PD>;
+ status = "disabled";
+ };
+
ethernet2: ethernet@482d0000 {
compatible = "st,stm32mp25-dwmac", "snps,dwmac-5.20";
reg = <0x482d0000 0x4000>;
--
2.43.0
^ permalink raw reply related
* [PATCH v3 02/14] arm64: dts: st: add SDMMC2 support on stm32mp25
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Conor Dooley,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
The SDMMC2 controller supports SD cards, eMMC memories and SDIO devices.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
Changes in v3:
- Add power-domains property. Suggested by Sashiko.
arch/arm64/boot/dts/st/stm32mp251.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi
index 673fbc5632e6..faa1355948e8 100644
--- a/arch/arm64/boot/dts/st/stm32mp251.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi
@@ -1667,6 +1667,22 @@ sdmmc1: mmc@48220000 {
status = "disabled";
};
+ sdmmc2: mmc@48230000 {
+ compatible = "st,stm32mp25-sdmmc2", "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x00353180>;
+ reg = <0x48230000 0x400>, <0x44230800 0x8>;
+ interrupts = <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc CK_KER_SDMMC2>;
+ clock-names = "apb_pclk";
+ resets = <&rcc SDMMC2_R>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <120000000>;
+ access-controllers = <&rifsc 77>;
+ power-domains = <&CLUSTER_PD>;
+ status = "disabled";
+ };
+
ethernet1: ethernet@482c0000 {
compatible = "st,stm32mp25-dwmac", "snps,dwmac-5.20";
reg = <0x482c0000 0x4000>;
--
2.43.0
^ permalink raw reply related
* [PATCH v3 01/14] dt-bindings: arm: stm32: support Engicam MicroGEA-STM32MP257-RMM board
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Conor Dooley, Alexandre Torgue, Amelie Delaunay,
Christophe Parant, Conor Dooley, Himanshu Bhavani,
Krzysztof Kozlowski, Maxime Coquelin, Rob Herring, devicetree,
linux-arm-kernel, linux-stm32
In-Reply-To: <20260605062900.368376-1-dario.binacchi@amarulasolutions.com>
Add devicetree bindings for Engicam MicroGEA-STM32MP257-RMM board based
on the Engicam MicroGEA-STM32MP257 SoM (System-on-Module).
The use of an enum for a single element is justified by the future
addition of other boards based on the same SoM.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
(no changes since v2)
Changes in v2:
- Add Acked-by of Conor Dooley for patch 0/1 "dt-bindings: arm: stm32:
support Engicam MicroGEA-STM32MP257-RMM board"
Documentation/devicetree/bindings/arm/stm32/stm32.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml
index c6af3a46364f..c5ce81e3ce45 100644
--- a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml
+++ b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml
@@ -203,6 +203,13 @@ properties:
- st,stm32mp257f-ev1
- const: st,stm32mp257
+ - description: Engicam MicroGEA STM32MP257 SoM based Boards
+ items:
+ - enum:
+ - engicam,microgea-stm32mp257-rmm
+ - const: engicam,microgea-stm32mp257
+ - const: st,stm32mp257
+
- description: ST STM32MP235 based Boards
items:
- enum:
--
2.43.0
^ permalink raw reply related
* [PATCH v3 00/14] arm64: support Engicam MicroGEA-STM32MP257-RMM board
From: Dario Binacchi @ 2026-06-05 6:27 UTC (permalink / raw)
To: linux-kernel
Cc: domenico.acri, francesco.utel, michael, linux-amarula,
Dario Binacchi, Alexandre Torgue, Amelie Delaunay,
Bjorn Andersson, Christophe Parant, Conor Dooley,
Dmitry Baryshkov, Eric Biggers, Florian Fainelli,
Geert Uytterhoeven, Himanshu Bhavani, Huang Shijie,
Krzysztof Kozlowski, Krzysztof Kozlowski, Luca Weiss,
Maxime Coquelin, Michal Simek, Rob Herring, Sven Peter,
devicetree, linux-arm-kernel, linux-stm32
This series adds initial support for the Engicam MicroGEA-STM32MP257-RMM
board based on the MicroGEA-STM32MP257 SoM.
The support includes device tree descriptions for both the SoM and the
carrier board, together with the required pinctrl definitions for the
peripherals used.
The series also updates the arm64 defconfig accordingly.
Changes in v3:
- Add power-domains property in the SDMMC2 node.
- Drop patch "arm64: defconfig: cleanup the defconfig"
Changes in v2:
- Add Acked-by of Conor Dooley for patch 0/1 "dt-bindings: arm: stm32:
support Engicam MicroGEA-STM32MP257-RMM board"
- Add resets property to dts CAN node. Suggested by Sashiko.
- Drop the clocks property from the sai1 node in stm32mp257-engicam-microgea-rmm.dts
to avoid overriding the peripheral bus clock reference defined in the base
SoC device tree. Suggested by Sashiko.
- Reference the existing labeled nodes directly at the root level using
&sai1a and &sai1b in stm32mp257-engicam-microgea-rmm.dts instead of
redefining the entire node structure and redeclaring the labels. Suggested by Sashiko.
- Drop the #clock-cells property from sai1a and remove the reference to sai1a from
the clocks array in sai1b, relying strictly on the st,sync property to handle
internal synchronization.
Dario Binacchi (14):
dt-bindings: arm: stm32: support Engicam MicroGEA-STM32MP257-RMM board
arm64: dts: st: add SDMMC2 support on stm32mp25
arm64: dts: st: add CAN1 support on stm32mp25
arm64: dts: st: add i2c1 pins for stm32mp25
arm64: dts: st: add ltdc pins for stm32mp25
arm64: dts: st: add can1 pins for stm32mp25
arm64: dts: st: add pwm2/pwm4 pins for stm32mp25
arm64: dts: st: add sai1 pins for stm32mp25
arm64: dts: st: add sdmmc2 pins for stm32mp25
arm64: dts: st: add spi1 pins for stm32mp25
arm64: dts: st: add usart1 pins for stm32mp25
arm64: dts: st: support Engicam MicroGEA-STM32MP257 SoM
arm64: dts: st: support Engicam MicroGEA-STM32MP257-RMM board
arm64: defconfig: enable configs for Engicam MicroGEA-STM32MP257-RMM
.../devicetree/bindings/arm/stm32/stm32.yaml | 7 +
arch/arm64/boot/dts/st/Makefile | 1 +
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 328 ++++++++++++++++++
arch/arm64/boot/dts/st/stm32mp251.dtsi | 16 +
arch/arm64/boot/dts/st/stm32mp253.dtsi | 16 +
.../st/stm32mp257-engicam-microgea-rmm.dts | 319 +++++++++++++++++
.../dts/st/stm32mp257-engicam-microgea.dtsi | 64 ++++
arch/arm64/configs/defconfig | 4 +
8 files changed, 755 insertions(+)
create mode 100644 arch/arm64/boot/dts/st/stm32mp257-engicam-microgea-rmm.dts
create mode 100644 arch/arm64/boot/dts/st/stm32mp257-engicam-microgea.dtsi
--
2.43.0
base-commit: ba3e43a9e601636f5edb54e259a74f96ca3b8fd8
branch: stm32mp257d-microgea-v3
^ permalink raw reply
* Re: [PATCH v14 29/44] arm64: RMI: Runtime faulting of memory
From: Gavin Shan @ 2026-06-05 6:23 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve,
WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <20260513131757.116630-30-steven.price@arm.com>
Hi Steve,
On 5/13/26 11:17 PM, Steven Price wrote:
> At runtime if the realm guest accesses memory which hasn't yet been
> mapped then KVM needs to either populate the region or fault the guest.
>
> For memory in the lower (protected) region of IPA a fresh page is
> provided to the RMM which will zero the contents. For memory in the
> upper (shared) region of IPA, the memory from the memslot is mapped
> into the realm VM non secure.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v13:
> * Numerous changes due to rebasing.
> * Fix addr_range_desc() to encode the correct block size.
> Changes since v12:
> * Switch to RMM v2.0 range based APIs.
> Changes since v11:
> * Adapt to upstream changes.
> Changes since v10:
> * RME->RMI renaming.
> * Adapt to upstream gmem changes.
> Changes since v9:
> * Fix call to kvm_stage2_unmap_range() in kvm_free_stage2_pgd() to set
> may_block to avoid stall warnings.
> * Minor coding style fixes.
> Changes since v8:
> * Propagate the may_block flag.
> * Minor comments and coding style changes.
> Changes since v7:
> * Remove redundant WARN_ONs for realm_create_rtt_levels() - it will
> internally WARN when necessary.
> Changes since v6:
> * Handle PAGE_SIZE being larger than RMM granule size.
> * Some minor renaming following review comments.
> Changes since v5:
> * Reduce use of struct page in preparation for supporting the RMM
> having a different page size to the host.
> * Handle a race when delegating a page where another CPU has faulted on
> a the same page (and already delegated the physical page) but not yet
> mapped it. In this case simply return to the guest to either use the
> mapping from the other CPU (or refault if the race is lost).
> * The changes to populate_par_region() are moved into the previous
> patch where they belong.
> Changes since v4:
> * Code cleanup following review feedback.
> * Drop the PTE_SHARED bit when creating unprotected page table entries.
> This is now set by the RMM and the host has no control of it and the
> spec requires the bit to be set to zero.
> Changes since v2:
> * Avoid leaking memory if failing to map it in the realm.
> * Correctly mask RTT based on LPA2 flag (see rtt_get_phys()).
> * Adapt to changes in previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 8 ++
> arch/arm64/include/asm/kvm_rmi.h | 12 ++
> arch/arm64/kvm/mmu.c | 128 ++++++++++++++++----
> arch/arm64/kvm/rmi.c | 173 +++++++++++++++++++++++++++
> 4 files changed, 301 insertions(+), 20 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 2e69fe494716..8b6f9d26b5d8 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -712,6 +712,14 @@ static inline bool kvm_realm_is_created(struct kvm *kvm)
> return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
> }
>
> +static inline gpa_t kvm_gpa_from_fault(struct kvm *kvm, phys_addr_t ipa)
> +{
> + if (!kvm_is_realm(kvm))
> + return ipa;
> +
> + return ipa & ~BIT(kvm->arch.realm.ia_bits - 1);
> +}
> +
> static inline bool vcpu_is_rec(const struct kvm_vcpu *vcpu)
> {
> return kvm_is_realm(vcpu->kvm);
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index a2b6bc412a22..b65cfec10dee 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,7 @@
> #ifndef __ASM_KVM_RMI_H
> #define __ASM_KVM_RMI_H
>
> +#include <asm/kvm_pgtable.h>
> #include <asm/rmi_smc.h>
>
> /**
> @@ -97,6 +98,17 @@ void kvm_realm_unmap_range(struct kvm *kvm,
> unsigned long size,
> bool unmap_private,
> bool may_block);
> +int realm_map_protected(struct kvm *kvm,
> + unsigned long base_ipa,
> + kvm_pfn_t pfn,
> + unsigned long size,
> + struct kvm_mmu_memory_cache *memcache);
> +int realm_map_non_secure(struct realm *realm,
> + unsigned long ipa,
> + kvm_pfn_t pfn,
> + unsigned long size,
> + enum kvm_pgtable_prot prot,
> + struct kvm_mmu_memory_cache *memcache);
>
> static inline bool kvm_realm_is_private_address(struct realm *realm,
> unsigned long addr)
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index ac2a0f0106b0..776ffe56d17e 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -334,8 +334,15 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64
>
> lockdep_assert_held_write(&kvm->mmu_lock);
> WARN_ON(size & ~PAGE_MASK);
> - WARN_ON(stage2_apply_range(mmu, start, end, KVM_PGT_FN(kvm_pgtable_stage2_unmap),
> - may_block));
> +
> + if (kvm_is_realm(kvm)) {
> + kvm_realm_unmap_range(kvm, start, size, !only_shared,
> + may_block);
> + } else {
> + WARN_ON(stage2_apply_range(mmu, start, end,
> + KVM_PGT_FN(kvm_pgtable_stage2_unmap),
> + may_block));
> + }
> }
>
> void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
> @@ -358,7 +365,10 @@ static void stage2_flush_memslot(struct kvm *kvm,
> phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
> phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
>
> - kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
> + if (kvm_is_realm(kvm))
> + kvm_realm_unmap_range(kvm, addr, end - addr, false, true);
> + else
> + kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
> }
>
> /**
> @@ -1103,6 +1113,10 @@ void stage2_unmap_vm(struct kvm *kvm)
> struct kvm_memory_slot *memslot;
> int idx, bkt;
>
> + /* For realms this is handled by the RMM so nothing to do here */
> + if (kvm_is_realm(kvm))
> + return;
> +
> idx = srcu_read_lock(&kvm->srcu);
> mmap_read_lock(current->mm);
> write_lock(&kvm->mmu_lock);
> @@ -1528,6 +1542,29 @@ static bool kvm_vma_mte_allowed(struct vm_area_struct *vma)
> return vma->vm_flags & VM_MTE_ALLOWED;
> }
>
> +static int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
> + kvm_pfn_t pfn, unsigned long map_size,
> + enum kvm_pgtable_prot prot,
> + struct kvm_mmu_memory_cache *memcache)
> +{
> + struct realm *realm = &kvm->arch.realm;
> +
> + /*
> + * Write permission is required for now even though it's possible to
> + * map unprotected pages (granules) as read-only. It's impossible to
> + * map protected pages (granules) as read-only.
> + */
> + if (WARN_ON(!(prot & KVM_PGTABLE_PROT_W)))
> + return -EFAULT;
> +
I'm a bit concerned with this. We don't have KVM_PGTABLE_PROT_W set in @prot
if the stage2 fault is raised due to memory read. With -EFAULT returned to VMM
(e.g. QEMU), the vCPU continuous execution is stopped and system won't be
working any more.
> + ipa = ALIGN_DOWN(ipa, PAGE_SIZE);
> + if (!kvm_realm_is_private_address(realm, ipa))
> + return realm_map_non_secure(realm, ipa, pfn, map_size, prot,
> + memcache);
> +
> + return realm_map_protected(kvm, ipa, pfn, map_size, memcache);
> +}
> +
> static bool kvm_vma_is_cacheable(struct vm_area_struct *vma)
> {
> switch (FIELD_GET(PTE_ATTRINDX_MASK, pgprot_val(vma->vm_page_prot))) {
> @@ -1604,27 +1641,52 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> bool write_fault, exec_fault;
> enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
> - struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
> + struct kvm_vcpu *vcpu = s2fd->vcpu;
> + struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
> + gpa_t gpa = kvm_gpa_from_fault(vcpu->kvm, s2fd->fault_ipa);
> unsigned long mmu_seq;
> struct page *page;
> - struct kvm *kvm = s2fd->vcpu->kvm;
> + struct kvm *kvm = vcpu->kvm;
> void *memcache;
> kvm_pfn_t pfn;
> gfn_t gfn;
> int ret;
>
> - memcache = get_mmu_memcache(s2fd->vcpu);
> - ret = topup_mmu_memcache(s2fd->vcpu, memcache);
> + if (kvm_is_realm(vcpu->kvm)) {
> + /* check for memory attribute mismatch */
> + bool is_priv_gfn = kvm_mem_is_private(kvm, gpa >> PAGE_SHIFT);
> + /*
> + * For Realms, the shared address is an alias of the private
> + * PA with the top bit set. Thus if the fault address matches
> + * the GPA then it is the private alias.
> + */
> + bool is_priv_fault = (gpa == s2fd->fault_ipa);
> +
> + if (is_priv_gfn != is_priv_fault) {
> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
> + kvm_is_write_fault(vcpu),
> + false,
> + is_priv_fault);
> + /*
> + * KVM_EXIT_MEMORY_FAULT requires an return code of
> + * -EFAULT, see the API documentation
> + */
> + return -EFAULT;
> + }
> + }
> +
> + memcache = get_mmu_memcache(vcpu);
> + ret = topup_mmu_memcache(vcpu, memcache);
> if (ret)
> return ret;
>
> if (s2fd->nested)
> gfn = kvm_s2_trans_output(s2fd->nested) >> PAGE_SHIFT;
> else
> - gfn = s2fd->fault_ipa >> PAGE_SHIFT;
> + gfn = gpa >> PAGE_SHIFT;
>
> - write_fault = kvm_is_write_fault(s2fd->vcpu);
> - exec_fault = kvm_vcpu_trap_is_exec_fault(s2fd->vcpu);
> + write_fault = kvm_is_write_fault(vcpu);
> + exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
>
> VM_WARN_ON_ONCE(write_fault && exec_fault);
>
> @@ -1634,7 +1696,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>
> ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, &page, NULL);
> if (ret) {
> - kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
> write_fault, exec_fault, false);
> return ret;
> }
> @@ -1654,14 +1716,20 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> kvm_fault_lock(kvm);
> if (mmu_invalidate_retry(kvm, mmu_seq)) {
> ret = -EAGAIN;
> - goto out_unlock;
> + goto out_release_page;
> + }
> +
> + if (kvm_is_realm(kvm)) {
> + ret = realm_map_ipa(kvm, s2fd->fault_ipa, pfn,
> + PAGE_SIZE, KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W, memcache);
> + goto out_release_page;
> }
>
> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
> __pfn_to_phys(pfn), prot,
> memcache, flags);
>
> -out_unlock:
> +out_release_page:
> kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W);
> kvm_fault_unlock(kvm);
>
> @@ -1847,7 +1915,7 @@ static int kvm_s2_fault_get_vma_info(const struct kvm_s2_fault_desc *s2fd,
> * mapping size to ensure we find the right PFN and lay down the
> * mapping in the right place.
> */
> - s2vi->gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi->vma_pagesize) >> PAGE_SHIFT;
> + s2vi->gfn = kvm_gpa_from_fault(kvm, ALIGN_DOWN(s2fd->fault_ipa, s2vi->vma_pagesize)) >> PAGE_SHIFT;
>
> s2vi->mte_allowed = kvm_vma_mte_allowed(vma);
>
> @@ -2056,6 +2124,9 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,
> prot &= ~KVM_NV_GUEST_MAP_SZ;
> ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, gfn_to_gpa(gfn),
> prot, flags);
> + } else if (kvm_is_realm(kvm)) {
> + ret = realm_map_ipa(kvm, s2fd->fault_ipa, pfn, mapping_size,
> + prot, memcache);
> } else {
> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, gfn_to_gpa(gfn), mapping_size,
> __pfn_to_phys(pfn), prot,
For the case kvm_is_realm(), need we adjust 's2fd->fault_ipa' for the sake of
huge pages. In kvm_s2_fault_map(), @gfn and @pfn may have been adjusted by
transparent_hugepage_adjust() to be aligned with huge page size. If the
adjustment happened in transparent_hugepage_adjust(), we need to align
s2fd->fault_ipa down to the huge page size either.
> @@ -2214,6 +2285,13 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
> return 0;
> }
>
> +static bool shared_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)
> +{
> + gpa_t gpa = kvm_gpa_from_fault(kvm, fault_ipa);
> +
> + return (gpa != fault_ipa);
> +}
> +
> /**
> * kvm_handle_guest_abort - handles all 2nd stage aborts
> * @vcpu: the VCPU pointer
> @@ -2324,8 +2402,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> nested = &nested_trans;
> }
>
> - gfn = ipa >> PAGE_SHIFT;
> + gfn = kvm_gpa_from_fault(vcpu->kvm, ipa) >> PAGE_SHIFT;
> memslot = gfn_to_memslot(vcpu->kvm, gfn);
> +
> hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
> write_fault = kvm_is_write_fault(vcpu);
> if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
> @@ -2368,7 +2447,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> * of the page size.
> */
> ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
> - ret = io_mem_abort(vcpu, ipa);
> + ret = io_mem_abort(vcpu, kvm_gpa_from_fault(vcpu->kvm, ipa));
> goto out_unlock;
> }
>
> @@ -2396,7 +2475,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> !write_fault &&
> !kvm_vcpu_trap_is_exec_fault(vcpu));
>
> - if (kvm_slot_has_gmem(memslot))
> + if (kvm_slot_has_gmem(memslot) && !shared_ipa_fault(vcpu->kvm, fault_ipa))
> ret = gmem_abort(&s2fd);
> else
> ret = user_mem_abort(&s2fd);
> @@ -2433,6 +2512,10 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
> return false;
>
> + /* We don't support aging for Realms */
> + if (kvm_is_realm(kvm))
> + return true;
> +
> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> range->start << PAGE_SHIFT,
> size, true);
> @@ -2449,6 +2532,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
> return false;
>
> + /* We don't support aging for Realms */
> + if (kvm_is_realm(kvm))
> + return true;
> +
> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> range->start << PAGE_SHIFT,
> size, false);
> @@ -2628,10 +2715,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
> return -EFAULT;
>
> /*
> - * Only support guest_memfd backed memslots with mappable memory, since
> - * there aren't any CoCo VMs that support only private memory on arm64.
> + * Only support guest_memfd backed memslots with mappable memory,
> + * unless the guest is a CCA realm guest.
> */
> - if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
> + if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new) &&
> + !kvm_is_realm(kvm))
> return -EINVAL;
>
> hva = new->userspace_addr;
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index cae29fd3353c..761b38a4071c 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -597,6 +597,179 @@ static int realm_data_map_init(struct kvm *kvm, unsigned long ipa,
> return ret;
> }
>
> +static unsigned long addr_range_desc(unsigned long phys, unsigned long size)
> +{
> + unsigned long out = 0;
> +
> + switch (size) {
> + case P4D_SIZE:
> + out = 3 | (1 << 2);
> + break;
> + case PUD_SIZE:
> + out = 2 | (1 << 2);
> + break;
> + case PMD_SIZE:
> + out = 1 | (1 << 2);
> + break;
> + case PAGE_SIZE:
> + out = 0 | (1 << 2);
> + break;
> + default:
> + /*
> + * Only support mapping at the page level granulatity when
> + * it's an unusual length. This should get us back onto a larger
> + * block size for the subsequent mappings.
> + */
> + out = 0 | ((MIN(size >> PAGE_SHIFT, PTRS_PER_PTE - 1)) << 2);
> + break;
> + }
> +
> + WARN_ON(phys & ~PAGE_MASK);
> +
> + out |= phys & PAGE_MASK;
> +
> + return out;
> +}
> +
> +int realm_map_protected(struct kvm *kvm,
> + unsigned long ipa,
> + kvm_pfn_t pfn,
> + unsigned long map_size,
> + struct kvm_mmu_memory_cache *memcache)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + phys_addr_t phys = __pfn_to_phys(pfn);
> + phys_addr_t base_phys = phys;
> + phys_addr_t rd = virt_to_phys(realm->rd);
> + unsigned long base_ipa = ipa;
> + unsigned long ipa_top = ipa + map_size;
> + int ret = 0;
> +
> + if (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa, map_size)))
> + return -EINVAL;
> +
> + if (rmi_delegate_range(phys, map_size)) {
> + /*
> + * It's likely we raced with another VCPU on the same
> + * fault. Assume the other VCPU has handled the fault
> + * and return to the guest.
> + */
> + return 0;
> + }
> +
> + while (ipa < ipa_top) {
> + unsigned long flags = RMI_ADDR_TYPE_SINGLE;
> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa);
> + unsigned long out_top;
> +
> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,
> + &out_top);
> +
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + WARN_ON(level == KVM_PGTABLE_LAST_LEVEL);
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + KVM_PGTABLE_LAST_LEVEL,
> + memcache);
> + if (ret)
> + goto err_undelegate;
> +
> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags,
> + range_desc, &out_top);
> + }
> +
> + if (WARN_ON(ret))
> + goto err_undelegate;
> +
> + phys += out_top - ipa;
> + ipa = out_top;
> + }
> +
> + return 0;
> +
> +err_undelegate:
> + realm_unmap_private_range(kvm, base_ipa, ipa, true);
> + if (WARN_ON(rmi_undelegate_range(base_phys, map_size))) {
> + /* Page can't be returned to NS world so is lost */
> + get_page(phys_to_page(base_phys));
> + }
> + return -ENXIO;
> +}
> +
> +int realm_map_non_secure(struct realm *realm,
> + unsigned long ipa,
> + kvm_pfn_t pfn,
> + unsigned long size,
> + enum kvm_pgtable_prot prot,
> + struct kvm_mmu_memory_cache *memcache)
> +{
> + unsigned long attr, flags = 0;
> + phys_addr_t rd = virt_to_phys(realm->rd);
> + phys_addr_t phys = __pfn_to_phys(pfn);
> + unsigned long ipa_top = ipa + size;
> + int ret;
> +
> + if (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa, size)))
> + return -EINVAL;
> +
> + switch (prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC)) {
> + case KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC:
> + return -EINVAL;
> + case KVM_PGTABLE_PROT_DEVICE:
> + attr = MT_S2_FWB_DEVICE_nGnRE;
> + break;
> + case KVM_PGTABLE_PROT_NORMAL_NC:
> + attr = MT_S2_FWB_NORMAL_NC;
> + break;
> + default:
> + attr = MT_S2_FWB_NORMAL;
> + }
> +
> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR, attr);
> +
> + if (prot & KVM_PGTABLE_PROT_R)
> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP, RMI_S2AP_DIRECT_READ);
> + if (prot & KVM_PGTABLE_PROT_W)
> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP, RMI_S2AP_DIRECT_WRITE);
> +
> + flags |= RMI_ADDR_TYPE_SINGLE;
> +
> + while (ipa < ipa_top) {
> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa);
> + unsigned long out_top;
> +
> + ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags, range_desc,
> + &out_top);
> +
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + WARN_ON(level == KVM_PGTABLE_LAST_LEVEL);
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + KVM_PGTABLE_LAST_LEVEL,
> + memcache);
> + if (ret)
> + return ret;
> +
> + ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags,
> + range_desc, &out_top);
> + }
> +
> + if (WARN_ON(ret))
> + return ret;
> +
> + phys += out_top - ipa;
> + ipa = out_top;
> + }
> +
> + return 0;
> +}
> +
> static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> struct page *src_page, void *opaque)
> {
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH v3 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk
From: Dev Jain @ 2026-06-05 6:02 UTC (permalink / raw)
To: Wen Jiang
Cc: linux-mm, linux-arm-kernel, catalin.marinas, will, akpm, urezki,
baohua, Xueyuan.chen21, rppt, david, ryan.roberts,
anshuman.khandual, ajd, linux-kernel, jiangwen6
In-Reply-To: <CAHKocdEfX3dgUr3O6q1WOe4PddN2=LKfX6Y5DfrfTwF9MM5J3w@mail.gmail.com>
On 28/05/26 9:09 am, Wen Jiang wrote:
> On Wed, 27 May 2026 at 13:59, Dev Jain <dev.jain@arm.com> wrote:
>>
>>
>>
>> On 22/05/26 11:01 am, Wen Jiang wrote:
>> From: "Barry Song (Xiaomi)" <baohua@kernel.org>
>>
>> vmap_pages_range_noflush_walk() (formerly vmap_small_pages_range_noflush())
>> provides a clean interface by taking struct page **pages and mapping them
>> via direct PTE iteration. This avoids the page table rewalk seen when
>> using vmap_range_noflush() for page_shift values other than PAGE_SHIFT.
>>
>> Extend it to support larger page_shift values, and add PMD- and
>> contiguous-PTE mappings as well. Rename it to vmap_pages_range_noflush_walk()
>> since it now handles more than just small pages.
>>
>> For vmalloc() allocations with VM_ALLOW_HUGE_VMAP, we no longer need to
>> iterate over pages one by one via vmap_range_noflush(), which would
>> otherwise lead to page table rewalk. The code is now unified with the
>> PAGE_SHIFT case by simply calling vmap_pages_range_noflush_walk().
>>
>> Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
>> Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
>> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
>> ---
>> mm/vmalloc.c | 71 +++++++++++++++++++++++++++++-----------------------
>> 1 file changed, 40 insertions(+), 31 deletions(-)
>>
>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>> index 53fd4ee460ea4..deb764abc0571 100644
>> --- a/mm/vmalloc.c
>> +++ b/mm/vmalloc.c
>> @@ -543,8 +543,10 @@ void vunmap_range(unsigned long addr, unsigned long end)
>>
>> static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>> unsigned long end, pgprot_t prot, struct page **pages, int *nr,
>> - pgtbl_mod_mask *mask)
>> + pgtbl_mod_mask *mask, unsigned int shift)
>> {
>> + unsigned long pfn, size;
>> + unsigned int steps;
>> int err = 0;
>> pte_t *pte;
>>
>> @@ -575,9 +577,10 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>> break;
>> }
>>
>> - set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));
>> - (*nr)++;
>> - } while (pte++, addr += PAGE_SIZE, addr != end);
>> + pfn = page_to_pfn(page);
>> + size = vmap_set_ptes(pte, addr, end, pfn, prot, shift);
>> + steps = PFN_DOWN(size);
>> + } while (pte += steps, *nr += steps, addr += size, addr != end);
>>
>> lazy_mmu_mode_disable();
>> *mask |= PGTBL_PTE_MODIFIED;
>> @@ -587,7 +590,7 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>>
>> static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>> unsigned long end, pgprot_t prot, struct page **pages, int *nr,
>> - pgtbl_mod_mask *mask)
>> + pgtbl_mod_mask *mask, unsigned int shift)
>> {
>> pmd_t *pmd;
>> unsigned long next;
>>> @@ -597,7 +600,27 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>>> return -ENOMEM;
>>> do {
>>> next = pmd_addr_end(addr, end);
>>> - if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
>>> +
>>> + if (shift == PMD_SHIFT) {
>>> + struct page *page = pages[*nr];
>>> + phys_addr_t phys_addr;
>>> +
>>> + if (WARN_ON(!page))
>>> + return -ENOMEM;
>>> + if (WARN_ON(!pfn_valid(page_to_pfn(page))))
>>> + return -EINVAL;
>>
>>
>> So I know these !page and !pfn_valid checks have been copied from vmap_pages_pte_range,
>> but do they mean anything?
>>
>> I think pfn_valid() makes sense in that someone may take a random VA/PA, convert it into a struct
>> page and pass to vmap layer. But I don't see how anyone would pass page == NULL? At the
>> very least, returning ENOMEM does not make sense because the pages are not being
>> allocated by vmap() but have already been allocated.
>
> Hi Dev,
>
> vmap() is EXPORT_SYMBOL with many callers across drivers, each
> constructing the pages array differently. The !page check guards
> against malformed arrays at this API boundary.
>
> The same -ENOMEM issue also exists in vmap_pages_pte_range().
> Should I fix both in this patchset or leave it as a separate cleanup?
>
>>
>>> +
>>> + phys_addr = page_to_phys(page);
>>> +
>>> + if (vmap_try_huge_pmd(pmd, addr, next, phys_addr, prot,
>>> + shift)) {
>>> + *mask |= PGTBL_PMD_MODIFIED;
>>> + *nr += 1 << (shift - PAGE_SHIFT);
>>> + continue;
>>> + }
>>> + }
>>> +
>>> + if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask, shift))
>>> return -ENOMEM;
>>> } while (pmd++, addr = next, addr != end);
>>> return 0;
>>> @@ -605,7 +628,7 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>>>
>>> static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>>> unsigned long end, pgprot_t prot, struct page **pages, int *nr,
>>> - pgtbl_mod_mask *mask)
>>> + pgtbl_mod_mask *mask, unsigned int shift)
>>> {
>>> pud_t *pud;
>>> unsigned long next;
>>> @@ -615,7 +638,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>>> return -ENOMEM;
>>> do {
>>> next = pud_addr_end(addr, end);
>>> - if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
>>> + if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask, shift))
>>> return -ENOMEM;
>>> } while (pud++, addr = next, addr != end);
>>> return 0;
>>> @@ -623,7 +646,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>>>
>>> static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
>>> unsigned long end, pgprot_t prot, struct page **pages, int *nr,
>>> - pgtbl_mod_mask *mask)
>>> + pgtbl_mod_mask *mask, unsigned int shift)
>>> {
>>> p4d_t *p4d;
>>> unsigned long next;
>>> @@ -633,14 +656,14 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
>>> return -ENOMEM;
>>> do {
>>> next = p4d_addr_end(addr, end);
>>> - if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
>>> + if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask, shift))
>>> return -ENOMEM;
>>> } while (p4d++, addr = next, addr != end);
>>> return 0;
>>> }
>>>
>>> -static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
>>> - pgprot_t prot, struct page **pages)
>>> +static int vmap_pages_range_noflush_walk(unsigned long addr, unsigned long end,
>>> + pgprot_t prot, struct page **pages, unsigned int shift)
>>> {
>>> unsigned long start = addr;
>>> pgd_t *pgd;
>>> @@ -655,7 +678,7 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
>>> next = pgd_addr_end(addr, end);
>>> if (pgd_bad(*pgd))
>>> mask |= PGTBL_PGD_MODIFIED;
>>> - err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, &mask);
>>> + err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, &mask, shift);
>>> if (err)
>>> break;
>>> } while (pgd++, addr = next, addr != end);
>>> @@ -678,27 +701,13 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
>>> int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
>>> pgprot_t prot, struct page **pages, unsigned int page_shift)
>>> {
>>> - unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
>>> -
>>> WARN_ON(page_shift < PAGE_SHIFT);
>>>
>>> - if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
>>> - page_shift == PAGE_SHIFT)
>>> - return vmap_small_pages_range_noflush(addr, end, prot, pages);
>>> + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC))
>>> + page_shift = PAGE_SHIFT;
>>>
>>> - for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
>>> - int err;
>>> -
>>> - err = vmap_range_noflush(addr, addr + (1UL << page_shift),
>>> - page_to_phys(pages[i]), prot,
>>> - page_shift);
>>> - if (err)
>>> - return err;
>>> -
>>> - addr += 1UL << page_shift;
>>> - }
>>> -
>>> - return 0;
>>> + return vmap_pages_range_noflush_walk(addr, end, prot, pages,
>>> + min(page_shift, PMD_SHIFT));
>>
>>
>> We can easily extend to PUD huge mappings right? Not sure whether we
>> should keep everything symmetric to how vmap_range_noflush() operates
>> right now, since P4D mappings don't exist, but PUD looks worthwhile.
>>
>
> PUD mapping requires 1GB of contiguous physical memory, but the buddy
> allocator's MAX_PAGE_ORDER is 10 (4MB on 4K pages). So page_shift
> passed to vmap_pages_range_noflush_walk() never exceeds PMD_SHIFT.
Can we then just drop the min()? You can guard the try_huge_pmd with
shift >= PMD_SHIFT - the walker has the necessary ingredients to work
with a shift > PMD_SHIFT, so let us not confuse by this min() truncation.
>
> Thanks,
> Wen
>>> }
>>>
>>> int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
>>
^ permalink raw reply
* Re: [PATCH v2] KVM/arm64: vgic-its: Make ABI commit helpers return void
From: Oliver Upton @ 2026-06-05 6:01 UTC (permalink / raw)
To: Jackie Liu; +Cc: maz, linux-arm-kernel, yuzenghui, will, kvmarm
In-Reply-To: <20260604075147.53299-1-liu.yun@linux.dev>
On Thu, Jun 04, 2026 at 03:51:47PM +0800, Jackie Liu wrote:
> From: Jackie Liu <liuyun01@kylinos.cn>
>
> The return values of vgic_its_set_abi() and vgic_its_commit_v0() are always
> 0 and do not carry useful error information. Simplify by changing them to
> void.
>
> Suggested-by: Oliver Upton <oupton@kernel.org>
> Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
Reviewed-by: Oliver Upton <oupton@kernel.org>
Thanks,
Oliver
> ---
> arch/arm64/kvm/vgic/vgic-its.c | 21 +++++++++------------
> 1 file changed, 9 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
> index 1d7e5d560af4..ca48b34dec20 100644
> --- a/arch/arm64/kvm/vgic/vgic-its.c
> +++ b/arch/arm64/kvm/vgic/vgic-its.c
> @@ -27,7 +27,7 @@ static struct kvm_device_ops kvm_arm_vgic_its_ops;
>
> static int vgic_its_save_tables_v0(struct vgic_its *its);
> static int vgic_its_restore_tables_v0(struct vgic_its *its);
> -static int vgic_its_commit_v0(struct vgic_its *its);
> +static void vgic_its_commit_v0(struct vgic_its *its);
> static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
> struct kvm_vcpu *filter_vcpu, bool needs_inv);
>
> @@ -168,7 +168,7 @@ struct vgic_its_abi {
> int ite_esz;
> int (*save_tables)(struct vgic_its *its);
> int (*restore_tables)(struct vgic_its *its);
> - int (*commit)(struct vgic_its *its);
> + void (*commit)(struct vgic_its *its);
> };
>
> #define ABI_0_ESZ 8
> @@ -192,13 +192,13 @@ inline const struct vgic_its_abi *vgic_its_get_abi(struct vgic_its *its)
> return &its_table_abi_versions[its->abi_rev];
> }
>
> -static int vgic_its_set_abi(struct vgic_its *its, u32 rev)
> +static void vgic_its_set_abi(struct vgic_its *its, u32 rev)
> {
> const struct vgic_its_abi *abi;
>
> its->abi_rev = rev;
> abi = vgic_its_get_abi(its);
> - return abi->commit(its);
> + abi->commit(its);
> }
>
> /*
> @@ -472,7 +472,8 @@ static int vgic_mmio_uaccess_write_its_iidr(struct kvm *kvm,
>
> if (rev >= NR_ITS_ABIS)
> return -EINVAL;
> - return vgic_its_set_abi(its, rev);
> + vgic_its_set_abi(its, rev);
> + return 0;
> }
>
> static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
> @@ -1888,14 +1889,11 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
> its->baser_coll_table = INITIAL_BASER_VALUE |
> ((u64)GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT);
> dev->kvm->arch.vgic.propbaser = INITIAL_PROPBASER_VALUE;
> -
> dev->private = its;
>
> - ret = vgic_its_set_abi(its, NR_ITS_ABIS - 1);
> -
> + vgic_its_set_abi(its, NR_ITS_ABIS - 1);
> mutex_unlock(&dev->kvm->arch.config_lock);
> -
> - return ret;
> + return 0;
> }
>
> static void vgic_its_destroy(struct kvm_device *kvm_dev)
> @@ -2610,7 +2608,7 @@ static int vgic_its_restore_tables_v0(struct vgic_its *its)
> return ret;
> }
>
> -static int vgic_its_commit_v0(struct vgic_its *its)
> +static void vgic_its_commit_v0(struct vgic_its *its)
> {
> const struct vgic_its_abi *abi;
>
> @@ -2623,7 +2621,6 @@ static int vgic_its_commit_v0(struct vgic_its *its)
>
> its->baser_device_table |= (GIC_ENCODE_SZ(abi->dte_esz, 5)
> << GITS_BASER_ENTRY_SIZE_SHIFT);
> - return 0;
> }
>
> static void vgic_its_reset(struct kvm *kvm, struct vgic_its *its)
> --
> 2.54.0
>
>
^ permalink raw reply
* Re: [PATCH] KVM: arm64: vgic: Check the interrupt is still ours before migrating it
From: Oliver Upton @ 2026-06-05 6:00 UTC (permalink / raw)
To: Hyunwoo Kim
Cc: maz, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, Sascha.Bischoff, jic23, timothy.hayes,
eric.auger, christoffer.dall, andre.przywara, linux-arm-kernel,
kvmarm
In-Reply-To: <aiHnI1mu6SGQrgnz@v4bel>
On Fri, Jun 05, 2026 at 05:59:15AM +0900, Hyunwoo Kim wrote:
> vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating
> an interrupt to another vCPU. After reacquiring the locks it only checks
> that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq))
> before moving the interrupt, which assumes that an interrupt whose affinity
> is preserved is still queued on this vCPU's ap_list.
>
> That assumption no longer holds if the interrupt is taken off the ap_list
> while the locks are dropped. vgic_flush_pending_lpis() removes the
> interrupt from the list and sets irq->vcpu to NULL, but leaves
> enabled/pending/target_vcpu untouched. As the interrupt is still enabled
> and pending, vgic_target_oracle() returns the same target_vcpu, so the
> affinity check passes and list_del() is run a second time on an entry that
> has already been removed.
>
> Also check that the interrupt is still assigned to this vCPU
> (irq->vcpu == vcpu) before moving it.
>
> Fixes: 0919e84c0fc1 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework")
> Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
Looking at this and the other VGIC patch you sent (which should've been
a combined series), are you trying to deal with a vCPU writing to
another vCPU's redistributor? I.e. vCPU B setting GICR_CTLR.EnableLPIs=0
behind the back of vCPU A?
That is extremely relevant information as the off-the-cuff reaction is
that no race exists. But since the GIC architecture is awesome and
allows for this sort of insanity, it obviously does....
Anyway, for LPIs resident on a particular RD, there's zero expectation
that the pending state is preserved when EnableLPIs=0. So I'd rather
vgic_flush_pending_lpis() just invalidate the pending state.
Beyond that, I see two other fixes for lifetime issues around the
vgic_irq in the middle of migration. I'd like to see explicit RCU
protection around the release && reacquire of the ap_list_lock rather
than depending on the precondition that IRQs are disabled.
Then vgic_flush_pending_lpis() should leave IRQs intact that are pending
a migration (e.g. irq->vcpu != vgic_target_oracle()) as the only expectation
we need to uphold is that LPIs resident on the RD have the pending state cleared.
Although I think we could benefit from the wetware implementation of the
GIC giving this a once over too. Any thoughts Marc?
Thanks,
Oliver
> ---
> arch/arm64/kvm/vgic/vgic.c | 11 ++++++-----
> 1 file changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
> index 1e9fe8764584..18b280de9a29 100644
> --- a/arch/arm64/kvm/vgic/vgic.c
> +++ b/arch/arm64/kvm/vgic/vgic.c
> @@ -818,15 +818,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
> raw_spin_lock(&irq->irq_lock);
>
> /*
> - * If the affinity has been preserved, move the
> - * interrupt around. Otherwise, it means things have
> - * changed while the interrupt was unlocked, and we
> - * need to replay this.
> + * If the interrupt is still ours and its affinity has
> + * been preserved, move it around. Otherwise, it means
> + * things have changed while the interrupt was unlocked
> + * (it may even have been taken off the list with its
> + * affinity left untouched), and we need to replay this.
> *
> * In all cases, we cannot trust the list not to have
> * changed, so we restart from the beginning.
> */
> - if (target_vcpu == vgic_target_oracle(irq)) {
> + if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) {
> struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;
>
> list_del(&irq->ap_list);
> --
> 2.43.0
>
^ permalink raw reply
* [PATCH v4 2/2] ARM: dts: aspeed: add 'resets' to video node
From: Haiyue Wang @ 2026-06-05 5:52 UTC (permalink / raw)
To: linux-clk, devicetree, linux-aspeed
Cc: Haiyue Wang, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Hans Verkuil, Jammy Huang,
moderated list:ARM/ASPEED MACHINE SUPPORT, open list
In-Reply-To: <20260605055712.118501-1-haiyuewa@163.com>
The aspeed video (be compatible for ast2400, ast2500, ast2600) now needs
the reset DTS handle specified, otherwise it will fail to load:
[ 4.809494] aspeed-video 1e700000.video: irq 57
[ 4.809977] aspeed-video 1e700000.video: Unable to get reset
[ 4.810341] aspeed-video 1e700000.video: probe with driver aspeed-video failed with error -2
Fixes: e83f8dd668ea ("media: aspeed: Fix dram hang at res-change")
Signed-off-by: Haiyue Wang <haiyuewa@163.com>
---
arch/arm/boot/dts/aspeed/aspeed-g4.dtsi | 1 +
arch/arm/boot/dts/aspeed/aspeed-g5.dtsi | 1 +
arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 1 +
3 files changed, 3 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g4.dtsi
index c3d4d916c69b..1547e28d77e2 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed/aspeed-g4.dtsi
@@ -242,6 +242,7 @@ video: video@1e700000 {
<&syscon ASPEED_CLK_GATE_ECLK>;
clock-names = "vclk", "eclk";
interrupts = <7>;
+ resets = <&syscon ASPEED_RESET_VIDEO>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/aspeed/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g5.dtsi
index 39500bdb4747..793570ca2518 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed/aspeed-g5.dtsi
@@ -296,6 +296,7 @@ video: video@1e700000 {
<&syscon ASPEED_CLK_GATE_ECLK>;
clock-names = "vclk", "eclk";
interrupts = <7>;
+ resets = <&syscon ASPEED_RESET_VIDEO>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
index 189bc3bbb47c..3adf48987a17 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
@@ -428,6 +428,7 @@ video: video@1e700000 {
<&syscon ASPEED_CLK_GATE_ECLK>;
clock-names = "vclk", "eclk";
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&syscon ASPEED_RESET_VIDEO>;
status = "disabled";
};
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] KVM: arm64: vgic: Use list_del_rcu() when flushing pending LPIs
From: Oliver Upton @ 2026-06-05 5:47 UTC (permalink / raw)
To: Hyunwoo Kim
Cc: maz, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, Sascha.Bischoff, jic23, linux-arm-kernel,
kvmarm
In-Reply-To: <aiHrGM1f8czcUby4@v4bel>
Hi Hyunwoo,
On Fri, Jun 05, 2026 at 06:16:08AM +0900, Hyunwoo Kim wrote:
> vgic_v3_fold_lr_state() walks the ap_list from last_lr_irq without holding
> the ap_list_lock, relying on vgic_irq being freed via kfree_rcu() and on
> interrupts being disabled. vgic_flush_pending_lpis() removes entries with
> list_del(), which clobbers a node's next pointer, so when another vCPU
> disables LPIs via GICR_CTLR the walk can follow the clobbered next pointer
> from a removed node, or from the node that last_lr_irq points to.
>
> Remove entries with list_del_rcu() so that the next pointer stays valid
> until the walk completes.
>
> Fixes: 3cfd59f81e0f ("KVM: arm64: GICv3: Handle LR overflow when EOImode==0")
> Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
Changing only one of the writer paths to use the rculist helpers does
not make the ap_list an rculist. Insertions are not RCU-safe, nor are
deleations from vgic_prune_ap_list().
And TBH, the real bug here is the fact that vgic_v3_fold_lr_state() isn't
taking the ap_list_lock.
Thanks,
Oliver
> ---
> arch/arm64/kvm/vgic/vgic.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
> index 1e9fe8764584d..73efc0f95bfb1 100644
> --- a/arch/arm64/kvm/vgic/vgic.c
> +++ b/arch/arm64/kvm/vgic/vgic.c
> @@ -204,7 +204,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
> list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) {
> if (irq_is_lpi(vcpu->kvm, irq->intid)) {
> raw_spin_lock(&irq->irq_lock);
> - list_del(&irq->ap_list);
> + list_del_rcu(&irq->ap_list);
> irq->vcpu = NULL;
> raw_spin_unlock(&irq->irq_lock);
> deleted |= vgic_put_irq_norelease(vcpu->kvm, irq);
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH] KVM: arm64: Reallocate the nested_mmus array under the mmu_lock
From: Hyunwoo Kim @ 2026-06-05 5:35 UTC (permalink / raw)
To: Oliver Upton
Cc: maz, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, christoffer.dall, linux-arm-kernel, kvmarm,
imv4bel
In-Reply-To: <aiH7xKnprU9bUap5@kernel.org>
On Thu, Jun 04, 2026 at 03:27:16PM -0700, Oliver Upton wrote:
> Hi,
>
> The shortlog is very confusing, since "allocate behind $LOCK" is usually
> something alarming. Maybe instead:
>
> KVM: arm64: Reassign nested_mmus array behind mmu_lock
heh, that's confusing indeed. I'll change it that way.
>
> On Fri, Jun 05, 2026 at 03:30:00AM +0900, Hyunwoo Kim wrote:
> > Code that walks kvm->arch.nested_mmus[] holds kvm->mmu_lock. By contrast,
> > kvm_vcpu_init_nested() reallocates the array and frees the old buffer while
> > holding only kvm->arch.config_lock, so a walker can reference the freed
> > array.
>
> It wouldn't hurt to share slightly more information here. Are you
> dealing with a concurrent MMU notifier?
Yes. The MMU notifier path also walks nested_mmus[] under mmu_lock.
kvm_vcpu_init_nested() holds only config_lock, so if a notifier fires
during vCPU init, it races with the array realloc and free.
Here's the reworked changelog. Should I send v2?
kvm->arch.nested_mmus[] is walked under kvm->mmu_lock, including from the
MMU notifier path (kvm_unmap_gfn_range() -> kvm_nested_s2_unmap()), which
can run at any time. kvm_vcpu_init_nested() reallocates the array and frees
the old buffer while holding only kvm->arch.config_lock, so such a walker
can reference the freed array.
Allocate the new array outside of mmu_lock, as the allocation can sleep.
Under the lock, copy the existing entries, fix up the back pointers and
reassign the array. Free the old buffer after dropping the lock, as
kvfree() can sleep as well.
>
> > Allocate the new array outside the lock, as the allocation can sleep, and
> > do only the copy and the pointer swap under the mmu_lock. After the swap no
> > walker can reach the old buffer, so free it once the lock has been
> > released.
> >
> > Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures")
> > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
>
> The diff itself LGTM
>
> Reviewed-by: Oliver Upton <oupton@kernel.org>
Thanks for the review.
>
> Thanks,
> Oliver
>
> > ---
> > arch/arm64/kvm/nested.c | 33 ++++++++++++++++++++-------------
> > 1 file changed, 20 insertions(+), 13 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> > index 38f672e940878..6f7bc9a9992e0 100644
> > --- a/arch/arm64/kvm/nested.c
> > +++ b/arch/arm64/kvm/nested.c
> > @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
> > * again, and there is no reason to affect the whole VM for this.
> > */
> > num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
> > - tmp = kvrealloc(kvm->arch.nested_mmus,
> > - size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
> > - GFP_KERNEL_ACCOUNT | __GFP_ZERO);
> > - if (!tmp)
> > - return -ENOMEM;
> >
> > - swap(kvm->arch.nested_mmus, tmp);
> > + if (num_mmus > kvm->arch.nested_mmus_size) {
> > + tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
> > + if (!tmp)
> > + return -ENOMEM;
> >
> > - /*
> > - * If we went through a realocation, adjust the MMU back-pointers in
> > - * the previously initialised kvm_pgtable structures.
> > - */
> > - if (kvm->arch.nested_mmus != tmp)
> > - for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
> > - kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
> > + write_lock(&kvm->mmu_lock);
> > +
> > + if (kvm->arch.nested_mmus_size) {
> > + memcpy(tmp, kvm->arch.nested_mmus,
> > + size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
> > +
> > + for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
> > + tmp[i].pgt->mmu = &tmp[i];
> > + }
> > +
> > + swap(kvm->arch.nested_mmus, tmp);
> > +
> > + write_unlock(&kvm->mmu_lock);
> > +
> > + kvfree(tmp);
> > + }
> >
> > for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
> > ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
> > --
> > 2.43.0
> >
Best regards,
Hyunwoo Kim
^ permalink raw reply
* Re: [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: Rob Herring (Arm) @ 2026-06-05 4:40 UTC (permalink / raw)
To: joakim.zhang
Cc: krzk+dt, cix-kernel-upstream, sboyd, conor+dt, mturquette,
bmasney, devicetree, p.zabel, gary.yang, linux-kernel,
linux-arm-kernel, linux-clk
In-Reply-To: <20260605032225.523669-2-joakim.zhang@cixtech.com>
On Fri, 05 Jun 2026 11:22:21 +0800, joakim.zhang@cixtech.com wrote:
> From: Joakim Zhang <joakim.zhang@cixtech.com>
>
> The Cix Sky1 Audio Subsystem (AUDSS) groups audio-related clock, reset
> and control registers in a dedicated CRU block. Software reset lines are
> exposed on the syscon parent via #reset-cells, following the same model
> as the existing Sky1 FCH and S5 system control bindings.
>
> Add the cix,sky1-audss-system-control compatible to
> cix,sky1-system-control.yaml for the MFD/syscon parent node, and define
> AUDSS software reset indices in
> include/dt-bindings/reset/cix,sky1-audss-system-control.h for I2S, HDA,
> DMAC, mailbox, watchdog and timer blocks.
>
> Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
> ---
> .../soc/cix/cix,sky1-system-control.yaml | 39 ++++++++++++++++---
> .../reset/cix,sky1-audss-system-control.h | 27 +++++++++++++
> 2 files changed, 61 insertions(+), 5 deletions(-)
> create mode 100644 include/dt-bindings/reset/cix,sky1-audss-system-control.h
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml: Unresolvable reference: /schemas/clock/cix,sky1-audss-clock.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.example.dtb: system-controller@7110000 (cix,sky1-audss-system-control): clock-controller: False schema does not allow {'compatible': ['cix,sky1-audss-clock'], 'power-domains': [[4294967295, 0]], '#clock-cells': 1, 'clocks': [[4294967295, 0], [4294967295, 1], [4294967295, 2], [4294967295, 3], [4294967295, 4], [4294967295, 5]], 'clock-names': ['audio_clk0', 'audio_clk1', 'audio_clk2', 'audio_clk3', 'audio_clk4', 'audio_clk5'], 'resets': [[4294967295, 0]]}
from schema $id: http://devicetree.org/schemas/soc/cix/cix,sky1-system-control.yaml
Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.example.dtb: /example-1/system-controller@7110000/clock-controller: failed to match any schema with compatible: ['cix,sky1-audss-clock']
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260605032225.523669-2-joakim.zhang@cixtech.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH] phy: freescale: imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path
From: Liu Ying @ 2026-06-05 3:44 UTC (permalink / raw)
To: Felix Gu
Cc: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-phy, imx,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260604-lvds-v1-1-b8e1ff1bdee7@gmail.com>
On Thu, Jun 04, 2026 at 10:39:07PM +0800, Felix Gu wrote:
> If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(),
> the function returns directly without calling pm_runtime_disable(),
> leaving runtime PM permanently enabled for the device.
>
> Fix this by using devm_pm_runtime_enable() so that cleanup is
> automatic on any probe failure or driver unbind. This also allows
> removing the manual err label and the .remove callback.
>
> Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
> drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 +++++++-----------------
> 1 file changed, 7 insertions(+), 17 deletions(-)
It would be good if the patch subject prefix can be changed to
"phy: freescale: phy-fsl-imx8qm-lvds-phy:".
With that done:
Acked-by: Liu Ying <victor.liu@nxp.com>
--
Regards,
Liu Ying
^ permalink raw reply
* [PATCH v2 0/5] Add Cix Sky1 AUDSS clock and reset support
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
From: Joakim Zhang <joakim.zhang@cixtech.com>
This patch set adds the clock and reset support for AUDSS. The AUDSS groups
audio-related peripherals (HDA, I2S, DSP, DMA, mailboxes, watchdog, timer, etc.)
behind a single Clock and Reset Unit (CRU) register block.
I know the best approach would be to separate reset and clock into different
patches for review. However, here the relationship between them as parent
and child nodes is coupled, which makes it easier to understand and explain.
Therefore, they are sent for review in a single patch set, and the code is
based on the latest clk-next branch.
ChangeLogs:
v1->v2:
* remove audss_rst device node since it doesn't has resource, and
move to reset-sky1.c driver.
* remove hda related which would be sent after this patch set accepted
* soc componnet is okay by default from dtsi
* fix for audss clk driver:
* remove "comment "Clock options for Cixtech audss:""
* add select MFD_SYSCON
* move lock and clk_data into struct sky1_audss_clks_priv
* const char *name -> const char * const * name
* remove CLK_GET_RATE_NOCACHE
* divicer -> divider
* Reverse Christmas tree order
* return reg ? 1 : 0; -> return !!reg;
* return ERR_CAST(hw); -> return hw;
* of_device_get_match_data(dev) -> device_get_match_data()
* add lock from runtime_suspend/resume
* loop to more mailing lists
Joakim Zhang (5):
dt-bindings: soc: cix,sky1-system-control: add audss system control
reset: cix: add audss support to sky1 reset driver
dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
clk: cix: add sky1 audss clock controller
arm64: dts: cix: sky1: add audss system control
.../bindings/clock/cix,sky1-audss-clock.yaml | 92 ++
.../soc/cix/cix,sky1-system-control.yaml | 39 +-
arch/arm64/boot/dts/cix/sky1.dtsi | 28 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/cix/Kconfig | 16 +
drivers/clk/cix/Makefile | 3 +
drivers/clk/cix/clk-sky1-audss.c | 1129 +++++++++++++++++
drivers/reset/reset-sky1.c | 36 +-
include/dt-bindings/clock/cix,sky1-audss.h | 62 +
.../reset/cix,sky1-audss-system-control.h | 27 +
11 files changed, 1427 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml
create mode 100644 drivers/clk/cix/Kconfig
create mode 100644 drivers/clk/cix/Makefile
create mode 100644 drivers/clk/cix/clk-sky1-audss.c
create mode 100644 include/dt-bindings/clock/cix,sky1-audss.h
create mode 100644 include/dt-bindings/reset/cix,sky1-audss-system-control.h
--
2.50.1
^ permalink raw reply
* [PATCH v2 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260605032225.523669-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
The AUDSS CRU contains an internal clock tree of muxes, dividers and
gates for DSP, I2S, HDA, DMAC and related blocks. The clock provider is
a child node of the cix,sky1-audss-system-control syscon and accesses
registers through the parent MMIO region.
Add the devicetree binding for cix,sky1-audss-clock and clock indices in
include/dt-bindings/clock/cix,sky1-audss.h. Document the parent syscon
indices.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
.../bindings/clock/cix,sky1-audss-clock.yaml | 92 +++++++++++++++++++
include/dt-bindings/clock/cix,sky1-audss.h | 62 +++++++++++++
2 files changed, 154 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml
create mode 100644 include/dt-bindings/clock/cix,sky1-audss.h
diff --git a/Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml b/Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml
new file mode 100644
index 000000000000..22b4cc72f395
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/cix,sky1-audss-clock.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cix Sky1 audio subsystem clock controller
+
+maintainers:
+ - Joakim Zhang <joakim.zhang@cixtech.com>
+
+description: |
+ Clock provider for the Cix Sky1 audio subsystem (AUDSS).
+
+ This node is a child of a cix,sky1-audss-system-control MFD/syscon node
+ (see cix,sky1-system-control.yaml). It does not have a reg property; clock
+ mux, divider and gate fields are accessed through the parent register block.
+
+ Software reset lines for AUDSS blocks are exposed on the parent syscon via
+ #reset-cells. Reset indices are defined in
+ include/dt-bindings/reset/cix,sky1-audss-system-control.h.
+
+ Six SoC-level reference clocks listed in clocks/clock-names feed the AUDSS
+ clock tree. The provider exposes the internal AUDSS clocks to other devices
+ via #clock-cells; indices are defined in cix,sky1-audss.h.
+
+properties:
+ compatible:
+ const: cix,sky1-audss-clock
+
+ '#clock-cells':
+ const: 1
+ description:
+ Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss.h.
+
+ clocks:
+ minItems: 6
+ maxItems: 6
+ description:
+ Six SoC-level audio reference clocks that feed the audio subsystem,
+ in the same order as clock-names.
+
+ clock-names:
+ items:
+ - const: audio_clk0
+ - const: audio_clk1
+ - const: audio_clk2
+ - const: audio_clk3
+ - const: audio_clk4
+ - const: audio_clk5
+
+ resets:
+ maxItems: 1
+ description: Audio subsystem NoC (or bus) reset line.
+
+ power-domains:
+ maxItems: 1
+ description: Audio subsystem power domain.
+
+required:
+ - compatible
+ - '#clock-cells'
+ - clocks
+ - clock-names
+ - resets
+ - power-domains
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/cix,sky1.h>
+ #include <dt-bindings/reset/cix,sky1-audss-system-control.h>
+ #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+ audss_syscon: system-controller@7110000 {
+ compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
+ reg = <0x7110000 0x10000>;
+ #reset-cells = <1>;
+
+ audss_clk: clock-controller {
+ compatible = "cix,sky1-audss-clock";
+ power-domains = <&smc_devpd 0>;
+ #clock-cells = <1>;
+ clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>, <&scmi_clk CLK_TREE_AUDIO_CLK1>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK2>, <&scmi_clk CLK_TREE_AUDIO_CLK3>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK4>, <&scmi_clk CLK_TREE_AUDIO_CLK5>;
+ clock-names = "audio_clk0", "audio_clk1", "audio_clk2",
+ "audio_clk3", "audio_clk4", "audio_clk5";
+ resets = <&src SKY1_AUDIO_HIFI5_NOC_RESET_N>;
+ };
+ };
diff --git a/include/dt-bindings/clock/cix,sky1-audss.h b/include/dt-bindings/clock/cix,sky1-audss.h
new file mode 100644
index 000000000000..041f9daa1ee4
--- /dev/null
+++ b/include/dt-bindings/clock/cix,sky1-audss.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright 2026 Cix Technology Group Co., Ltd.
+ */
+
+#ifndef _DT_BINDINGS_CLK_CIX_SKY1_AUDSS_H
+#define _DT_BINDINGS_CLK_CIX_SKY1_AUDSS_H
+
+#define CLK_AUD_CLK4_DIV2 0
+#define CLK_AUD_CLK4_DIV4 1
+#define CLK_AUD_CLK5_DIV2 2
+
+#define CLK_DSP_CLK 3
+#define CLK_DSP_BCLK 4
+#define CLK_DSP_PBCLK 5
+
+#define CLK_SRAM_AXI 6
+
+#define CLK_HDA_SYS 7
+#define CLK_HDA_HDA 8
+
+#define CLK_DMAC_AXI 9
+
+#define CLK_WDG_APB 10
+#define CLK_WDG_WDG 11
+
+#define CLK_TIMER_APB 12
+#define CLK_TIMER_TIMER 13
+
+#define CLK_MB_0_APB 14 /* MB0: ap->dsp */
+#define CLK_MB_1_APB 15 /* MB1: dsp->ap */
+
+#define CLK_I2S0_APB 16
+#define CLK_I2S1_APB 17
+#define CLK_I2S2_APB 18
+#define CLK_I2S3_APB 19
+#define CLK_I2S4_APB 20
+#define CLK_I2S5_APB 21
+#define CLK_I2S6_APB 22
+#define CLK_I2S7_APB 23
+#define CLK_I2S8_APB 24
+#define CLK_I2S9_APB 25
+#define CLK_I2S0 26
+#define CLK_I2S1 27
+#define CLK_I2S2 28
+#define CLK_I2S3 29
+#define CLK_I2S4 30
+#define CLK_I2S5 31
+#define CLK_I2S6 32
+#define CLK_I2S7 33
+#define CLK_I2S8 34
+#define CLK_I2S9 35
+
+#define CLK_MCLK0 36
+#define CLK_MCLK1 37
+#define CLK_MCLK2 38
+#define CLK_MCLK3 39
+#define CLK_MCLK4 40
+
+#define AUDSS_MAX_CLKS 41
+
+#endif
--
2.50.1
^ permalink raw reply related
* [PATCH v2 4/5] clk: cix: add sky1 audss clock controller
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260605032225.523669-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Add a platform driver for the Cix Sky1 Audio Subsystem (AUDSS) internal
clock controller. The driver binds to a cix,sky1-audss-clock device tree
node under the AUDSS syscon, obtains the parent regmap via
syscon_node_to_regmap(), and registers mux/divider/gate composite clocks
for DSP, SRAM, HDA, DMAC, watchdog, timer, mailbox and I2S outputs. Six
SoC-level audio reference clocks are brought up as inputs to the tree.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/cix/Kconfig | 16 +
drivers/clk/cix/Makefile | 3 +
drivers/clk/cix/clk-sky1-audss.c | 1129 ++++++++++++++++++++++++++++++
5 files changed, 1150 insertions(+)
create mode 100644 drivers/clk/cix/Kconfig
create mode 100644 drivers/clk/cix/Makefile
create mode 100644 drivers/clk/cix/clk-sky1-audss.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index b2efbe9f6acb..ead41e45c0f6 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -503,6 +503,7 @@ source "drivers/clk/actions/Kconfig"
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/aspeed/Kconfig"
source "drivers/clk/bcm/Kconfig"
+source "drivers/clk/cix/Kconfig"
source "drivers/clk/eswin/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a3e2862ebd7e..9135ea6e5a8f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
+obj-y += cix/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
diff --git a/drivers/clk/cix/Kconfig b/drivers/clk/cix/Kconfig
new file mode 100644
index 000000000000..3909796f8656
--- /dev/null
+++ b/drivers/clk/cix/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# Audio subsystem clock support for Cixtech SoC family
+menu "Clock support for Cixtech audss"
+
+config CLK_SKY1_AUDSS
+ tristate "Cixtech Sky1 Audio Subsystem Clock Driver"
+ depends on ARCH_CIX || COMPILE_TEST
+ select MFD_SYSCON
+ select REGMAP_MMIO
+ select RESET_CONTROLLER
+ help
+ Support for the Audio Subsystem clock controller present on
+ Cixtech Sky1 SoC. This driver provides mux, divider and gate
+ clocks for DSP, I2S, HDA and related blocks in the audio
+ subsystem. Say M or Y here if you want to build this driver.
+endmenu
diff --git a/drivers/clk/cix/Makefile b/drivers/clk/cix/Makefile
new file mode 100644
index 000000000000..bc612f1d08b2
--- /dev/null
+++ b/drivers/clk/cix/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o
diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c
new file mode 100644
index 000000000000..899452d5ed14
--- /dev/null
+++ b/drivers/clk/cix/clk-sky1-audss.c
@@ -0,0 +1,1129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright 2026 Cix Technology Group Co., Ltd.
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/clock/cix,sky1-audss.h>
+
+#define INFO_HIFI0 0x00
+#define INFO_CLK_GATE 0x10
+#define INFO_CLK_DIV 0x14
+#define INFO_CLK_MUX 0x18
+#define INFO_MCLK 0x70
+
+#define SKY1_AUDSS_CLK_PARENTS_CNT 6
+#define AUDSS_CLK_PARENTS_MAX SKY1_AUDSS_CLK_PARENTS_CNT
+
+static u32 sky1_reg_save[][2] = {
+ { INFO_HIFI0, 0 },
+ { INFO_CLK_GATE, 0 },
+ { INFO_CLK_DIV, 0 },
+ { INFO_CLK_MUX, 0 },
+ { INFO_MCLK, 0 },
+};
+
+static const char * const sky1_audss_clk_names[SKY1_AUDSS_CLK_PARENTS_CNT] = {
+ "audio_clk0", "audio_clk1", "audio_clk2",
+ "audio_clk3", "audio_clk4", "audio_clk5",
+};
+
+static const u32 sky1_clk_rate_default[SKY1_AUDSS_CLK_PARENTS_CNT] = {
+ 294912000,
+ 344064000,
+ 270950400,
+ 316108800,
+ 800000000,
+ 48000000,
+};
+
+static const char * const dsp_clk_parent[] = {
+ "audio_clk4"
+};
+
+static const char * const dsp_bclk_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const dsp_pbclk_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const sram_axi_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const hda_sys_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const hda_hda_parent[] = {
+ "audio_clk5"
+};
+
+static const char * const dmac_axi_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const wdg_apb_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const wdg_wdg_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const timer_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const timer_timer_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const mailbox_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const i2s_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const i2s0_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s1_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s2_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s3_parents[] = {
+ "audio_clk0", "audio_clk1", "audio_clk2", "audio_clk3"
+};
+
+static const char * const i2s4_parents[] = {
+ "audio_clk0", "audio_clk1", "audio_clk2", "audio_clk3"
+};
+
+static const char * const i2s5_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s6_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s7_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s8_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s9_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const mclk_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+/*
+ * audss composite clock definition
+ */
+struct muxdiv_cfg {
+ int offset;
+ u8 shift;
+ u8 width;
+ u8 flags;
+};
+
+struct gate_cfg {
+ int offset;
+ u8 shift;
+ u8 flags;
+};
+
+struct composite_clk_cfg {
+ u32 id;
+ const char * const name;
+ const char * const *parent_names;
+ int num_parents;
+ struct muxdiv_cfg *mux_cfg;
+ struct muxdiv_cfg *div_cfg;
+ struct gate_cfg *gate_cfg;
+ unsigned long flags;
+};
+
+#define CFG(_id,\
+ _name,\
+ _parent_names,\
+ _mux_offset, _mux_shift, _mux_width, _mux_flags,\
+ _div_offset, _div_shift, _div_width, _div_flags,\
+ _gate_offset, _gate_shift, _gate_flags,\
+ _flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parent_names,\
+ .num_parents = ARRAY_SIZE(_parent_names),\
+ .mux_cfg = &(struct muxdiv_cfg) { _mux_offset, _mux_shift, _mux_width, _mux_flags },\
+ .div_cfg = &(struct muxdiv_cfg) { _div_offset, _div_shift, _div_width, _div_flags },\
+ .gate_cfg = &(struct gate_cfg) { _gate_offset, _gate_shift, _gate_flags },\
+ .flags = _flags,\
+}
+
+static const struct composite_clk_cfg sky1_audss_clks[] = {
+ /* dsp */
+ CFG(CLK_DSP_CLK,
+ "audss_dsp_clk",
+ dsp_clk_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_HIFI0, 0, 0,
+ 0),
+ CFG(CLK_DSP_BCLK,
+ "audss_dsp_bclk",
+ dsp_bclk_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ -1, 0, 0,
+ 0),
+ CFG(CLK_DSP_PBCLK,
+ "audss_dsp_pbclk",
+ dsp_pbclk_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ -1, 0, 0,
+ 0),
+ /* sram */
+ CFG(CLK_SRAM_AXI,
+ "audss_sram_axi",
+ sram_axi_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 16, 0,
+ 0),
+ /* hda */
+ CFG(CLK_HDA_SYS,
+ "audss_hda_sys",
+ hda_sys_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 14, 0,
+ 0),
+ CFG(CLK_HDA_HDA,
+ "audss_hda_hda",
+ hda_hda_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 14, 0,
+ 0),
+ /* dmac */
+ CFG(CLK_DMAC_AXI,
+ "audss_dmac_axi",
+ dmac_axi_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 15, 0,
+ 0),
+ /* wdg */
+ CFG(CLK_WDG_APB,
+ "audss_wdg_apb",
+ wdg_apb_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 10, 0,
+ 0),
+ CFG(CLK_WDG_WDG,
+ "audss_wdg_wdg",
+ wdg_wdg_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 10, 0,
+ 0),
+ /* timer */
+ CFG(CLK_TIMER_APB,
+ "audss_timer_apb",
+ timer_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 11, 0,
+ 0),
+ CFG(CLK_TIMER_TIMER,
+ "audss_timer_timer",
+ timer_timer_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 11, 0,
+ 0),
+ /* mailbox: mb0(ap->dsp), mb1(dsp->ap) */
+ CFG(CLK_MB_0_APB,
+ "audss_mb_0_apb",
+ mailbox_apb_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 12, 0,
+ 0),
+ CFG(CLK_MB_1_APB,
+ "audss_mb_1_apb",
+ mailbox_apb_parent,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 13, 0,
+ 0),
+ /* i2s */
+ CFG(CLK_I2S0_APB,
+ "audss_i2s0_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 0, 0,
+ 0),
+ CFG(CLK_I2S1_APB,
+ "audss_i2s1_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 1, 0,
+ 0),
+ CFG(CLK_I2S2_APB,
+ "audss_i2s2_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 2, 0,
+ 0),
+ CFG(CLK_I2S3_APB,
+ "audss_i2s3_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 3, 0,
+ 0),
+ CFG(CLK_I2S4_APB,
+ "audss_i2s4_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 4, 0,
+ 0),
+ CFG(CLK_I2S5_APB,
+ "audss_i2s5_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 5, 0,
+ 0),
+ CFG(CLK_I2S6_APB,
+ "audss_i2s6_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 6, 0,
+ 0),
+ CFG(CLK_I2S7_APB,
+ "audss_i2s7_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 7, 0,
+ 0),
+ CFG(CLK_I2S8_APB,
+ "audss_i2s8_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 8, 0,
+ 0),
+ CFG(CLK_I2S9_APB,
+ "audss_i2s9_apb",
+ i2s_apb_parent,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 9, 0,
+ 0),
+ CFG(CLK_I2S0,
+ "audss_i2s0",
+ i2s0_parents,
+ INFO_CLK_MUX, 0, 2, 0,
+ INFO_CLK_DIV, 2, 2, 0,
+ INFO_CLK_GATE, 0, 0,
+ 0),
+ CFG(CLK_I2S1,
+ "audss_i2s1",
+ i2s1_parents,
+ INFO_CLK_MUX, 2, 2, 0,
+ INFO_CLK_DIV, 4, 2, 0,
+ INFO_CLK_GATE, 1, 0,
+ 0),
+ CFG(CLK_I2S2,
+ "audss_i2s2",
+ i2s2_parents,
+ INFO_CLK_MUX, 4, 2, 0,
+ INFO_CLK_DIV, 6, 2, 0,
+ INFO_CLK_GATE, 2, 0,
+ 0),
+ CFG(CLK_I2S3,
+ "audss_i2s3",
+ i2s3_parents,
+ INFO_CLK_MUX, 6, 2, 0,
+ INFO_CLK_DIV, 8, 2, 0,
+ INFO_CLK_GATE, 3, 0,
+ 0),
+ CFG(CLK_I2S4,
+ "audss_i2s4",
+ i2s4_parents,
+ INFO_CLK_MUX, 8, 2, 0,
+ INFO_CLK_DIV, 10, 2, 0,
+ INFO_CLK_GATE, 4, 0,
+ 0),
+ CFG(CLK_I2S5,
+ "audss_i2s5",
+ i2s5_parents,
+ INFO_CLK_MUX, 10, 2, 0,
+ INFO_CLK_DIV, 12, 2, 0,
+ INFO_CLK_GATE, 5, 0,
+ 0),
+ CFG(CLK_I2S6,
+ "audss_i2s6",
+ i2s6_parents,
+ INFO_CLK_MUX, 12, 2, 0,
+ INFO_CLK_DIV, 14, 2, 0,
+ INFO_CLK_GATE, 6, 0,
+ 0),
+ CFG(CLK_I2S7,
+ "audss_i2s7",
+ i2s7_parents,
+ INFO_CLK_MUX, 14, 2, 0,
+ INFO_CLK_DIV, 16, 2, 0,
+ INFO_CLK_GATE, 7, 0,
+ 0),
+ CFG(CLK_I2S8,
+ "audss_i2s8",
+ i2s8_parents,
+ INFO_CLK_MUX, 16, 2, 0,
+ INFO_CLK_DIV, 18, 2, 0,
+ INFO_CLK_GATE, 8, 0,
+ 0),
+ CFG(CLK_I2S9,
+ "audss_i2s9",
+ i2s9_parents,
+ INFO_CLK_MUX, 18, 2, 0,
+ INFO_CLK_DIV, 20, 2, 0,
+ INFO_CLK_GATE, 9, 0,
+ 0),
+ /* mclk */
+ CFG(CLK_MCLK0,
+ "audss_mclk0",
+ mclk_parents,
+ INFO_MCLK, 5, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 0, 0,
+ 0),
+ CFG(CLK_MCLK1,
+ "audss_mclk1",
+ mclk_parents,
+ INFO_MCLK, 6, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 1, 0,
+ 0),
+ CFG(CLK_MCLK2,
+ "audss_mclk2",
+ mclk_parents,
+ INFO_MCLK, 7, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 2, 0,
+ 0),
+ CFG(CLK_MCLK3,
+ "audss_mclk3",
+ mclk_parents,
+ INFO_MCLK, 8, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 3, 0,
+ 0),
+ CFG(CLK_MCLK4,
+ "audss_mclk4",
+ mclk_parents,
+ INFO_MCLK, 9, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 4, 0,
+ 0),
+};
+
+struct sky1_audss_clks_devtype_data {
+ u32 (*reg_save)[2];
+ size_t reg_save_size;
+ const char * const *clk_names;
+ size_t clk_num;
+ const u32 *clk_rate_default;
+ const struct composite_clk_cfg *clk_cfg;
+ size_t clk_cfg_size;
+};
+
+struct sky1_audss_clks_priv {
+ struct device *dev;
+ struct regmap *regmap_cru;
+ struct clk *clks[AUDSS_CLK_PARENTS_MAX];
+ struct reset_control *rst_noc;
+ const struct sky1_audss_clks_devtype_data *devtype_data;
+ spinlock_t lock;
+ struct clk_hw_onecell_data *clk_data;
+};
+
+/*
+ * clk_ops for audss clock mux/divider/gate
+ */
+struct sky1_clk_divider {
+ struct clk_divider div;
+ struct regmap *regmap;
+ int offset;
+};
+
+struct sky1_clk_gate {
+ struct clk_gate gate;
+ struct regmap *regmap;
+ int offset;
+};
+
+struct sky1_clk_mux {
+ struct clk_mux mux;
+ struct regmap *regmap;
+ int offset;
+};
+
+static inline struct sky1_clk_mux *to_sky1_clk_mux(struct clk_mux *mux)
+{
+ return container_of(mux, struct sky1_clk_mux, mux);
+}
+
+static u8 sky1_audss_clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux);
+ u32 val;
+
+ regmap_read(sky1_mux->regmap, sky1_mux->offset, &val);
+ val = val >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
+}
+
+static int sky1_audss_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
+ struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux);
+ unsigned long flags = 0;
+ u32 reg;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+ else
+ __acquire(mux->lock);
+
+ if (mux->flags & CLK_MUX_HIWORD_MASK) {
+ reg = mux->mask << (mux->shift + 16);
+ } else {
+ regmap_read(sky1_mux->regmap, sky1_mux->offset, ®);
+ reg &= ~(mux->mask << mux->shift);
+ }
+ val = val << mux->shift;
+ reg |= val;
+ regmap_write(sky1_mux->regmap, sky1_mux->offset, reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+ else
+ __release(mux->lock);
+
+ return 0;
+}
+
+static int sky1_audss_clk_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+
+ return clk_mux_determine_rate_flags(hw, req, mux->flags);
+}
+
+static const struct clk_ops sky1_audss_clk_mux_ops = {
+ .get_parent = sky1_audss_clk_mux_get_parent,
+ .set_parent = sky1_audss_clk_mux_set_parent,
+ .determine_rate = sky1_audss_clk_mux_determine_rate,
+};
+
+static inline struct sky1_clk_divider *to_sky1_clk_divider(struct clk_divider *div)
+{
+ return container_of(div, struct sky1_clk_divider, div);
+}
+
+static unsigned long sky1_audss_clk_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+ unsigned int val;
+
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val = val >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_recalc_rate(hw, parent_rate, val, divider->table,
+ divider->flags, divider->width);
+}
+
+static int sky1_audss_clk_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ u32 val;
+
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val = val >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_ro_determine_rate(hw, req, divider->table,
+ divider->width,
+ divider->flags, val);
+ }
+
+ return divider_determine_rate(hw, req, divider->table, divider->width,
+ divider->flags);
+}
+
+static int sky1_audss_clk_divider_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+ int value;
+ unsigned long flags = 0;
+ u32 val;
+
+ value = divider_get_val(rate, parent_rate, divider->table,
+ divider->width, divider->flags);
+ if (value < 0)
+ return value;
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+ else
+ __acquire(divider->lock);
+
+ if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+ val = clk_div_mask(divider->width) << (divider->shift + 16);
+ } else {
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
+ }
+ val |= (u32)value << divider->shift;
+ regmap_write(sky1_div->regmap, sky1_div->offset, val);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+ else
+ __release(divider->lock);
+
+ return 0;
+}
+
+static const struct clk_ops sky1_audss_clk_divider_ops = {
+ .recalc_rate = sky1_audss_clk_divider_recalc_rate,
+ .determine_rate = sky1_audss_clk_divider_determine_rate,
+ .set_rate = sky1_audss_clk_divider_set_rate,
+};
+
+static inline struct sky1_clk_gate *to_sky1_clk_gate(struct clk_gate *gate)
+{
+ return container_of(gate, struct sky1_clk_gate, gate);
+}
+
+static void sky1_audss_clk_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate);
+ int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
+ unsigned long flags = 0;
+ u32 reg;
+
+ set ^= enable;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+ else
+ __acquire(gate->lock);
+
+ if (gate->flags & CLK_GATE_HIWORD_MASK) {
+ reg = BIT(gate->bit_idx + 16);
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ } else {
+ regmap_read(sky1_gate->regmap, sky1_gate->offset, ®);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+ }
+
+ regmap_write(sky1_gate->regmap, sky1_gate->offset, reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+ else
+ __release(gate->lock);
+}
+
+static int sky1_audss_clk_gate_enable(struct clk_hw *hw)
+{
+ sky1_audss_clk_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void sky1_audss_clk_gate_disable(struct clk_hw *hw)
+{
+ sky1_audss_clk_gate_endisable(hw, 0);
+}
+
+static int sky1_audss_clk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate);
+ u32 reg;
+
+ regmap_read(sky1_gate->regmap, sky1_gate->offset, ®);
+
+ /* if a set bit disables this clk, flip it before masking */
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ reg ^= BIT(gate->bit_idx);
+
+ reg &= BIT(gate->bit_idx);
+
+ return !!reg;
+}
+
+static const struct clk_ops sky1_audss_clk_gate_ops = {
+ .enable = sky1_audss_clk_gate_enable,
+ .disable = sky1_audss_clk_gate_disable,
+ .is_enabled = sky1_audss_clk_gate_is_enabled,
+};
+
+static struct clk_hw *sky1_audss_clk_register(struct device *dev,
+ const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ struct regmap *regmap,
+ struct muxdiv_cfg *mux_cfg,
+ struct muxdiv_cfg *div_cfg,
+ struct gate_cfg *gate_cfg,
+ unsigned long flags,
+ spinlock_t *lock)
+{
+ const struct clk_ops *sky1_mux_ops = NULL;
+ const struct clk_ops *sky1_div_ops = NULL;
+ const struct clk_ops *sky1_gate_ops = NULL;
+ struct clk_hw *hw = ERR_PTR(-ENOMEM);
+ struct sky1_clk_divider *sky1_div = NULL;
+ struct sky1_clk_gate *sky1_gate = NULL;
+ struct sky1_clk_mux *sky1_mux = NULL;
+
+ if (mux_cfg->offset >= 0) {
+ sky1_mux = devm_kzalloc(dev, sizeof(*sky1_mux), GFP_KERNEL);
+ if (!sky1_mux)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_mux->mux.reg = NULL;
+ sky1_mux->mux.shift = mux_cfg->shift;
+ sky1_mux->mux.mask = BIT(mux_cfg->width) - 1;
+ sky1_mux->mux.flags = mux_cfg->flags;
+ sky1_mux->mux.lock = lock;
+ sky1_mux_ops = &sky1_audss_clk_mux_ops;
+ sky1_mux->regmap = regmap;
+ sky1_mux->offset = mux_cfg->offset;
+ }
+
+ if (div_cfg->offset >= 0) {
+ sky1_div = devm_kzalloc(dev, sizeof(*sky1_div), GFP_KERNEL);
+ if (!sky1_div)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_div->div.reg = NULL;
+ sky1_div->div.shift = div_cfg->shift;
+ sky1_div->div.width = div_cfg->width;
+ sky1_div->div.flags = div_cfg->flags | CLK_DIVIDER_POWER_OF_TWO;
+ sky1_div->div.lock = lock;
+ sky1_div_ops = &sky1_audss_clk_divider_ops;
+ sky1_div->regmap = regmap;
+ sky1_div->offset = div_cfg->offset;
+ }
+
+ if (gate_cfg->offset >= 0) {
+ sky1_gate = devm_kzalloc(dev, sizeof(*sky1_gate), GFP_KERNEL);
+ if (!sky1_gate)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_gate->gate.reg = NULL;
+ sky1_gate->gate.bit_idx = gate_cfg->shift;
+ sky1_gate->gate.flags = gate_cfg->flags;
+ sky1_gate->gate.lock = lock;
+ sky1_gate_ops = &sky1_audss_clk_gate_ops;
+ sky1_gate->regmap = regmap;
+ sky1_gate->offset = gate_cfg->offset;
+ }
+
+ hw = clk_hw_register_composite(dev, name, parent_names, num_parents,
+ sky1_mux ? &sky1_mux->mux.hw : NULL, sky1_mux_ops,
+ sky1_div ? &sky1_div->div.hw : NULL, sky1_div_ops,
+ sky1_gate ? &sky1_gate->gate.hw : NULL, sky1_gate_ops,
+ flags);
+ if (IS_ERR(hw)) {
+ dev_err(dev, "register %s clock failed with err = %ld\n",
+ name, PTR_ERR(hw));
+ return hw;
+ }
+
+ return hw;
+}
+
+static int sky1_audss_clks_get(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ priv->clks[i] = devm_clk_get(priv->dev, devtype_data->clk_names[i]);
+ if (IS_ERR(priv->clks[i]))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->clks[i]),
+ "failed to get clock %s", devtype_data->clk_names[i]);
+ }
+
+ return 0;
+}
+
+static int sky1_audss_clks_enable(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i, err;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ err = clk_prepare_enable(priv->clks[i]);
+ if (err) {
+ dev_err(priv->dev, "failed to enable clock %s\n",
+ devtype_data->clk_names[i]);
+ goto err_clks;
+ }
+ }
+
+ return 0;
+
+err_clks:
+ while (--i >= 0)
+ clk_disable_unprepare(priv->clks[i]);
+
+ return err;
+}
+
+static void sky1_audss_clks_disable(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i;
+
+ for (i = 0; i < devtype_data->clk_num; i++)
+ clk_disable_unprepare(priv->clks[i]);
+}
+
+static int sky1_audss_clks_set_rate(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i, err;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ err = clk_set_rate(priv->clks[i], devtype_data->clk_rate_default[i]);
+ if (err) {
+ dev_err(priv->dev, "failed to set clock rate %s\n",
+ devtype_data->clk_names[i]);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* register sky1 audio subsystem clocks */
+static int sky1_audss_clk_probe(struct platform_device *pdev)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data;
+ struct sky1_audss_clks_priv *priv;
+ struct device_node *parent_np;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rst_noc;
+ struct clk_hw **clk_table;
+ struct regmap *regmap_cru;
+ int i, ret;
+
+ parent_np = of_get_parent(pdev->dev.of_node);
+ regmap_cru = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(regmap_cru))
+ return dev_err_probe(dev, PTR_ERR(regmap_cru),
+ "unable to get audss cru regmap");
+
+ devtype_data = device_get_match_data(dev);
+ if (!devtype_data)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ priv->clk_data = devm_kzalloc(&pdev->dev,
+ struct_size(priv->clk_data, hws, AUDSS_MAX_CLKS),
+ GFP_KERNEL);
+ if (!priv->clk_data)
+ return -ENOMEM;
+
+ priv->clk_data->num = AUDSS_MAX_CLKS;
+ clk_table = priv->clk_data->hws;
+
+ priv->dev = dev;
+ priv->regmap_cru = regmap_cru;
+ priv->devtype_data = devtype_data;
+
+ ret = sky1_audss_clks_get(priv);
+ if (ret)
+ return ret;
+
+ rst_noc = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(rst_noc))
+ return dev_err_probe(dev, PTR_ERR(rst_noc),
+ "failed to get noc reset");
+ priv->rst_noc = rst_noc;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = sky1_audss_clks_enable(priv);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ /*
+ * Enable runtime PM here to allow the clock core using runtime PM
+ * for the registered clocks.
+ */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = sky1_audss_clks_set_rate(priv);
+ if (ret) {
+ dev_err(dev, "failed to set clocks rate\n");
+ goto fail_clks_set;
+ }
+
+ /* assert reset */
+ reset_control_assert(rst_noc);
+ usleep_range(1, 2);
+
+ /* deassert reset */
+ reset_control_deassert(rst_noc);
+
+ /* audio_clk4 clock fixed divider */
+ clk_table[CLK_AUD_CLK4_DIV2] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk4_div2",
+ "audio_clk4",
+ 0,
+ 1, 2);
+ if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV2])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV2]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV2, ret);
+ goto fail_fixed_clk;
+ }
+
+ clk_table[CLK_AUD_CLK4_DIV4] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk4_div4",
+ "audio_clk4",
+ 0,
+ 1, 4);
+ if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV4])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV4]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV4, ret);
+ goto fail_fixed_clk;
+ }
+
+ /* audio_clk5 clock fixed divider */
+ clk_table[CLK_AUD_CLK5_DIV2] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk5_div2",
+ "audio_clk5",
+ 0,
+ 1, 2);
+ if (IS_ERR(clk_table[CLK_AUD_CLK5_DIV2])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK5_DIV2]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK5_DIV2, ret);
+ goto fail_fixed_clk;
+ }
+
+ for (i = 0; i < devtype_data->clk_cfg_size; i++) {
+ clk_table[devtype_data->clk_cfg[i].id] =
+ sky1_audss_clk_register(dev,
+ devtype_data->clk_cfg[i].name,
+ devtype_data->clk_cfg[i].parent_names,
+ devtype_data->clk_cfg[i].num_parents,
+ regmap_cru,
+ devtype_data->clk_cfg[i].mux_cfg,
+ devtype_data->clk_cfg[i].div_cfg,
+ devtype_data->clk_cfg[i].gate_cfg,
+ devtype_data->clk_cfg[i].flags,
+ &priv->lock);
+ if (IS_ERR(clk_table[devtype_data->clk_cfg[i].id])) {
+ ret = PTR_ERR(clk_table[devtype_data->clk_cfg[i].id]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n",
+ devtype_data->clk_cfg[i].id, ret);
+ goto fail_array_clk;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv->clk_data);
+ if (ret) {
+ dev_err(dev, "failed to add clock provider: %d\n", ret);
+ goto fail_register;
+ }
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+
+fail_register:
+fail_array_clk:
+ while (i--)
+ clk_hw_unregister_composite(clk_table[devtype_data->clk_cfg[i].id]);
+fail_fixed_clk:
+fail_clks_set:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void sky1_audss_clk_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i = 0;
+
+ for (i = 0; i < devtype_data->clk_cfg_size; i++)
+ clk_hw_unregister_composite(priv->clk_data->hws[devtype_data->clk_cfg[i].id]);
+
+ if (!pm_runtime_status_suspended(dev))
+ pm_runtime_force_suspend(dev);
+
+ pm_runtime_disable(dev);
+}
+
+static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev)
+{
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ for (i = 0; i < devtype_data->reg_save_size; i++)
+ regmap_read(priv->regmap_cru,
+ devtype_data->reg_save[i][0], &devtype_data->reg_save[i][1]);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ sky1_audss_clks_disable(priv);
+
+ return 0;
+}
+
+static int __maybe_unused sky1_audss_clk_runtime_resume(struct device *dev)
+{
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ unsigned long flags;
+ int i, ret;
+
+ ret = sky1_audss_clks_enable(priv);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ reset_control_deassert(priv->rst_noc);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ for (i = 0; i < devtype_data->reg_save_size; i++)
+ regmap_write(priv->regmap_cru,
+ devtype_data->reg_save[i][0], devtype_data->reg_save[i][1]);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static const struct dev_pm_ops sky1_audss_clk_pm_ops = {
+ SET_RUNTIME_PM_OPS(sky1_audss_clk_runtime_suspend,
+ sky1_audss_clk_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct sky1_audss_clks_devtype_data sky1_devtype_data = {
+ .reg_save = sky1_reg_save,
+ .reg_save_size = ARRAY_SIZE(sky1_reg_save),
+ .clk_names = sky1_audss_clk_names,
+ .clk_num = ARRAY_SIZE(sky1_audss_clk_names),
+ .clk_rate_default = sky1_clk_rate_default,
+ .clk_cfg = sky1_audss_clks,
+ .clk_cfg_size = ARRAY_SIZE(sky1_audss_clks),
+};
+
+static const struct of_device_id sky1_audss_clk_of_match[] = {
+ { .compatible = "cix,sky1-audss-clock", .data = &sky1_devtype_data, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sky1_audss_clk_of_match);
+
+static struct platform_driver sky1_audss_clk_driver = {
+ .probe = sky1_audss_clk_probe,
+ .remove = sky1_audss_clk_remove,
+ .driver = {
+ .name = "sky1-audss-clk",
+ .suppress_bind_attrs = true,
+ .of_match_table = sky1_audss_clk_of_match,
+ .pm = &sky1_audss_clk_pm_ops,
+ },
+};
+module_platform_driver(sky1_audss_clk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joakim Zhang <joakim.zhang@cixtech.com>");
+MODULE_DESCRIPTION("Cixtech Sky1 Audio Subsystem Clock Controller Driver");
--
2.50.1
^ permalink raw reply related
* [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260605032225.523669-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
The Cix Sky1 Audio Subsystem (AUDSS) groups audio-related clock, reset
and control registers in a dedicated CRU block. Software reset lines are
exposed on the syscon parent via #reset-cells, following the same model
as the existing Sky1 FCH and S5 system control bindings.
Add the cix,sky1-audss-system-control compatible to
cix,sky1-system-control.yaml for the MFD/syscon parent node, and define
AUDSS software reset indices in
include/dt-bindings/reset/cix,sky1-audss-system-control.h for I2S, HDA,
DMAC, mailbox, watchdog and timer blocks.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
.../soc/cix/cix,sky1-system-control.yaml | 39 ++++++++++++++++---
.../reset/cix,sky1-audss-system-control.h | 27 +++++++++++++
2 files changed, 61 insertions(+), 5 deletions(-)
create mode 100644 include/dt-bindings/reset/cix,sky1-audss-system-control.h
diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
index a01a515222c6..3b243b02a7e8 100644
--- a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
+++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
@@ -15,11 +15,16 @@ description:
properties:
compatible:
- items:
- - enum:
- - cix,sky1-system-control
- - cix,sky1-s5-system-control
- - const: syscon
+ oneOf:
+ - items:
+ - enum:
+ - cix,sky1-system-control
+ - cix,sky1-s5-system-control
+ - const: syscon
+ - items:
+ - const: cix,sky1-audss-system-control
+ - const: simple-mfd
+ - const: syscon
reg:
maxItems: 1
@@ -27,6 +32,11 @@ properties:
'#reset-cells':
const: 1
+ clock-controller:
+ $ref: /schemas/clock/cix,sky1-audss-clock.yaml#
+ description:
+ AUDSS internal clock provider (cix,sky1-audss-system-control only).
+
required:
- compatible
- reg
@@ -40,3 +50,22 @@ examples:
reg = <0x4160000 0x100>;
#reset-cells = <1>;
};
+ - |
+ #include <dt-bindings/reset/cix,sky1-audss-system-control.h>
+
+ audss_syscon: system-controller@7110000 {
+ compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
+ reg = <0x7110000 0x10000>;
+ #reset-cells = <1>;
+
+ clock-controller {
+ compatible = "cix,sky1-audss-clock";
+ power-domains = <&smc_devpd 0>;
+ #clock-cells = <1>;
+ clocks = <&scmi_clk 0>, <&scmi_clk 1>, <&scmi_clk 2>,
+ <&scmi_clk 3>, <&scmi_clk 4>, <&scmi_clk 5>;
+ clock-names = "audio_clk0", "audio_clk1", "audio_clk2",
+ "audio_clk3", "audio_clk4", "audio_clk5";
+ resets = <&src 0>;
+ };
+ };
diff --git a/include/dt-bindings/reset/cix,sky1-audss-system-control.h b/include/dt-bindings/reset/cix,sky1-audss-system-control.h
new file mode 100644
index 000000000000..2ebc5c4f10cd
--- /dev/null
+++ b/include/dt-bindings/reset/cix,sky1-audss-system-control.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright 2026 Cix Technology Group Co., Ltd.
+ */
+#ifndef DT_BINDING_RESET_CIX_SKY1_AUDSS_SYSTEM_CONTROL_H
+#define DT_BINDING_RESET_CIX_SKY1_AUDSS_SYSTEM_CONTROL_H
+
+#define AUDSS_I2S0_SW_RST_N 0
+#define AUDSS_I2S1_SW_RST_N 1
+#define AUDSS_I2S2_SW_RST_N 2
+#define AUDSS_I2S3_SW_RST_N 3
+#define AUDSS_I2S4_SW_RST_N 4
+#define AUDSS_I2S5_SW_RST_N 5
+#define AUDSS_I2S6_SW_RST_N 6
+#define AUDSS_I2S7_SW_RST_N 7
+#define AUDSS_I2S8_SW_RST_N 8
+#define AUDSS_I2S9_SW_RST_N 9
+#define AUDSS_WDT_SW_RST_N 10
+#define AUDSS_TIMER_SW_RST_N 11
+#define AUDSS_MB0_SW_RST_N 12
+#define AUDSS_MB1_SW_RST_N 13
+#define AUDSS_HDA_SW_RST_N 14
+#define AUDSS_DMAC_SW_RST_N 15
+
+#define SKY1_AUDSS_SW_RESET_NUM 16
+
+#endif
--
2.50.1
^ permalink raw reply related
* [PATCH v2 2/5] reset: cix: add audss support to sky1 reset driver
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260605032225.523669-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Extend the Sky1 reset controller driver for the AUDSS CRU syscon. The
AUDSS block provides sixteen active-low software reset bits in one
register for audio subsystem peripherals.
Add a device tree match for cix,sky1-audss-system-control and the
corresponding reset signal table in reset-sky1.c, reusing the existing
regmap-based reset ops used by the FCH and S5 system control variants.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
drivers/reset/reset-sky1.c | 36 ++++++++++++++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/drivers/reset/reset-sky1.c b/drivers/reset/reset-sky1.c
index 78e80a533c39..ff91ce93c16b 100644
--- a/drivers/reset/reset-sky1.c
+++ b/drivers/reset/reset-sky1.c
@@ -16,6 +16,7 @@
#include <dt-bindings/reset/cix,sky1-system-control.h>
#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+#include <dt-bindings/reset/cix,sky1-audss-system-control.h>
#define SKY1_RESET_SLEEP_MIN_US 50
#define SKY1_RESET_SLEEP_MAX_US 100
@@ -258,6 +259,34 @@ static const struct sky1_src_variant variant_sky1_fch = {
.signals_num = ARRAY_SIZE(sky1_src_fch_signals),
};
+enum {
+ AUDSS_SW_RST = 0x78,
+};
+
+static const struct sky1_src_signal sky1_audss_signals[SKY1_AUDSS_SW_RESET_NUM] = {
+ [AUDSS_I2S0_SW_RST_N] = { AUDSS_SW_RST, BIT(0) },
+ [AUDSS_I2S1_SW_RST_N] = { AUDSS_SW_RST, BIT(1) },
+ [AUDSS_I2S2_SW_RST_N] = { AUDSS_SW_RST, BIT(2) },
+ [AUDSS_I2S3_SW_RST_N] = { AUDSS_SW_RST, BIT(3) },
+ [AUDSS_I2S4_SW_RST_N] = { AUDSS_SW_RST, BIT(4) },
+ [AUDSS_I2S5_SW_RST_N] = { AUDSS_SW_RST, BIT(5) },
+ [AUDSS_I2S6_SW_RST_N] = { AUDSS_SW_RST, BIT(6) },
+ [AUDSS_I2S7_SW_RST_N] = { AUDSS_SW_RST, BIT(7) },
+ [AUDSS_I2S8_SW_RST_N] = { AUDSS_SW_RST, BIT(8) },
+ [AUDSS_I2S9_SW_RST_N] = { AUDSS_SW_RST, BIT(9) },
+ [AUDSS_WDT_SW_RST_N] = { AUDSS_SW_RST, BIT(10) },
+ [AUDSS_TIMER_SW_RST_N] = { AUDSS_SW_RST, BIT(11) },
+ [AUDSS_MB0_SW_RST_N] = { AUDSS_SW_RST, BIT(12) },
+ [AUDSS_MB1_SW_RST_N] = { AUDSS_SW_RST, BIT(13) },
+ [AUDSS_HDA_SW_RST_N] = { AUDSS_SW_RST, BIT(14) },
+ [AUDSS_DMAC_SW_RST_N] = { AUDSS_SW_RST, BIT(15) },
+};
+
+static const struct sky1_src_variant variant_sky1_audss = {
+ .signals = sky1_audss_signals,
+ .signals_num = ARRAY_SIZE(sky1_audss_signals),
+};
+
static struct sky1_src *to_sky1_src(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct sky1_src, rcdev);
@@ -329,6 +358,8 @@ static int sky1_reset_probe(struct platform_device *pdev)
return -ENOMEM;
variant = of_device_get_match_data(dev);
+ if (!variant)
+ return -ENODEV;
sky1src->regmap = device_node_to_regmap(dev->of_node);
if (IS_ERR(sky1src->regmap)) {
@@ -347,8 +378,9 @@ static int sky1_reset_probe(struct platform_device *pdev)
}
static const struct of_device_id sky1_sysreg_of_match[] = {
- { .compatible = "cix,sky1-system-control", .data = &variant_sky1_fch},
- { .compatible = "cix,sky1-s5-system-control", .data = &variant_sky1},
+ { .compatible = "cix,sky1-system-control", .data = &variant_sky1_fch },
+ { .compatible = "cix,sky1-s5-system-control", .data = &variant_sky1 },
+ { .compatible = "cix,sky1-audss-system-control", .data = &variant_sky1_audss },
{},
};
MODULE_DEVICE_TABLE(of, sky1_sysreg_of_match);
--
2.50.1
^ permalink raw reply related
* [PATCH v2 5/5] arm64: dts: cix: sky1: add audss system control
From: joakim.zhang @ 2026-06-05 3:22 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260605032225.523669-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Add audss system control device node, which would provides
clocks and resets for devices in audss domain.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
arch/arm64/boot/dts/cix/sky1.dtsi | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index bb5cfb1f2113..0edc35523c71 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -6,6 +6,10 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/clock/cix,sky1-audss.h>
+#include <dt-bindings/reset/cix,sky1-system-control.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+#include <dt-bindings/reset/cix,sky1-audss-system-control.h>
#include "sky1-power.h"
/ {
@@ -488,6 +492,30 @@ mbox_pm2ap: mailbox@65a0080 {
cix,mbox-dir = "rx";
};
+ audss_cru: system-controller@7110000 {
+ compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
+ reg = <0x0 0x07110000 0x0 0x10000>;
+ #reset-cells = <1>;
+ status = "okay";
+
+ audss_clk: clock-controller {
+ compatible = "cix,sky1-audss-clock";
+ power-domains = <&smc_devpd SKY1_PD_AUDIO>;
+ resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>;
+ clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK1>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK2>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK3>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK4>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK5>;
+ clock-names = "audio_clk0", "audio_clk1",
+ "audio_clk2", "audio_clk3",
+ "audio_clk4", "audio_clk5";
+ #clock-cells = <1>;
+ status = "okay";
+ };
+ };
+
mbox_sfh2ap: mailbox@8090000 {
compatible = "cix,sky1-mbox";
reg = <0x0 0x08090000 0x0 0x10000>;
--
2.50.1
^ permalink raw reply related
* Re: [PATCH v14 0/7] Provide support for Trigger Generation Unit
From: Songwei.Chai @ 2026-06-05 3:14 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt, gregkh
Cc: linux-kernel, linux-arm-kernel, linux-arm-msm, coresight,
devicetree, gregkh
In-Reply-To: <20260417073336.2712426-1-songwei.chai@oss.qualcomm.com>
hi Greg & Alexander,
I hope this message finds you well.
We are currently working on a Qualcomm TGU (Trace Generation Unit)
driver and would like to seek your guidance on how best to integrate it
into the /hwtracing/ subsystem.
TGU is a programmable hardware block that monitors signal conditions and
triggers debug-related actions, effectively acting as a trace generation
utility. Based on its functionality, placing it under
|"drivers/hwtracing"| appears to be a reasonable choice.
We initially explored integrating it into
"|drivers/hwtracing/coresight"|.However, that approach did not receive
support, primarily because the component is not tightly coupled with the
CoreSight subsystem.
*Chat History*:
https://lore.kernel.org/all/CAJ9a7ViKxHThyZfFFDV_FkNRimk4uo1NrMtQ-kcaj1qO4ZcGnA@mail.gmail.com/
As an alternative, we are proposing to introduce a dedicated
|"drivers/hwtracing/qcom/"|directory, similar to the existing
"|drivers/hwtracing/intel_th"|.
A more detailed rationale can be found in the cover letter under the
section /"Why we are proposing this:"/.
*Current status of the patch:*
* Reviewed-by: Jie Gan
* Acked-by: Konrad Dybcio
We would greatly appreciate it if you could take some time to review
this patch and share your thoughts. Your feedback would be very helpful
in moving this effort forward in the right direction.
Thanks a lot for your time and consideration.
Best regards,
Songwei
On 4/17/2026 3:33 PM, Songwei Chai wrote:
> We propose creating a new qcom directory under drivers/hwtracing
> to host this TGU driver, as well as additional Qualcomm-specific
> hwtracing drivers that we plan to submit in the coming months.
> This structure will help organize vendor-specific implementations
> and facilitate future development and maintenance.
>
> Feedback from the community on this proposal is highly appreciated.
>
> - Why we are proposing this:
>
> TGU has the ability to monitor signal conditions and trigger debug-related
> actions, serving as a programmable hardware component that enhances system
> trace and debug capabilities. Placing it under drivers/hwtracing aligns
> with its function as a trace generation utility.
>
> We previously attempted to push this driver to drivers/hwtracing/coresight,
> but did not receive support from the maintainers of the CoreSight
> subsystem. The reason provided was: “This component is primarily a part
> of the Qualcomm proprietary QPMDA subsystem, and is capable of operating
> independently from the CoreSight hardware trace generation system.”
>
> Chat history : https://lore.kernel.org/all/CAJ9a7ViKxHThyZfFFDV_FkNRimk4uo1NrMtQ-kcaj1qO4ZcGnA@mail.gmail.com/
>
> Given this, we have been considering whether it would be appropriate
> to create a dedicated drivers/hwtracing/qcom directory for
> Qualcomm-related hwtracing drivers. This would follow the precedent set
> by Intel, which maintains its own directory at drivers/hwtracing/intel_th.
> We believe this structure would significantly facilitate
> future submissions of related Qualcomm drivers.
>
> - Maintenance of drivers/hwtracing/qcom:
>
> Bjorn, who maintains linux-arm-msm, will be the maintainer of this
> directory — we’ve discussed this with him and he’s aware that his task
> list may grow accordingly. Additionally, Qualcomm engineers familiar with
> the debug hardware — such as [Tingwei Zhang, Jinlong Mao, Songwei Chai],
> will be available to review incoming patches and support ongoing
> development.
>
> - Detail for TGU:
>
> This component can be utilized to sense a plurality of signals and
> create a trigger into the CTI or generate interrupts to processors
> once the input signal meets the conditions. We can treat the TGU’s
> workflow as a flowsheet, it has some “steps” regions for customization.
> In each step region, we can set the signals that we want with priority
> in priority_group, set the conditions in each step via condition_decode,
> and set the resultant action by condition_select. Meanwhile,
> some TGUs (not all) also provide timer/counter functionality.
> Based on the characteristics described above, we consider the TGU as a
> helper in the CoreSight subsystem. Its master device is the TPDM, which
> can transmit signals from other subsystems, and we reuse the existing
> ports mechanism to link the TPDM to the connected TGU.
>
> Here is a detailed example to explain how to use the TGU:
>
> In this example, the TGU is configured to use 2 conditions, 2 steps, and
> the timer. The goal is to look for one of two patterns which are generated
> from TPDM, giving priority to one, and then generate a trigger once the
> timer reaches a certain value. In other words, two conditions are used
> for the first step to look for the two patterns, where the one with the
> highest priority is used in the first condition. Then, in the second step,
> the timer is enabled and set to be compared to the given value at each
> clock cycle. These steps are better shown below.
>
> |-----------------|
> | |
> | TPDM |
> | |
> |-----------------|
> |
> |
> --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ------
> | | |
> | | |--------------------| |
> | |---- ---> | | Go to next steps | |
> | | | |--- ---> | Enable timer | |
> | | v | | | |
> | | |-----------------| | |--------------------| |
> | | | | Yes | | |
> | | | inputs==0xB | ----->| | <-------- |
> | | | | | | No | |
> | No | |-----------------| | v | |
> | | | | |-----------------| | |
> | | | | | | | |
> | | | | | timer>=3 |-- |
> | | v | | | |
> | | |-----------------| | |-----------------| |
> | | | | Yes | | |
> | |--- | inputs==0xA | ----->| | Yes |
> | | | | |
> | |-----------------| v |
> | |-----------------| |
> | | | |
> | | Trigger | |
> | | | |
> | |-----------------| |
> | TGU | |
> |--- --- --- --- --- --- --- --- --- --- --- --- --- --- |--- --- -- |
> |
> v
> |-----------------|
> |The controllers |
> |which will use |
> |triggers further |
> |-----------------|
>
> steps:
> 1. Reset TGU /*it will disable tgu and reset dataset*/
> - echo 1 > /sys/bus/amba/devices/<tgu-name>/reset_tgu
>
> 2. Set the pattern match for priority0 to 0xA = 0b1010 and for
> priority 1 to 0xB = 0b1011.
> - echo 0x11113232 > /sys/bus/amba/devices/<tgu-name>/step0_priority0/reg0
> - echo 0x11113233 > /sys/bus/amba/devices/<tgu-name>/step0_priority1/reg0
>
> Note:
> Bit distribution diagram for each priority register
> |-------------------------------------------------------------------|
> | Bits | Field Nam | Description |
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 29:28 | SEL_BIT7_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 25:24 | SEL_BIT6_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 21:20 | SEL_BIT5_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 17:16 | SEL_BIT4_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 13:12 | SEL_BIT3_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 9:8 | SEL_BIT2_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 5:4 | SEL_BIT1_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> | | | 00 = bypass for OR output |
> | 1:0 | SEL_BIT0_TYPE2 | 01 = bypass for AND output |
> | | | 10 = sense input '0' is true|
> | | | 11 = sense input '1' is true|
> |-------------------------------------------------------------------|
> These bits are used to identify the signals we want to sense, with
> a maximum signal number of 140. For example, to sense the signal
> 0xA (binary 1010), we set the value of bits 0 to 13 to 3232, which
> represents 1010. The remaining bits are set to 1, as we want to use
> AND gate to summarize all the signals we want to sense here. For
> rising or falling edge detection of any input to the priority, set
> the remaining bits to 0 to use an OR gate.
>
> 3. look for the pattern for priority_i i=0,1.
> - echo 0x3 > /sys/bus/amba/devices/<tgu-name>/step0_condition_decode/reg0
> - echo 0x30 > /sys/bus/amba/devices/<tgu-name>/step0_condition_decode/reg1
>
> |-------------------------------------------------------------------------------|
> | Bits | Field Nam | Description |
> |-------------------------------------------------------------------------------|
> | | |For each decoded condition, this |
> | 24 | NOT |inverts the output. If the condition |
> | | |decodes to true, and the NOT field |
> | | |is '1', then the output is NOT true. |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | 21 | BC0_COMP_ACTIVE |comparator will be actively included in|
> | | |the decoding of this particular |
> | | |condition. |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | | |comparator will need to be 1 to affect |
> | 20 | BC0_COMP_HIGH |the decoding of this condition. |
> | | |Conversely, a '0' here requires a '0' |
> | | |from the comparator |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | 17 | |comparator will be actively included in|
> | | TC0_COMP_ACTIVE |the decoding of this particular |
> | | |condition. |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | | |comparator will need to be 1 to affect |
> | 16 | TC0_COMP_HIGH |the decoding of this particular |
> | | |condition.Conversely, a 0 here |
> | | |requires a '0' from the comparator |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from Priority_n |
> | | |OR logic will be actively |
> | 4n+3 | Priority_n_OR_ACTIVE|included in the decoding of |
> | | (n=0,1,2,3) |this particular condition. |
> | | | |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from Priority_n |
> | | |will need to be '1' to affect the |
> | 4n+2 | Priority_n_OR_HIGH |decoding of this particular |
> | | (n=0,1,2,3) |condition. Conversely, a '0' here |
> | | |requires a '0' from Priority_n OR logic|
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from Priority_n |
> | | |AND logic will be actively |
> | 4n+1 |Priority_n_AND_ACTIVE|included in the decoding of this |
> | | (n=0,1,2,3) |particular condition. |
> | | | |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from Priority_n |
> | | |AND logic will need to be '1' to |
> | 4n | Priority_n_AND_HIGH |affect the decoding of this |
> | | (n=0,1,2,3) |particular condition. Conversely, |
> | | |a '0' here requires a '0' from |
> | | |Priority_n AND logic. |
> |-------------------------------------------------------------------------------|
> Since we use `priority_0` and `priority_1` with an AND output in step 2, we set `0x3`
> and `0x30` here to activate them.
>
> 4. Set NEXT_STEP = 1 and TC0_ENABLE = 1 so that when the conditions
> are met then the next step will be step 1 and the timer will be enabled.
> - echo 0x20008 > /sys/bus/amba/devices/<tgu-name>/step0_condition_select/reg0
> - echo 0x20008 > /sys/bus/amba/devices/<tgu-name>/step0_condition_select/reg1
>
> |-----------------------------------------------------------------------------|
> | Bits | Field Nam | Description |
> |-----------------------------------------------------------------------------|
> | | |This field defines the next step the |
> | 18:17 | NEXT_STEP |TGU will 'goto' for the associated |
> | | |Condition and Step. |
> |-----------------------------------------------------------------------------|
> | | |For each possible output trigger |
> | 13 | TRIGGER |available, set a '1' if you want |
> | | |the trigger to go active for the |
> | | |associated condition and Step. |
> |-----------------------------------------------------------------------------|
> | | |This will cause BC0 to increment if the|
> | 9 | BC0_INC |associated Condition is decoded for |
> | | |this step. |
> |-----------------------------------------------------------------------------|
> | | |This will cause BC0 to decrement if the|
> | 8 | BC0_DEC |associated Condition is decoded for |
> | | |this step. |
> |-----------------------------------------------------------------------------|
> | | |This will clear BC0 count value to 0 if|
> | 7 | BC0_CLEAR |the associated Condition is decoded |
> | | |for this step. |
> |-----------------------------------------------------------------------------|
> | | |This will cause TC0 to increment until |
> | 3 | TC0_ENABLE |paused or cleared if the associated |
> | | |Condition is decoded for this step. |
> |-----------------------------------------------------------------------------|
> | | |This will cause TC0 to pause until |
> | 2 | TC0_PAUSE |enabled if the associated Condition |
> | | |is decoded for this step. |
> |-----------------------------------------------------------------------------|
> | | |This will clear TC0 count value to 0 |
> | 1 | TC0_CLEAR |if the associated Condition is |
> | | |decoded for this step. |
> |-----------------------------------------------------------------------------|
> | | |This will set the done signal to the |
> | 0 | DONE |TGU FSM if the associated Condition |
> | | |is decoded for this step. |
> |-----------------------------------------------------------------------------|
> Based on the distribution diagram, we set `0x20008` for `priority0` and `priority1` to
> achieve "jump to step 1 and enable TC0" once the signal is sensed.
>
> 5. activate the timer comparison for this step.
> - echo 0x30000 > /sys/bus/amba/devices/<tgu-name>/step1_condition_decode/reg0
>
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | 17 | |comparator will be actively included in|
> | | TC0_COMP_ACTIVE |the decoding of this particular |
> | | |condition. |
> |-------------------------------------------------------------------------------|
> | | |When '1' the output from the associated|
> | | |comparator will need to be 1 to affect |
> | 16 | TC0_COMP_HIGH |the decoding of this particular |
> | | |condition.Conversely, a 0 here |
> | | |requires a '0' from the comparator |
> |-------------------------------------------------------------------------------|
> Accroding to the decode distribution diagram , we give 0x30000 here to set 16th&17th bit
> to enable timer comparison.
>
> 6. Set the NEXT_STEP = 0 and TC0_PAUSE = 1 and TC0_CLEAR = 1 once the timer
> has reached the given value.
> - echo 0x6 > /sys/bus/amba/devices/<tgu-name>/step1_condition_select/reg0
>
> 7. Enable Trigger 0 for TGU when the condition 0 is met in step1,
> i.e. when the timer reaches 3.
> - echo 0x2000 > /sys/bus/amba/devices/<tgu-name>/step1_condition_select/default
>
> Note:
> 1. 'default' register allows for establishing the resultant action for
> the default condition
>
> 2. Trigger:For each possible output trigger available from
> the Design document, there are three triggers: interrupts, CTI,
> and Cross-TGU mapping.All three triggers can occur, but
> the choice of which trigger to use depends on the user's
> needs.
>
> 8. Compare the timer to 3 in step 1.
> - echo 0x3 > /sys/bus/amba/devices/<tgu-name>/step1_timer/reg0
>
> 9. enale tgu
> - echo 1 > /sys/bus/amba/devices/<tgu-name>/enable_tgu
> ---
> Link to V13: https://lore.kernel.org/all/20260402092838.341295-1-songwei.chai@oss.qualcomm.com/
>
> Changes in V14:
> - Fix some typos and formatting.
> ---
> Link to V12: https://lore.kernel.org/all/20260317032639.2393221-1-songwei.chai@oss.qualcomm.com/
>
> Changes in V13:
> - add ":" after "KernelVersion"
> - add an enablement check in the enable function to avoid increasing the counter each time
> ---
> Link to V11: https://lore.kernel.org/all/ee1ca8e6-8e5f-47d8-8a24-f904ee2fc6d0@oss.qualcomm.com/
>
> Changes in V12:
> - Remove the in-ports property from the bindings, as this device is decoupled from CoreSight.
> - Update kernel version and date.
> ---
> Link to V10: https://lore.kernel.org/all/20c5406d-3e9f-4fdb-84ba-4cbe629c79b5@oss.qualcomm.com/
>
> Changes in V11:
> - Change the names of members in drvdata: max_xxx -> num_xxx, enable -> enabled
> - Use "FIELD_GET" to replace "BMVAL"
> - Use devm_kcalloc to replace devm_kzalloc once create members of value_table
> - Keep a consistent \n above return
> - Keep reverse-Christmas-tree style
> - Add checks so that the enable and reset nodes only accept 0 or 1
> ---
> Link to V9: https://lore.kernel.org/all/20251219065902.2296896-1-songwei.chai@oss.qualcomm.com/
>
> Changes in V10:
> - Modified code formatting based on Jie's feedback to improve readability.
> - Applied inverse Christmas tree order to the variables.
> ---
> Link to V8: https://lore.kernel.org/all/20251203090055.2432719-1-songwei.chai@oss.qualcomm.com/
>
> Changes in V9:
> - Decoupled the tgu driver from coresight header file and registered it as an amba device.
> - Retained Rob's reviewed-by tag on patch1/7 since the file remains unchanged.
> - Updated the sysfs node path in the Documentation directory.
> ---
> Link to V7: https://lore.kernel.org/all/20251104064043.88972-1-songwei.chai@oss.qualcomm.com/
>
> Changes in V8:
> - Add "select" section in bindings.
> - Update publish date in "sysfs-bus-coresight-devices-tgu".
> ---
> Link to V6: https://lore.kernel.org/all/20250709104114.22240-1-songchai@qti.qualcomm.com/
>
> Changes in V7:
> - Move the TGU code location from 'drivers/hwtracing/coresight/' to 'drivers/hwtracing/qcom/'.
> - Rename the spinlock used in the code from 'spinlock' to 'lock'.
> - Perform the 'calculate_array_location' separately, instead of doing it within the function.
> - Update the sender email address.
> ---
> Link to V5: https://lore.kernel.org/all/20250529081949.26493-1-quic_songchai@quicinc.com/
>
> Changes in V6:
> - Replace spinlock with guard(spinlock) in tgu_enable.
> - Remove redundant blank line.
> - Update publish date and contact member's name in "sysfs-bus-coresight-devices-tgu".
> ---
> Link to V4: https://patchwork.kernel.org/project/linux-arm-msm/cover/20250423101054.954066-1-quic_songchai@quicinc.com/
>
> Changes in V5:
> - Update publish date and kernel_version in "sysfs-bus-coresight-devices-tgu"
> ---
> Link to V3: https://lore.kernel.org/all/20250227092640.2666894-1-quic_songchai@quicinc.com/
>
> Changes in V4:
> - Add changlog in coverletter.
> - Correct 'year' in Copyright in patch1.
> - Correct port mechansim description in patch1.
> - Remove 'tgu-steps','tgu-regs','tgu-conditions','tgu-timer-counters' from dt-binding
> and set them through reading DEVID register as per Mike's suggestion.
> - Modify tgu_disable func to make it have single return point in patch2 as per
> Mike's suggestion.
> - Use sysfs_emit in enable_tgu_show func in ptach2.
> - Remove redundant judgement in enable_tgu_store in patch2.
> - Correct typo in description in patch3.
> - Set default ret as SYSFS_GROUP_INVISIBLE, and returnret at end in pacth3 as
> per Mike's suggestion.
> - Remove tgu_dataset_ro definition in patch3
> - Use #define constants with explanations of what they are rather than
> arbitrary magic numbers in patch3 and patch4.
> - Check -EINVAL before using 'calculate_array_location()' in array in patch4.
> - Add 'default' in 'tgu_dataset_show''s switch part in patch4.
> - Document the value needed to initiate the reset in pacth7.
> - Check "value" in 'reset_tgu_store' and bail out with an error code if 0 in patch7.
> - Remove dev_dbg in 'reset_tgu_store' in patch7.
> ---
> Link to V2: https://lore.kernel.org/all/20241010073917.16023-1-quic_songchai@quicinc.com/
>
> Changes in V3:
> - Correct typo and format in dt-binding in patch1
> - Rebase to the latest kernel version
> ---
> Link to V1: https://lore.kernel.org/all/20240830092311.14400-1-quic_songchai@quicinc.com/
>
> Changes in V2:
> - Use real name instead of login name,
> - Correct typo and format in dt-binding and code.
> - Bring order in tgu_prob(declarations with and without assignments) as per
> Krzysztof's suggestion.
> - Add module device table in patch2.
> - Set const for tgu_common_grp and tgu_ids in patch2.
> - Initialize 'data' in tgu_ids to fix the warning in pacth2.
> ---
> Songwei Chai (7):
> dt-bindings: arm: Add support for Qualcomm TGU trace
> qcom-tgu: Add TGU driver
> qcom-tgu: Add signal priority support
> qcom-tgu: Add TGU decode support
> qcom-tgu: Add support to configure next action
> qcom-tgu: Add timer/counter functionality for TGU
> qcom-tgu: Add reset node to initialize
>
> .../ABI/testing/sysfs-bus-amba-devices-tgu | 51 ++
> .../devicetree/bindings/arm/qcom,tgu.yaml | 71 ++
> drivers/Makefile | 1 +
> drivers/hwtracing/Kconfig | 2 +
> drivers/hwtracing/qcom/Kconfig | 20 +
> drivers/hwtracing/qcom/Makefile | 3 +
> drivers/hwtracing/qcom/tgu.c | 704 ++++++++++++++++++
> drivers/hwtracing/qcom/tgu.h | 275 +++++++
> 8 files changed, 1127 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> create mode 100644 Documentation/devicetree/bindings/arm/qcom,tgu.yaml
> create mode 100644 drivers/hwtracing/qcom/Kconfig
> create mode 100644 drivers/hwtracing/qcom/Makefile
> create mode 100644 drivers/hwtracing/qcom/tgu.c
> create mode 100644 drivers/hwtracing/qcom/tgu.h
>
^ permalink raw reply
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