* Re: [PATCH 2/2] arm64: dts: ti: Add support for the phyCORE-AM67x
From: Andrew Davis @ 2026-06-25 20:37 UTC (permalink / raw)
To: Nathan Morrisson, nm, vigneshr, kristo, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, devicetree, linux-kernel, upstream
In-Reply-To: <20260625160214.4001298-2-nmorrisson@phytec.com>
On 6/25/26 11:02 AM, Nathan Morrisson wrote:
> Add support for the PHYTEC phyCORE-AM67x SoM [1] and the
> corresponding phyBOARD-Rigel carrier board [2]. The phyCORE-AM67x SoM
> uses the TI AM67x SoC and can come with different sizes and models of
> DDR, eMMC, and SPI NOR Flash.
>
> Supported features:
> * Audio playback and recording
> * CAN
> * Debug UART
> * eMMC
> * Ethernet
> * GPIO buttons
> * Heartbeat LED
> * I2C Current sensor
> * I2C EEPROM
> * I2C Light sensor
> * I2C RTC
> * Micro SD card
> * PCIe
> * SPI NOR flash
> * USB
>
> [1] https://www.phytec.com/product/phycore-am67x/
> [2] https://www.phytec.com/product/phyboard-am67x-development-kit/
>
> Signed-off-by: Nathan Morrisson <nmorrisson@phytec.com>
> ---
> arch/arm64/boot/dts/ti/Makefile | 1 +
> .../boot/dts/ti/k3-am67-phycore-som.dtsi | 328 ++++++++++++
> .../boot/dts/ti/k3-am6754-phyboard-rigel.dts | 502 ++++++++++++++++++
> 3 files changed, 831 insertions(+)
> create mode 100644 arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> create mode 100644 arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
>
> diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile
> index 371f9a043fe5..623ee2369132 100644
> --- a/arch/arm64/boot/dts/ti/Makefile
> +++ b/arch/arm64/boot/dts/ti/Makefile
> @@ -184,6 +184,7 @@ dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-pcie1-ep.dtbo
> dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-usb0-type-a.dtbo
>
> # Boards with J722s SoC
> +dtb-$(CONFIG_ARCH_K3) += k3-am6754-phyboard-rigel.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-am67a-beagley-ai.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm.dtb
> dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm-csi2-quad-rpi-cam-imx219.dtbo
> diff --git a/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> new file mode 100644
> index 000000000000..8a40f648098e
> --- /dev/null
> +++ b/arch/arm64/boot/dts/ti/k3-am67-phycore-som.dtsi
> @@ -0,0 +1,328 @@
> +// SPDX-License-Identifier: GPL-2.0-only OR MIT
> +/*
> + * Copyright (C) 2026 PHYTEC America LLC
> + * Author: Nathan Morrisson <nmorrisson@phytec.com>
> + */
> +
> +#include <dt-bindings/net/ti-dp83867.h>
> +#include <dt-bindings/leds/common.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +
> +/ {
> + compatible = "phytec,am67-phycore-som", "ti,j722s";
> + model = "PHYTEC phyCORE-AM67";
> +
> + aliases {
> + ethernet0 = &cpsw_port1;
> + gpio0 = &main_gpio0;
> + mmc0 = &sdhci0;
> + rtc0 = &i2c_som_rtc;
> + rtc1 = &wkup_rtc0;
> + spi0 = &ospi0;
> + };
> +
> + memory@80000000 {
> + /* 4G RAM */
> + reg = <0x00000000 0x80000000 0x00000000 0x80000000>,
> + <0x00000008 0x80000000 0x00000000 0x80000000>;
> + device_type = "memory";
> + bootph-all;
> + };
> +
> + reserved_memory: reserved-memory {
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + secure_tfa_ddr: tfa@9e780000 {
> + reg = <0x00 0x9e780000 0x00 0x80000>;
> + no-map;
> + };
> +
> + secure_ddr: optee@9e800000 {
> + reg = <0x00 0x9e800000 0x00 0x01800000>;
> + no-map;
> + };
> +
> + wkup_r5fss0_core0_dma_memory_region: memory@a0000000 {
> + compatible = "shared-dma-pool";
> + reg = <0x00 0xa0000000 0x00 0x100000>;
> + no-map;
> + };
> +
> + wkup_r5fss0_core0_memory_region: memory@a0100000 {
> + compatible = "shared-dma-pool";
> + reg = <0x00 0xa0100000 0x00 0xf00000>;
> + no-map;
> + };
> + };
> +
> + vcc_5v0_som: regulator-vcc-5v0-som {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_5V0_SOM";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + leds {
> + compatible = "gpio-leds";
> + pinctrl-names = "default";
> + pinctrl-0 = <&leds_pins_default>;
> +
> + led-0 {
> + color = <LED_COLOR_ID_GREEN>;
> + gpios = <&main_gpio0 13 GPIO_ACTIVE_HIGH>;
> + linux,default-trigger = "heartbeat";
> + function = LED_FUNCTION_HEARTBEAT;
> + };
> + };
> +};
> +
> +&main_pmx0 {
> + leds_pins_default: leds-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x034, PIN_OUTPUT, 7) /* (K22) OSPI0_CSN2.GPIO0_13 */
> + >;
> + };
> +
> + mdio_pins_default: mdio-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0160, PIN_OUTPUT, 0) /* (AC24) MDIO0_MDC */
> + J722S_IOPAD(0x015c, PIN_INPUT, 0) /* (AD25) MDIO0_MDIO */
> + >;
> + bootph-all;
> + };
> +
> + ospi0_pins_default: ospi0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x000, PIN_OUTPUT, 0) /* (L24) OSPI0_CLK */
> + J722S_IOPAD(0x02c, PIN_OUTPUT, 0) /* (K26) OSPI0_CSn0 */
> + J722S_IOPAD(0x00c, PIN_INPUT, 0) /* (K27) OSPI0_D0 */
> + J722S_IOPAD(0x010, PIN_INPUT, 0) /* (L27) OSPI0_D1 */
> + J722S_IOPAD(0x014, PIN_INPUT, 0) /* (L26) OSPI0_D2 */
> + J722S_IOPAD(0x018, PIN_INPUT, 0) /* (L25) OSPI0_D3 */
> + J722S_IOPAD(0x01c, PIN_INPUT, 0) /* (L21) OSPI0_D4 */
> + J722S_IOPAD(0x020, PIN_INPUT, 0) /* (M26) OSPI0_D5 */
> + J722S_IOPAD(0x024, PIN_INPUT, 0) /* (N27) OSPI0_D6 */
> + J722S_IOPAD(0x028, PIN_INPUT, 0) /* (M27) OSPI0_D7 */
> + J722S_IOPAD(0x008, PIN_INPUT, 0) /* (L22) OSPI0_DQS */
> + J722S_IOPAD(0x038, PIN_INPUT, 7) /* (J22) OSPI0_CSn3.GPIO0_14 */
> + >;
> + bootph-all;
> + };
> +
> + pmic_irq_pins_default: pmic-irq-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x030, PIN_INPUT, 7) /* (K23) OSPI0_CSN1.GPIO0_12 */
> + >;
> + };
> +
> + rgmii1_pins_default: rgmii1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x014c, PIN_INPUT, 0) /* (AC25) RGMII1_RD0 */
> + J722S_IOPAD(0x0150, PIN_INPUT, 0) /* (AD27) RGMII1_RD1 */
> + J722S_IOPAD(0x0154, PIN_INPUT, 0) /* (AE24) RGMII1_RD2 */
> + J722S_IOPAD(0x0158, PIN_INPUT, 0) /* (AE26) RGMII1_RD3 */
> + J722S_IOPAD(0x0148, PIN_INPUT, 0) /* (AE27) RGMII1_RXC */
> + J722S_IOPAD(0x0144, PIN_INPUT, 0) /* (AD23) RGMII1_RX_CTL */
> + J722S_IOPAD(0x0134, PIN_OUTPUT, 0) /* (AF27) RGMII1_TD0 */
> + J722S_IOPAD(0x0138, PIN_OUTPUT, 0) /* (AE23) RGMII1_TD1 */
> + J722S_IOPAD(0x013c, PIN_OUTPUT, 0) /* (AG25) RGMII1_TD2 */
> + J722S_IOPAD(0x0140, PIN_OUTPUT, 0) /* (AF24) RGMII1_TD3 */
> + J722S_IOPAD(0x0130, PIN_OUTPUT, 0) /* (AG26) RGMII1_TXC */
> + J722S_IOPAD(0x012c, PIN_OUTPUT, 0) /* (AF25) RGMII1_TX_CTL */
> + >;
> + bootph-all;
> + };
> +};
> +
> +&mcu_pmx0 {
> + wkup_i2c0_pins_default: wkup-i2c0-default-pins {
> + pinctrl-single,pins = <
> + J722S_MCU_IOPAD(0x04c, PIN_INPUT_PULLUP, 0) /* (B9) WKUP_I2C0_SCL */
> + J722S_MCU_IOPAD(0x050, PIN_INPUT_PULLUP, 0) /* (D11) WKUP_I2C0_SDA */
> + >;
> + bootph-all;
> + };
> +};
> +
> +&cpsw3g {
> + pinctrl-names = "default";
> + pinctrl-0 = <&rgmii1_pins_default>;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&cpsw3g_mdio {
> + pinctrl-names = "default";
> + pinctrl-0 = <&mdio_pins_default>;
> + status = "okay";
> +
> + cpsw3g_phy1: ethernet-phy@1 {
> + compatible = "ethernet-phy-ieee802.3-c22";
> + reg = <1>;
> + ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
> + tx-fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
> + ti,min-output-impedance;
> + };
> +};
> +
> +&cpsw_port1 {
> + phy-mode = "rgmii-id";
> + phy-handle = <&cpsw3g_phy1>;
> + status = "okay";
> +};
> +
> +&cpsw_port2 {
> + status = "disabled";
This should already be default disabled in the SoC dtsi,
no need to re-disable it here.
> +};
> +
> +&ospi0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&ospi0_pins_default>;
> + bootph-all;
> + status = "okay";
> +
> + serial_flash: flash@0 {
> + compatible = "jedec,spi-nor";
> + reg = <0x0>;
> + spi-tx-bus-width = <8>;
> + spi-rx-bus-width = <8>;
> + spi-max-frequency = <25000000>;
> + vcc-supply = <&vdd_1v8>;
> + cdns,tshsl-ns = <60>;
> + cdns,tsd2d-ns = <60>;
> + cdns,tchsh-ns = <60>;
> + cdns,tslch-ns = <60>;
> + cdns,read-delay = <0>;
> + };
> +};
> +
> +&sdhci0 {
> + non-removable;
> + bootph-all;
> + ti,driver-strength-ohm = <50>;
> + status = "okay";
> +};
> +
> +&wkup_i2c0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&wkup_i2c0_pins_default>;
> + clock-frequency = <400000>;
> + bootph-all;
> + status = "okay";
> +
> + pmic@30 {
> + compatible = "ti,tps65219";
> + reg = <0x30>;
> + buck1-supply = <&vcc_5v0_som>;
> + buck2-supply = <&vcc_5v0_som>;
> + buck3-supply = <&vcc_5v0_som>;
> + ldo1-supply = <&vdd_3v3>;
> + ldo2-supply = <&vdd_1v8>;
> + ldo3-supply = <&vdd_3v3>;
> + ldo4-supply = <&vdd_3v3>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&pmic_irq_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> + interrupt-controller;
> + #interrupt-cells = <1>;
> +
> + system-power-controller;
> + ti,power-button;
> +
> + regulators {
> + vdd_3v3: buck1 {
> + regulator-name = "VDD_3V3";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v8: buck2 {
> + regulator-name = "VDD_1V8";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_lpddr4: buck3 {
> + regulator-name = "VDD_LPDDR4";
> + regulator-min-microvolt = <1100000>;
> + regulator-max-microvolt = <1100000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vddshv_sdio: ldo1 {
> + regulator-name = "VDDSHV_SDIO";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-allow-bypass;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v2: ldo2 {
> + regulator-name = "VDD_1V2";
> + regulator-min-microvolt = <1200000>;
> + regulator-max-microvolt = <1200000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdda_1v8_phy: ldo3 {
> + regulator-name = "VDDA_1V8_PHY";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + vdd_1v8_pll: ldo4 {
> + regulator-name = "VDD_1V8_PLL";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> + };
> + };
> +
> + vdd_core: regulator-vdd-core@44 {
> + compatible = "ti,tps62873";
> + reg = <0x44>;
> + bootph-pre-ram;
> + regulator-name = "VDD_CORE";
> + regulator-min-microvolt = <850000>;
> + regulator-max-microvolt = <850000>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + eeprom@50 {
> + compatible = "atmel,24c32";
> + reg = <0x50>;
> + pagesize = <32>;
> + };
> +
> + som_eeprom_opt: eeprom@51 {
> + compatible = "atmel,24c32";
> + reg = <0x51>;
> + pagesize = <32>;
> + };
> +
> + i2c_som_rtc: rtc@52 {
> + compatible = "microcrystal,rv3028";
> + reg = <0x52>;
> + };
> +};
> +
> +#include "k3-j722s-ti-ipc-firmware.dtsi"
> diff --git a/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts b/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
> new file mode 100644
> index 000000000000..7853d4f5d3b9
> --- /dev/null
> +++ b/arch/arm64/boot/dts/ti/k3-am6754-phyboard-rigel.dts
> @@ -0,0 +1,502 @@
> +// SPDX-License-Identifier: GPL-2.0-only OR MIT
> +/*
> + * Copyright (C) 2026 PHYTEC America LLC
> + * Author: Nathan Morrisson <nmorrisson@phytec.com>
> + */
> +
> +/dts-v1/;
> +
> +#include <dt-bindings/input/input.h>
> +#include <dt-bindings/phy/phy.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +#include "k3-serdes.h"
> +#include "k3-j722s.dtsi"
This should be included by the som.dtsi, keeps the include chain sane.
Andrew
> +#include "k3-am67-phycore-som.dtsi"
> +
> +/ {
> + compatible = "phytec,am6754-phyboard-rigel",
> + "phytec,am67-phycore-som", "ti,j722s";
> + model = "PHYTEC phyBOARD-Rigel AM67";
> +
> + aliases {
> + gpio1 = &main_gpio1;
> + mmc1 = &sdhci1;
> + serial2 = &main_uart0;
> + usb0 = &usb0;
> + usb1 = &usb1;
> + };
> +
> + can_tc0: can-phy0 {
> + compatible = "ti,tcan1042";
> + #phy-cells = <0>;
> + max-bitrate = <8000000>;
> + standby-gpios = <&gpio_exp1 1 GPIO_ACTIVE_HIGH>;
> + };
> +
> + usb0_connector: connector {
> + compatible = "gpio-usb-b-connector", "usb-b-connector";
> + label = "USB-C";
> + data-role = "dual";
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_usbc_power_pins_default>;
> +
> + id-gpios = <&main_gpio1 15 GPIO_ACTIVE_HIGH>;
> +
> + port {
> + usb0_con: endpoint {
> + remote-endpoint = <&usb0_ep>;
> + };
> + };
> + };
> +
> + keys {
> + compatible = "gpio-keys";
> + autorepeat;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_keys_pins_default>;
> +
> + key-home {
> + label = "home";
> + linux,code = <KEY_HOME>;
> + gpios = <&main_gpio1 23 GPIO_ACTIVE_HIGH>;
> + };
> +
> + key-menu {
> + label = "menu";
> + linux,code = <KEY_MENU>;
> + gpios = <&gpio_exp1 4 GPIO_ACTIVE_HIGH>;
> + };
> + };
> +
> + pcie_refclk0: pcie-refclk0 {
> + compatible = "gpio-gate-clock";
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_pcie_usb_sel_pins_default>;
> + clocks = <&serdes_refclk>;
> + #clock-cells = <0>;
> + enable-gpios = <&main_gpio0 22 GPIO_ACTIVE_LOW>;
> + };
> +
> + vcc_1v8: regulator-vcc-1v8 {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_1V8";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_aud: regulator-vcc-3v3-aud {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_AUD";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_mmc: regulator-vcc-3v3-mmc {
> + /* TPS22963C OUTPUT */
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_MMC";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_3v3_sw: regulator-vcc-3v3-sw {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_3V3_SW";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + vcc_speaker: regulator-vcc-speaker {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC_SPEAKER";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + regulator-always-on;
> + regulator-boot-on;
> + };
> +
> + sound {
> + compatible = "simple-audio-card";
> + simple-audio-card,widgets =
> + "Microphone", "Mic Jack",
> + "Headphone", "Headphone Jack",
> + "Line", "Stereo Jack",
> + "Speaker", "L SPKR",
> + "Speaker", "R SPKR";
> + simple-audio-card,routing =
> + "MIC1RP", "Mic Jack",
> + "Mic Jack", "MICBIAS",
> + "Headphone Jack", "HPL",
> + "Headphone Jack", "HPR",
> + "MIC1LM", "Stereo Jack",
> + "MIC1LP", "Stereo Jack",
> + "SPL", "L SPKR",
> + "SPR", "R SPKR";
> + simple-audio-card,name = "phyBOARD-Rigel";
> + simple-audio-card,format = "dsp_b";
> + simple-audio-card,bitclock-master = <&sound_master>;
> + simple-audio-card,frame-master = <&sound_master>;
> + simple-audio-card,bitclock-inversion;
> +
> + simple-audio-card,cpu {
> + sound-dai = <&mcasp0>;
> + };
> +
> + sound_master: simple-audio-card,codec {
> + sound-dai = <&audio_codec>;
> + clocks = <&audio_refclk1>;
> + };
> + };
> +};
> +
> +&main_pmx0 {
> + audio_ext_refclk1_pins_default: audio-ext-refclk1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0a0, PIN_OUTPUT, 1) /* (N24) GPMC0_WPn.AUDIO_EXT_REFCLK1 */
> + >;
> + };
> +
> + gpio_exp0_int_pins_default: gpio-exp0-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0054, PIN_INPUT, 7) /* (T21) GPMC0_AD6.GPIO0_21 */
> + >;
> + };
> +
> + gpio_exp1_int_pins_default: gpio-exp1-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0244, PIN_INPUT, 7) /* (A24) MMC1_SDWP.GPIO1_49 */
> + >;
> + };
> +
> + gpio_exp2_int_pins_default: gpio-exp2-int-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0050, PIN_INPUT, 7) /* (T24) GPMC0_AD5.GPIO0_20 */
> + >;
> + };
> +
> + gpio_keys_pins_default: gpio-keys-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01d4, PIN_INPUT, 7) /* (B21) UART0_RTSn.GPIO1_23 */
> + >;
> + };
> +
> + main_i2c0_pins_default: main-i2c0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01e0, PIN_INPUT_PULLUP, 0) /* (D23) I2C0_SCL */
> + J722S_IOPAD(0x01e4, PIN_INPUT_PULLUP, 0) /* (B22) I2C0_SDA */
> + >;
> + bootph-all;
> + };
> +
> + main_i2c1_pins_default: main-i2c1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01e8, PIN_INPUT_PULLUP, 0) /* (C24) I2C1_SCL */
> + J722S_IOPAD(0x01ec, PIN_INPUT_PULLUP, 0) /* (A22) I2C1_SDA */
> + >;
> + bootph-all;
> + };
> +
> + main_mcan0_pins_default: main-mcan0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1dc, PIN_INPUT, 0) /* (C22) MCAN0_RX */
> + J722S_IOPAD(0x1d8, PIN_OUTPUT, 0) /* (D22) MCAN0_TX */
> + >;
> + };
> +
> + main_mcasp0_pins_default: main-mcasp0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1a8, PIN_INPUT, 0) /* (C26) MCASP0_AFSX */
> + J722S_IOPAD(0x1a4, PIN_INPUT, 0) /* (D25) MCASP0_ACLKX */
> + J722S_IOPAD(0x198, PIN_OUTPUT, 0) /* (A26) MCASP0_AXR2 */
> + J722S_IOPAD(0x194, PIN_INPUT, 0) /* (A25) MCASP0_AXR3 */
> + >;
> + };
> +
> + main_mcasp1_pins_default: main-mcasp1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x0090, PIN_INPUT, 2) /* (P27) GPMC0_BE0n_CLE.MCASP1_ACLKX */
> + J722S_IOPAD(0x0098, PIN_INPUT, 2) /* (V21) GPMC0_WAIT0.MCASP1_AFSX */
> + J722S_IOPAD(0x008c, PIN_OUTPUT, 2) /* (N23) GPMC0_WEn.MCASP1_AXR0 */
> + >;
> + };
> +
> + main_mmc1_pins_default: main-mmc1-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x023c, PIN_INPUT, 0) /* (H22) MMC1_CMD */
> + J722S_IOPAD(0x0234, PIN_INPUT, 0) /* (H24) MMC1_CLK */
> + J722S_IOPAD(0x0230, PIN_INPUT, 0) /* (H23) MMC1_DAT0 */
> + J722S_IOPAD(0x022c, PIN_INPUT, 0) /* (H20) MMC1_DAT1 */
> + J722S_IOPAD(0x0228, PIN_INPUT, 0) /* (J23) MMC1_DAT2 */
> + J722S_IOPAD(0x0224, PIN_INPUT, 0) /* (H25) MMC1_DAT3 */
> + J722S_IOPAD(0x0240, PIN_INPUT, 0) /* (B24) MMC1_SDCD */
> + >;
> + bootph-all;
> + };
> +
> + main_pcie_pins_default: main-pcie-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x07c, PIN_INPUT, 7) /* (T23) GPMC0_CLK.GPIO0_31 */
> + >;
> + };
> +
> + main_pcie_usb_sel_pins_default: main-pcie-usb-sel-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x058, PIN_INPUT, 7) /* (T22) GPMC0_AD7.GPIO0_22 */
> + >;
> + };
> +
> + main_uart0_pins_default: main-uart0-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x01c8, PIN_INPUT, 0) /* (F19) UART0_RXD */
> + J722S_IOPAD(0x01cc, PIN_OUTPUT, 0) /* (F20) UART0_TXD */
> + >;
> + bootph-all;
> + };
> +
> + main_usbc_power_pins_default: main-usbc-power-default-pins {
> + pinctrl-single,pins = <
> + J722S_IOPAD(0x1b4, PIN_INPUT, 7) /* (B20) SPI0_CS0.GPIO1_15 */
> + >;
> + };
> +};
> +
> +&audio_refclk1 {
> + assigned-clock-rates = <25000000>;
> +};
> +
> +&main_i2c0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_i2c0_pins_default>;
> + clock-frequency = <400000>;
> + status = "okay";
> +
> + veml6030: light-sensor@10 {
> + compatible = "vishay,veml6030";
> + reg = <0x10>;
> + vdd-supply = <&vcc_3v3_sw>;
> + };
> +};
> +
> +&main_i2c1 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_i2c1_pins_default>;
> + clock-frequency = <100000>;
> + status = "okay";
> +
> + audio_codec: audio-codec@18 {
> + compatible = "ti,tlv320aic3110";
> + reg = <0x18>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&audio_ext_refclk1_pins_default>;
> + #sound-dai-cells = <0>;
> + ai3xx-micbias-vg = <2>;
> + reset-gpios = <&gpio_exp1 7 GPIO_ACTIVE_LOW>;
> +
> + HPVDD-supply = <&vcc_3v3_aud>;
> + SPRVDD-supply = <&vcc_speaker>;
> + SPLVDD-supply = <&vcc_speaker>;
> + AVDD-supply = <&vcc_3v3_aud>;
> + IOVDD-supply = <&vcc_3v3_aud>;
> + DVDD-supply = <&vcc_1v8>;
> + };
> +
> + gpio_exp0: gpio@20 {
> + compatible = "nxp,pcf8574";
> + reg = <0x20>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp0_int_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "CSI3_STROBE", "CSI3_TRIGGER",
> + "CSI3_SHUTTER", "CSI3_OE",
> + "CSI2_STROBE", "CSI2_TRIGGER",
> + "CSI2_SHUTTER", "CSI2_OE";
> + };
> +
> + gpio_exp1: gpio@21 {
> + compatible = "nxp,pcf8574";
> + reg = <0x21>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp1_int_pins_default>;
> + interrupt-parent = <&main_gpio1>;
> + interrupts = <49 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "GPIO0_HDMI_RST", "GPIO1_CAN_nEN",
> + "GPIO2_LED", "GPIO3_MCU_CAN0_nEN",
> + "GPIO4_BUT2", "GPIO5_MCU_CAN1_nEN",
> + "GPIO6_AUDIO_GPIO", "GPIO7_AUDIO_USER_RESET";
> + };
> +
> + gpio_exp2: gpio@23 {
> + compatible = "nxp,pcf8574";
> + reg = <0x23>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio_exp2_int_pins_default>;
> + interrupt-parent = <&main_gpio0>;
> + interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
> + gpio-line-names = "CSI1_STROBE", "CSI1_TRIGGER",
> + "CSI1_SHUTTER", "CSI1_OE",
> + "CSI0_STROBE", "CSI0_TRIGGER",
> + "CSI0_SHUTTER", "CSI0_OE";
> + };
> +
> + current-sensor@40 {
> + compatible = "ti,ina233";
> + reg = <0x40>;
> + shunt-resistor = <18000>;
> + };
> +
> + eeprom@51 {
> + compatible = "atmel,24c02";
> + reg = <0x51>;
> + pagesize = <16>;
> + };
> +};
> +
> +&main_mcan0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcan0_pins_default>;
> + phys = <&can_tc0>;
> + status = "okay";
> +};
> +
> +&main_uart0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_uart0_pins_default>;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&mcasp0 {
> + #sound-dai-cells = <0>;
> + op-mode = <0>; /* MCASP_IIS_MODE */
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcasp0_pins_default>;
> + tdm-slots = <2>;
> + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
> + 0 0 1 2
> + 0 0 0 0
> + 0 0 0 0
> + 0 0 0 0
> + >;
> + status = "okay";
> +};
> +
> +&mcasp1 {
> + #sound-dai-cells = <0>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mcasp1_pins_default>;
> + op-mode = <0>; /* MCASP_IIS_MODE */
> + tdm-slots = <2>;
> + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
> + 1 0 2 0
> + 0 0 0 0
> + 0 0 0 0
> + 0 0 0 0
> + >;
> + status = "okay";
> +};
> +
> +&pcie0_rc {
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_pcie_pins_default>;
> + num-lanes = <1>;
> + phys = <&serdes1_pcie_link>;
> + phy-names = "pcie-phy";
> + reset-gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>;
> + status = "okay";
> +};
> +
> +&sdhci1 {
> + /* SD/MMC */
> + vmmc-supply = <&vcc_3v3_mmc>;
> + vqmmc-supply = <&vddshv_sdio>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&main_mmc1_pins_default>;
> + disable-wp;
> + no-1-8-v;
> + bootph-all;
> + status = "okay";
> +};
> +
> +&serdes_ln_ctrl {
> + idle-states = <J722S_SERDES0_LANE0_USB>,
> + <J722S_SERDES1_LANE0_PCIE0_LANE0>;
> +};
> +
> +&serdes0 {
> + status = "okay";
> +
> + serdes0_usb_link: phy@0 {
> + reg = <0>;
> + cdns,num-lanes = <1>;
> + #phy-cells = <0>;
> + cdns,phy-type = <PHY_TYPE_USB3>;
> + resets = <&serdes_wiz0 1>;
> + };
> +};
> +
> +&serdes_wiz0 {
> + status = "okay";
> +};
> +
> +&serdes1 {
> + status = "okay";
> +
> + serdes1_pcie_link: phy@0 {
> + reg = <0>;
> + cdns,num-lanes = <1>;
> + #phy-cells = <0>;
> + cdns,phy-type = <PHY_TYPE_PCIE>;
> + resets = <&serdes_wiz1 1>;
> + };
> +};
> +
> +&serdes_wiz1 {
> + clocks = <&k3_clks 280 0>, <&k3_clks 280 1>, <&pcie_refclk0>;
> + status = "okay";
> +};
> +
> +&usbss0 {
> + ti,vbus-divider;
> + status = "okay";
> +};
> +
> +&usb0 {
> + dr_mode = "otg";
> + usb-role-switch;
> + maximum-speed = "high-speed";
> +
> + port {
> + usb0_ep: endpoint {
> + remote-endpoint = <&usb0_con>;
> + };
> + };
> +};
> +
> +&usbss1 {
> + ti,vbus-divider;
> + status = "okay";
> +};
> +
> +&usb1 {
> + dr_mode = "host";
> + phys = <&serdes0_usb_link>;
> + phy-names = "cdns3,usb3-phy";
> + maximum-speed = "super-speed";
> +};
^ permalink raw reply
* Re: [PATCH v14 4/5] gpio: rpmsg: add generic rpmsg GPIO driver
From: Andrew Davis @ 2026-06-25 20:32 UTC (permalink / raw)
To: Shenwei Wang, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Mathieu Poirier, Frank Li, Sascha Hauer
Cc: Shuah Khan, linux-gpio, linux-doc, linux-kernel,
Pengutronix Kernel Team, Fabio Estevam, Shenwei Wang, Peng Fan,
devicetree, linux-remoteproc, imx, linux-arm-kernel, linux-imx,
Arnaud POULIQUEN, b-padhi, Andrew Lunn, Bartosz Golaszewski
In-Reply-To: <20260625155432.815185-5-shenwei.wang@oss.nxp.com>
On 6/25/26 10:54 AM, Shenwei Wang wrote:
> From: Shenwei Wang <shenwei.wang@nxp.com>
>
> On an AMP platform, the system may include multiple processors:
> - MCUs running an RTOS
> - An MPU running Linux
>
> These processors communicate via the RPMSG protocol.
> The driver implements the standard GPIO interface, allowing
> the Linux side to control GPIO controllers which reside in
> the remote processor via RPMSG protocol.
>
> Cc: Bartosz Golaszewski <brgl@bgdev.pl>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Shenwei Wang <shenwei.wang@nxp.com>
> ---
> drivers/gpio/Kconfig | 17 ++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-rpmsg.c | 568 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 586 insertions(+)
> create mode 100644 drivers/gpio/gpio-rpmsg.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 020e51e30317..4ad299fe3c6f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1917,6 +1917,23 @@ config GPIO_SODAVILLE
>
> endmenu
>
> +menu "RPMSG GPIO drivers"
> + depends on RPMSG
> +
> +config GPIO_RPMSG
> + tristate "Generic RPMSG GPIO support"
> + depends on OF && REMOTEPROC
> + select GPIOLIB_IRQCHIP
> + default REMOTEPROC
> + help
> + Say yes here to support the generic GPIO functions over the RPMSG
> + bus. Currently supported devices: i.MX7ULP, i.MX8ULP, i.MX8x, and
> + i.MX9x.
The support would depend on if the right firmware is loaded/running on the given
remote core. Also if you want to make this generic, then any vendor should be able
to make a firmware that implements this protocol and make use of this driver.
Suggest dropping this NXP specific device list.
> +
> + If unsure, say N.
> +
> +endmenu
> +
> menu "SPI GPIO expanders"
> depends on SPI_MASTER
>
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b267598b517d..ee75c0e65b8b 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -157,6 +157,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
> obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
> obj-$(CONFIG_GPIO_REG) += gpio-reg.o
> obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
> +obj-$(CONFIG_GPIO_RPMSG) += gpio-rpmsg.o
> obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
> obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
> obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
> diff --git a/drivers/gpio/gpio-rpmsg.c b/drivers/gpio/gpio-rpmsg.c
> new file mode 100644
> index 000000000000..332e2925a830
> --- /dev/null
> +++ b/drivers/gpio/gpio-rpmsg.c
> @@ -0,0 +1,568 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2026 NXP
> + *
> + * The driver exports a standard gpiochip interface to control
> + * the GPIO controllers via RPMSG on a remote processor.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/init.h>
> +#include <linux/irqdomain.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +#include <linux/rpmsg.h>
> +#include <linux/virtio_gpio.h>
> +
> +#define GPIOS_PER_PORT_DEFAULT 32
> +#define RPMSG_TIMEOUT 1000
> +
> +/* Additional commands beyond virtio-gpio */
> +#define VIRTIO_GPIO_MSG_SET_WAKEUP 0x0010
> +
> +/* GPIO Receive MSG Type */
> +#define GPIO_RPMSG_REPLY 1
> +#define GPIO_RPMSG_NOTIFY 2
> +
> +#define CHAN_NAME_PREFIX "rpmsg-io-"
> +#define GPIO_COMPAT_STR "rpmsg-gpio"
> +
> +struct rpmsg_gpio_response {
> + __u8 type;
> + union {
> + /* command reply */
> + struct {
> + __u8 status;
> + __u8 value;
> + };
> +
> + /* interrupt notification */
> + struct {
> + __u8 line;
> + __u8 trigger; /* rising/falling/high/low */
> + };
> + };
> +};
> +
> +struct rpmsg_gpio_line {
> + u8 irq_shutdown;
> + u8 irq_unmask;
> + u8 irq_mask;
> + u32 irq_wake_enable;
> + u32 irq_type;
> +};
> +
> +struct rpmsg_gpio_port {
> + struct gpio_chip gc;
> + struct rpmsg_device *rpdev;
> + struct virtio_gpio_request *send_msg;
> + struct rpmsg_gpio_response *recv_msg;
> + struct completion cmd_complete;
> + struct mutex lock;
> + u32 ngpios;
> + u32 idx;
> + struct rpmsg_gpio_line lines[GPIOS_PER_PORT_DEFAULT];
> +};
> +
> +static int rpmsg_gpio_send_message(struct rpmsg_gpio_port *port)
> +{
> + int ret;
> +
> + reinit_completion(&port->cmd_complete);
> +
> + ret = rpmsg_send(port->rpdev->ept, port->send_msg, sizeof(*port->send_msg));
> + if (ret) {
> + dev_err(&port->rpdev->dev, "rpmsg_send failed: cmd=%d ret=%d\n",
> + port->send_msg->type, ret);
> + return ret;
> + }
> +
> + ret = wait_for_completion_timeout(&port->cmd_complete,
> + msecs_to_jiffies(RPMSG_TIMEOUT));
> + if (ret == 0) {
> + dev_err(&port->rpdev->dev, "rpmsg_send timeout! cmd=%d\n",
> + port->send_msg->type);
> + return -ETIMEDOUT;
> + }
> +
> + if (unlikely(port->recv_msg->status != VIRTIO_GPIO_STATUS_OK)) {
> + dev_err(&port->rpdev->dev, "remote core replies an error: cmd=%d!\n",
> + port->send_msg->type);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct virtio_gpio_request *
> +rpmsg_gpio_msg_prepare(struct rpmsg_gpio_port *port, u16 line, u16 cmd, u32 val)
> +{
> + struct virtio_gpio_request *msg = port->send_msg;
> +
> + msg->type = cmd;
> + msg->gpio = line;
> + msg->value = val;
> +
> + return msg;
> +}
> +
> +static int rpmsg_gpio_get(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_GET_VALUE, 0);
> +
> + ret = rpmsg_gpio_send_message(port);
> + return ret ? ret : port->recv_msg->value;
> +}
> +
> +static int rpmsg_gpio_get_direction(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_GET_DIRECTION, 0);
> +
> + ret = rpmsg_gpio_send_message(port);
> + if (ret)
> + return ret;
> +
> + switch (port->recv_msg->value) {
> + case VIRTIO_GPIO_DIRECTION_IN:
> + return GPIO_LINE_DIRECTION_IN;
> + case VIRTIO_GPIO_DIRECTION_OUT:
> + return GPIO_LINE_DIRECTION_OUT;
> + default:
> + break;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int rpmsg_gpio_direction_input(struct gpio_chip *gc, unsigned int line)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_DIRECTION,
> + VIRTIO_GPIO_DIRECTION_IN);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int rpmsg_gpio_set(struct gpio_chip *gc, unsigned int line, int val)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_VALUE, val);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int rpmsg_gpio_direction_output(struct gpio_chip *gc, unsigned int line, int val)
> +{
> + struct rpmsg_gpio_port *port = gpiochip_get_data(gc);
> + int ret;
> +
> + guard(mutex)(&port->lock);
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_DIRECTION,
> + VIRTIO_GPIO_DIRECTION_OUT);
> +
> + ret = rpmsg_gpio_send_message(port);
> + if (ret)
> + return ret;
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_VALUE, val);
> +
> + return rpmsg_gpio_send_message(port);
> +}
> +
> +static int gpio_rpmsg_irq_set_type(struct irq_data *d, u32 type)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + switch (type) {
> + case IRQ_TYPE_EDGE_RISING:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_EDGE_FALLING:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_EDGE_BOTH:
> + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH;
> + irq_set_handler_locked(d, handle_simple_irq);
> + break;
> + case IRQ_TYPE_LEVEL_LOW:
> + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW;
> + irq_set_handler_locked(d, handle_level_irq);
> + break;
> + case IRQ_TYPE_LEVEL_HIGH:
> + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH;
> + irq_set_handler_locked(d, handle_level_irq);
> + break;
> + default:
> + dev_err(&port->rpdev->dev, "unsupported irq type: %u\n", type);
> + return -EINVAL;
> + }
> +
> + port->lines[line].irq_type = type;
> +
> + return 0;
> +}
> +
> +static int gpio_rpmsg_irq_set_wake(struct irq_data *d, u32 enable)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_wake_enable = enable;
> +
> + return 0;
> +}
> +
> +/*
> + * This unmask/mask function is invoked in two situations:
> + * - when an interrupt is being set up, and
> + * - after an interrupt has occurred.
> + *
> + * The GPIO driver does not access hardware registers directly.
> + * Instead, it caches all relevant information locally, and then sends
> + * the accumulated state to the remote system at this stage.
> + */
> +static void gpio_rpmsg_unmask_irq(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_unmask = 1;
> +}
> +
> +static void gpio_rpmsg_mask_irq(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + /*
> + * When an interrupt occurs, the remote system masks the interrupt
> + * and then sends a notification to Linux. After Linux processes
> + * that notification, it sends an RPMsg command back to the remote
> + * system to unmask the interrupt again.
> + */
> + port->lines[line].irq_mask = 1;
> +}
> +
> +static void gpio_rpmsg_irq_shutdown(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + port->lines[line].irq_shutdown = 1;
> +}
> +
> +static void gpio_rpmsg_irq_bus_lock(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> +
> + mutex_lock(&port->lock);
> +}
> +
> +static void gpio_rpmsg_irq_bus_sync_unlock(struct irq_data *d)
> +{
> + struct rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
> + u32 line = d->hwirq;
> +
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_SET_WAKEUP,
> + port->lines[line].irq_wake_enable);
> + rpmsg_gpio_send_message(port);
> +
> + /*
> + * For mask irq, do nothing here.
> + * The remote system will mask interrupt after an interrupt occurs,
> + * and then send a notification to Linux system. After Linux system
> + * handles the notification, it sends an rpmsg back to the remote
> + * system to unmask this interrupt again.
> + */
> + if (port->lines[line].irq_mask && !port->lines[line].irq_unmask) {
> + port->lines[line].irq_mask = 0;
> + mutex_unlock(&port->lock);
> + return;
> + }
> +
> + if (port->lines[line].irq_shutdown) {
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_IRQ_TYPE,
> + VIRTIO_GPIO_IRQ_TYPE_NONE);
> + port->lines[line].irq_shutdown = 0;
> + } else {
> + rpmsg_gpio_msg_prepare(port, line, VIRTIO_GPIO_MSG_IRQ_TYPE,
> + port->lines[line].irq_type);
> +
> + if (port->lines[line].irq_unmask)
> + port->lines[line].irq_unmask = 0;
> + }
> +
> + rpmsg_gpio_send_message(port);
> + mutex_unlock(&port->lock);
> +}
> +
> +static const struct irq_chip gpio_rpmsg_irq_chip = {
> + .irq_mask = gpio_rpmsg_mask_irq,
> + .irq_unmask = gpio_rpmsg_unmask_irq,
> + .irq_set_wake = gpio_rpmsg_irq_set_wake,
> + .irq_set_type = gpio_rpmsg_irq_set_type,
> + .irq_shutdown = gpio_rpmsg_irq_shutdown,
> + .irq_bus_lock = gpio_rpmsg_irq_bus_lock,
> + .irq_bus_sync_unlock = gpio_rpmsg_irq_bus_sync_unlock,
> + .flags = IRQCHIP_IMMUTABLE,
> +};
> +
> +static int rpmsg_gpiochip_register(struct rpmsg_device *rpdev,
> + struct device_node *np, const char *name)
> +{
> + struct rpmsg_gpio_port *port;
> + struct gpio_irq_chip *girq;
> + struct gpio_chip *gc;
> + int ret;
> +
> + port = devm_kzalloc(&rpdev->dev, sizeof(*port), GFP_KERNEL);
> + if (!port)
> + return -ENOMEM;
> +
> + ret = of_property_read_u32(np, "reg", &port->idx);
> + if (ret)
> + return ret;
> +
> + ret = devm_mutex_init(&rpdev->dev, &port->lock);
> + if (ret)
> + return ret;
> +
> + ret = of_property_read_u32(np, "ngpios", &port->ngpios);
> + if (ret || port->ngpios > GPIOS_PER_PORT_DEFAULT)
> + port->ngpios = GPIOS_PER_PORT_DEFAULT;
> +
> + port->send_msg = devm_kzalloc(&rpdev->dev,
> + sizeof(*port->send_msg),
> + GFP_KERNEL);
> +
> + port->recv_msg = devm_kzalloc(&rpdev->dev,
> + sizeof(*port->recv_msg),
> + GFP_KERNEL);
> + if (!port->send_msg || !port->recv_msg)
> + return -ENOMEM;
> +
> + init_completion(&port->cmd_complete);
> + port->rpdev = rpdev;
> +
> + gc = &port->gc;
> + gc->owner = THIS_MODULE;
> + gc->parent = &rpdev->dev;
> + gc->fwnode = of_fwnode_handle(np);
> + gc->ngpio = port->ngpios;
> + gc->base = -1;
> + gc->label = devm_kasprintf(&rpdev->dev, GFP_KERNEL, "%s-gpio%d",
> + name, port->idx);
> +
> + gc->direction_input = rpmsg_gpio_direction_input;
> + gc->direction_output = rpmsg_gpio_direction_output;
> + gc->get_direction = rpmsg_gpio_get_direction;
> + gc->get = rpmsg_gpio_get;
> + gc->set = rpmsg_gpio_set;
> +
> + girq = &gc->irq;
> + gpio_irq_chip_set_chip(girq, &gpio_rpmsg_irq_chip);
> + girq->parent_handler = NULL;
> + girq->num_parents = 0;
> + girq->parents = NULL;
> + girq->chip->name = devm_kstrdup(&rpdev->dev, gc->label, GFP_KERNEL);
> +
> + dev_set_drvdata(&rpdev->dev, port);
> +
> + return devm_gpiochip_add_data(&rpdev->dev, gc, port);
> +}
> +
> +static const char *rpmsg_get_rproc_node_name(struct rpmsg_device *rpdev)
> +{
> + const char *name = NULL;
> + struct device_node *np;
> + struct rproc *rproc;
> +
> + rproc = rproc_get_by_child(&rpdev->dev);
> + if (!rproc)
> + return NULL;
> +
> + np = of_node_get(rproc->dev.of_node);
> + if (!np && rproc->dev.parent)
> + np = of_node_get(rproc->dev.parent->of_node);
> +
> + if (np) {
> + name = devm_kstrdup(&rpdev->dev, np->name, GFP_KERNEL);
> + of_node_put(np);
> + }
> +
> + return name;
> +}
> +
> +static struct device_node *
> +rpmsg_find_child_by_compat_reg(struct device_node *parent, const char *compat, u32 idx)
> +{
> + struct device_node *child;
> + u32 reg;
> +
> + for_each_available_child_of_node(parent, child) {
> + if (!of_device_is_compatible(child, compat))
> + continue;
> +
> + if (of_property_read_u32(child, "reg", ®))
> + continue;
> +
> + if (reg == idx)
> + return child;
> + }
> +
> + return NULL;
> +}
> +
> +static struct device_node *
> +rpmsg_get_channel_ofnode(struct rpmsg_device *rpdev, const char *compat, u32 idx)
> +{
> + struct device_node *np_chan = NULL, *np;
> + struct rproc *rproc;
> +
> + rproc = rproc_get_by_child(&rpdev->dev);
> + if (!rproc)
> + return NULL;
> +
> + np = of_node_get(rproc->dev.of_node);
> + if (!np && rproc->dev.parent)
> + np = of_node_get(rproc->dev.parent->of_node);
> +
> + if (np)
> + np_chan = rpmsg_find_child_by_compat_reg(np, compat, idx);
> +
> + return np_chan;
> +}
> +
> +static int rpmsg_get_gpio_index(const char *name, const char *prefix)
> +{
> + const char *p;
> + int base = 10;
> + int val;
> +
> + if (!name)
> + return -EINVAL;
> +
> + /* Ensure correct prefix */
> + if (!str_has_prefix(name, prefix))
> + return -EINVAL;
> +
> + /* Find last '-' */
> + p = strrchr(name, '-');
> +
> + if (!p || *(p + 1) == '\0')
> + return -EINVAL;
> +
> + if (p[1] == '0' && (p[2] == 'x' || p[2] == 'X'))
> + base = 16;
> +
> + if (kstrtoint(p + 1, base, &val))
> + return -EINVAL;
> +
> + return val;
> +}
> +
> +static int rpmsg_gpio_channel_callback(struct rpmsg_device *rpdev, void *data,
> + int len, void *priv, u32 src)
> +{
> + struct rpmsg_gpio_response *msg = data;
> + struct rpmsg_gpio_port *port = NULL;
> +
> + port = dev_get_drvdata(&rpdev->dev);
> +
> + if (!port) {
> + dev_err(&rpdev->dev, "port is null\n");
> + return -EINVAL;
> + }
> +
> + if (msg->type == GPIO_RPMSG_REPLY) {
> + *port->recv_msg = *msg;
> + complete(&port->cmd_complete);
> + } else if (msg->type == GPIO_RPMSG_NOTIFY) {
> + generic_handle_domain_irq_safe(port->gc.irq.domain, msg->line);
> + } else {
> + dev_err(&rpdev->dev, "wrong message type (0x%x)\n", msg->type);
> + }
> +
> + return 0;
> +}
> +
> +static int rpmsg_gpio_channel_probe(struct rpmsg_device *rpdev)
> +{
> + struct device *dev = &rpdev->dev;
> + struct device_node *np;
> + const char *rproc_name;
> + int idx;
> +
> + idx = rpmsg_get_gpio_index(rpdev->id.name, CHAN_NAME_PREFIX);
> + if (idx < 0)
> + return -EINVAL;
> +
> + if (!dev->of_node) {
> + np = rpmsg_get_channel_ofnode(rpdev, GPIO_COMPAT_STR, idx);
> + if (!np)
> + return -ENODEV;
This seems to imply that DT nodes are required. RPMSG is a discoverable
bus with a nameservice that can bind/probe new devices. While then optionally
binding to a DT node when available so sub-devices can be described in DT is
fine, I don't see why it should be required.
> +
> + dev->of_node = np;
> + set_primary_fwnode(dev, of_fwnode_handle(np));
> + return -EPROBE_DEFER;
> + }
> +
> + rproc_name = rpmsg_get_rproc_node_name(rpdev);
> +
> + return rpmsg_gpiochip_register(rpdev, dev->of_node, rproc_name);
> +}
> +
> +static const struct of_device_id rpmsg_gpio_dt_ids[] = {
> + { .compatible = GPIO_COMPAT_STR },
> + { /* sentinel */ }
> +};
> +
> +static struct rpmsg_device_id rpmsg_gpio_channel_id_table[] = {
> + { .name = CHAN_NAME_PREFIX },
> + { },
> +};
> +MODULE_DEVICE_TABLE(rpmsg, rpmsg_gpio_channel_id_table);
> +
> +static struct rpmsg_driver rpmsg_gpio_channel_client = {
> + .callback = rpmsg_gpio_channel_callback,
> + .id_table = rpmsg_gpio_channel_id_table,
> + .probe = rpmsg_gpio_channel_probe,
> + .drv = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = rpmsg_gpio_dt_ids,
Does this line actually do anything anymore? Maybe it did when
this was a platform_driver, but this is a rpmsg_driver and
will probe though .id_table matches.
Andrew
> + },
> +};
> +module_rpmsg_driver(rpmsg_gpio_channel_client);
> +
> +MODULE_AUTHOR("Shenwei Wang <shenwei.wang@nxp.com>");
> +MODULE_DESCRIPTION("generic rpmsg gpio driver");
> +MODULE_LICENSE("GPL");
^ permalink raw reply
* [PATCH v7 16/27] phy: rockchip: usbdp: Cleanup DP lane selection function
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
logic.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 28 +++++++---------------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 1bae8172d1fe..71ee2f4faf0b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -553,30 +553,16 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
- u32 value = 0;
-
- switch (udphy->dp_lanes) {
- case 4:
- value |= 3 << udphy->dp_lane_sel[3] * 2;
- value |= 2 << udphy->dp_lane_sel[2] * 2;
- fallthrough;
-
- case 2:
- value |= 1 << udphy->dp_lane_sel[1] * 2;
- fallthrough;
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
+ int i;
- case 1:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
- break;
+ for (i = 0; i < udphy->dp_lanes; i++)
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
- default:
- break;
- }
+ value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
+ value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
- regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
- ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
- FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
- FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
+ regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
--
2.53.0
^ permalink raw reply related
* [PATCH v7 26/27] phy: rockchip: usbdp: Add some extra debug messages
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
It's useful to log PHY reinit to ease debugging issues around
USB-C hotplugging.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index fc788f2bf5fb..8c5d6b8595e2 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -24,6 +24,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/string_choices.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@@ -494,6 +495,8 @@ static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
+ dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
+
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
@@ -787,6 +790,10 @@ static int rk_udphy_init(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
+ dev_dbg(udphy->dev, "(re-)init PHY with USB=%s and DP=%s\n",
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_USB),
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_DP));
+
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@@ -857,6 +864,8 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
+ dev_dbg(udphy->dev, "enable PHY\n");
+
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
@@ -875,6 +884,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
static void rk_udphy_disable(struct rk_udphy *udphy)
{
+ dev_dbg(udphy->dev, "disable PHY\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
--
2.53.0
^ permalink raw reply related
* RE: [PATCH v2 4/6] Drivers: hv: Mark shared memory as decrypted for CCA Realms
From: Michael Kelley @ 2026-06-25 18:58 UTC (permalink / raw)
To: Kameron Carr, kys@microsoft.com, haiyangz@microsoft.com,
wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com
Cc: catalin.marinas@arm.com, will@kernel.org, mark.rutland@arm.com,
lpieralisi@kernel.org, sudeep.holla@kernel.org, arnd@arndb.de,
thuth@redhat.com, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
Michael Kelley
In-Reply-To: <20260625173500.1995481-5-kameroncarr@linux.microsoft.com>
From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Thursday, June 25, 2026 10:35 AM
>
> In hv_common_cpu_init(), the per-CPU hypercall input/output pages need
> to be marked as decrypted (shared) for confidential VM isolation types.
> This is already done for SNP and TDX isolation; extend the same handling
> to Arm CCA Realm guests so that the host hypervisor can access the
> shared hypercall buffers.
>
> We need to round up the memory allocated for the input/output pages to
> the nearest PAGE_SIZE, since set_memory_decrypted() requires the size to
> be a multiple of PAGE_SIZE. This only has an effect on ARM VMs that are
> using PAGE_SIZE larger than 4K.
I think this change resulted from a Sashiko comment. My understanding is
that the ARM CCA architecture only supports CCA guests with 4 KiB page
size. Is that still the case, or has that restriction been lifted in a later version
of the architecture? I'm in favor of handling the larger page sizes, if only for
future proofing. But I wondered whether your intent is to always support
> 4 KiB page sizes even if CCA doesn't support them now. Another way to
put it: In reviewing code, should I flag issues related to page sizes > 4 KiB?
>
> is_realm_world() is only declared in arch/arm64/include/asm/rsi.h, so
> using it directly in the arch-neutral drivers/hv/hv_common.c would
> break the x86 build. Introduce a Hyper-V-specific helper following the
> established hv_isolation_type_snp() / hv_isolation_type_tdx() pattern.
>
> On architectures other than arm64 the weak default keeps the existing
> behaviour.
>
> Signed-off-by: Kameron Carr <kameroncarr@linux.microsoft.com>
> ---
> arch/arm64/hyperv/mshyperv.c | 5 +++++
> drivers/hv/hv_common.c | 17 +++++++++++++----
> include/asm-generic/mshyperv.h | 1 +
> 3 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
> index 7d536d7fb557e..8e8148b723d9c 100644
> --- a/arch/arm64/hyperv/mshyperv.c
> +++ b/arch/arm64/hyperv/mshyperv.c
> @@ -164,3 +164,8 @@ bool hv_is_hyperv_initialized(void)
> return hyperv_initialized;
> }
> EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
> +
> +bool hv_isolation_type_cca(void)
> +{
> + return is_realm_world();
> +}
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index 6b67ac6167891..17048a0a18729 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -476,6 +476,7 @@ int hv_common_cpu_init(unsigned int cpu)
> u64 msr_vp_index;
> gfp_t flags;
> const int pgcount = hv_output_page_exists() ? 2 : 1;
> + const size_t alloc_size = ALIGN((size_t)pgcount * HV_HYP_PAGE_SIZE, PAGE_SIZE);
> void *mem;
> int ret = 0;
>
> @@ -489,7 +490,7 @@ int hv_common_cpu_init(unsigned int cpu)
> * online and then taken offline
> */
> if (!*inputarg) {
> - mem = kmalloc_array(pgcount, HV_HYP_PAGE_SIZE, flags);
> + mem = kmalloc(alloc_size, flags);
> if (!mem)
> return -ENOMEM;
>
> @@ -499,14 +500,16 @@ int hv_common_cpu_init(unsigned int cpu)
> }
>
> if (!ms_hyperv.paravisor_present &&
> - (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
> - ret = set_memory_decrypted((unsigned long)mem, pgcount);
> + (hv_isolation_type_snp() || hv_isolation_type_tdx() ||
> + hv_isolation_type_cca())) {
> + ret = set_memory_decrypted((unsigned long)kasan_reset_tag(mem),
> + alloc_size >> PAGE_SHIFT);
I don't know enough about KASAN or the tag situation on ARM64
to comment on this change. But this same sequence of allocating
memory and then decrypting it occurs in other places in Hyper-V
code. It seems like those places would also need the same
kasan_reset_tag() call.
Michael
> if (ret) {
> /* It may be unsafe to free 'mem' */
> return ret;
> }
>
> - memset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE);
> + memset(mem, 0x00, alloc_size);
> }
>
> /*
> @@ -666,6 +669,12 @@ bool __weak hv_isolation_type_tdx(void)
> }
> EXPORT_SYMBOL_GPL(hv_isolation_type_tdx);
>
> +bool __weak hv_isolation_type_cca(void)
> +{
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(hv_isolation_type_cca);
> +
> void __weak hv_setup_vmbus_handler(void (*handler)(void))
> {
> }
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index bf601d67cecb9..1fa79abce743c 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -79,6 +79,7 @@ u64 hv_do_fast_hypercall16(u16 control, u64 input1, u64 input2);
>
> bool hv_isolation_type_snp(void);
> bool hv_isolation_type_tdx(void);
> +bool hv_isolation_type_cca(void);
>
> /*
> * On architectures where Hyper-V doesn't support AEOI (e.g., ARM64),
> --
> 2.45.4
>
^ permalink raw reply
* RE: [PATCH v2 3/6] arm64: hyperv: Add per-CPU RSI host call infrastructure for CCA Realms
From: Michael Kelley @ 2026-06-25 18:58 UTC (permalink / raw)
To: Kameron Carr, kys@microsoft.com, haiyangz@microsoft.com,
wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com
Cc: catalin.marinas@arm.com, will@kernel.org, mark.rutland@arm.com,
lpieralisi@kernel.org, sudeep.holla@kernel.org, arnd@arndb.de,
thuth@redhat.com, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
Michael Kelley
In-Reply-To: <20260625173500.1995481-4-kameroncarr@linux.microsoft.com>
From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Thursday, June 25, 2026 10:35 AM
> To: kys@microsoft.com; haiyangz@microsoft.com; wei.liu@kernel.org;
> decui@microsoft.com; longli@microsoft.com
> Cc: catalin.marinas@arm.com; will@kernel.org; mark.rutland@arm.com;
> lpieralisi@kernel.org; sudeep.holla@kernel.org; arnd@arndb.de; thuth@redhat.com; linux-
> hyperv@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org; linux-arch@vger.kernel.org; mhklinux@outlook.com
> Subject: [PATCH v2 3/6] arm64: hyperv: Add per-CPU RSI host call infrastructure for CCA
> Realms
>
> Arm CCA Realms cannot issue Hyper-V hypercalls via HVC; the guest must
> route them through the RSI_HOST_CALL interface, which takes the IPA of a
> per-CPU rsi_host_call structure as its argument.
>
> Add hv_hostcall_array as a per-CPU struct array and allocate it during
> hyperv_init(). The allocation is gated on is_realm_world() so non-Realm
> arm64 Hyper-V guests pay no memory cost.
>
> Signed-off-by: Kameron Carr <kameroncarr@linux.microsoft.com>
> ---
> arch/arm64/hyperv/mshyperv.c | 32 ++++++++++++++++++++++++++++++-
> arch/arm64/include/asm/mshyperv.h | 4 ++++
> 2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
> index 4fdc26ade1d74..7d536d7fb557e 100644
> --- a/arch/arm64/hyperv/mshyperv.c
> +++ b/arch/arm64/hyperv/mshyperv.c
> @@ -15,10 +15,15 @@
> #include <linux/errno.h>
> #include <linux/version.h>
> #include <linux/cpuhotplug.h>
> +#include <linux/slab.h>
> #include <asm/mshyperv.h>
> +#include <asm/rsi.h>
>
> static bool hyperv_initialized;
>
> +struct rsi_host_call *hv_hostcall_array;
> +EXPORT_SYMBOL_GPL(hv_hostcall_array);
> +
> int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
> {
> hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
> @@ -60,6 +65,12 @@ static bool __init hyperv_detect_via_acpi(void)
>
> #endif
>
> +static void hv_hostcall_free(void)
> +{
> + kfree(hv_hostcall_array);
> + hv_hostcall_array = NULL;
> +}
> +
> static bool __init hyperv_detect_via_smccc(void)
> {
> uuid_t hyperv_uuid = UUID_INIT(
> @@ -85,6 +96,20 @@ static int __init hyperv_init(void)
> if (!hyperv_detect_via_acpi() && !hyperv_detect_via_smccc())
> return 0;
>
> + /*
> + * The RSI host-call buffers are only ever used when
> + * is_realm_world() is true. Skip the allocation on non-Realm
> + * guests. A single contiguous array of nr_cpu_ids entries is
> + * allocated; each CPU indexes into it by its processor ID.
> + */
> + if (is_realm_world()) {
> + hv_hostcall_array = kcalloc(nr_cpu_ids,
> + sizeof(struct rsi_host_call),
> + GFP_KERNEL);
> + if (!hv_hostcall_array)
> + return -ENOMEM;
> + }
> +
> /* Setup the guest ID */
> guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
> hv_set_vpreg(HV_REGISTER_GUEST_OS_ID, guest_id);
> @@ -106,12 +131,13 @@ static int __init hyperv_init(void)
>
> ret = hv_common_init();
> if (ret)
> - return ret;
> + goto free_hostcall_mem;
>
> ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online",
> hv_common_cpu_init, hv_common_cpu_die);
> if (ret < 0) {
> hv_common_free();
> + hv_hostcall_free();
> return ret;
Let me suggest a small additional simplification. For this error
path, call hv_common_free() as you have now, but then do
"goto free_hostcall_mem". At the free_hostcall_mem label, do
kfree(hv_hostcall_array);
hv_hostcall_array = NULL;
directly inline, and eliminate the hv_hostcall_free() helper
function. Saves about 5 lines of code overall and I think is a
bit simpler.
> }
>
> @@ -125,6 +151,10 @@ static int __init hyperv_init(void)
>
> hyperv_initialized = true;
> return 0;
> +
> +free_hostcall_mem:
> + hv_hostcall_free();
> + return ret;
> }
>
> early_initcall(hyperv_init);
> diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
> index b721d3134ab66..c207a3f79b99b 100644
> --- a/arch/arm64/include/asm/mshyperv.h
> +++ b/arch/arm64/include/asm/mshyperv.h
> @@ -63,4 +63,8 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
>
> #include <asm-generic/mshyperv.h>
>
> +/* Per-CPU-indexed RSI host call structures for CCA Realms */
> +struct rsi_host_call;
> +extern struct rsi_host_call *hv_hostcall_array;
> +
The intent is that the #include of asm-generic/mshyperv.h should be
last in the arch-specific version of mshyperv.h. If there's a need to go
after the #include, that's a red flag to check if some restructuring of
the definitions would be appropriate.
Unless I'm missing something, I think these new definitions can go
above the #include.
Michael
> #endif
> --
> 2.45.4
>
^ permalink raw reply
* [PATCH] ARM: remove unreachable invalid range check in kasan_init()
From: Sang-Heon Jeon @ 2026-06-25 18:40 UTC (permalink / raw)
To: Andrey Ryabinin, Russell King
Cc: Sang-Heon Jeon, Alexander Potapenko, Andrey Konovalov,
Dmitry Vyukov, kasan-dev, linux-arm-kernel, Vincenzo Frascino
kasan_init() maps each memblock region with for_each_mem_range(), which
guarantees pa_start < pa_end. Then it skips any region with
pa_start >= arm_lowmem_limit, so pa_start < arm_lowmem_limit is guaranteed
as well.
When pa_end <= arm_lowmem_limit, pa_start < pa_end means start < end, so
the start >= end check is unreachable.
When pa_end > arm_lowmem_limit, end is clamped to __va(arm_lowmem_limit),
and pa_start < arm_lowmem_limit means start < end, so the check is
unreachable as well.
No functional change.
Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
---
arch/arm/mm/kasan_init.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c
index c6625e808bf8..1f7c74c5df9e 100644
--- a/arch/arm/mm/kasan_init.c
+++ b/arch/arm/mm/kasan_init.c
@@ -262,12 +262,6 @@ void __init kasan_init(void)
&pa_start, &pa_end, &arm_lowmem_limit);
end = __va(arm_lowmem_limit);
}
- if (start >= end) {
- pr_info("Skipping invalid memory block %pa-%pa (virtual %p-%p)\n",
- &pa_start, &pa_end, start, end);
- continue;
- }
-
create_mapping(start, end);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 1/2] arm64: errata: Workaround NVIDIA Olympus device store/load ordering
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
In-Reply-To: <20260625182425.3194066-1-sdonthineni@nvidia.com>
On systems with NVIDIA Olympus cores, a Device-nGnR* load can be
observed by a peripheral before an older, non-overlapping Device-nGnR*
store to the same peripheral. This breaks the program-order guarantee
that software expects for Device-nGnR* accesses and can leave a
peripheral in an incorrect state, as a load is observed before an
earlier store takes effect.
The erratum can occur only when all of the following apply:
- A PE executes a Device-nGnR* store followed by a younger
Device-nGnR* load.
- The store is not a store-release.
- The accesses target the same peripheral and do not overlap in bytes.
- There is at most one intervening Device-nGnR* store in program
order, and there are no intervening Device-nGnR* loads.
- There is no DSB, and no DMB that orders loads, between the store and
the load.
- Specific micro-architectural and timing conditions occur.
Promote the raw MMIO store helpers (__raw_writeb/w/l/q) from plain str*
to stlr* (Store-Release) on affected CPUs, which removes the "store is
not a store-release" condition for every device write the kernel issues.
Because writel() and writel_relaxed() are both built on __raw_writel()
in asm-generic/io.h, patching the raw variants covers both the
non-relaxed and relaxed APIs without touching the higher layers. Note
that writel()'s own barrier sits before the store, so it does not order
the store against a subsequent readl(); the store-release promotion is
what provides that ordering.
Like ARM64_ERRATUM_832075 on the load side, the change is gated on a new
ARM64_WORKAROUND_DEVICE_STORE_RELEASE capability and only activated on
parts that match MIDR_NVIDIA_OLYMPUS, so unaffected CPUs continue to use
plain str* instructions.
Note: stlr* only supports base-register addressing, so the raw MMIO
write helpers use a base-register str*/stlr* alternative sequence. This
gives up the offset-addressed str* code generation introduced by commit
d044d6ba6f02 ("arm64: io: permit offset addressing"). A static-branch
implementation would add extra control flow without preserving the
desired offset-addressed code generation in practice, so use a direct
base-register str*/stlr* alternative instead.
For the write-combining copy helpers (__iowrite{32,64}_copy()), the
contiguous str* groups are kept, because replacing those stores would
defeat the write-combining behaviour used to improve store performance.
Rather than rely on the relaxed, no-ordering contract of these helpers -
which would leave affected CPUs behaving differently from every other
arm64 system and exposed to any future driver that depends on ordering
across such copies - the DGH hint emitted once after each copy is
promoted to dmb osh on affected CPUs. That orders the grouped stores
against subsequent loads without placing a barrier in the copy loop,
while unaffected CPUs keep the existing DGH hint. The single-element
case of __const_memcpy_toio_aligned{32,64}() likewise uses a plain str*
(instead of __raw_write*()) so it shares that str* group + DGH path
rather than taking a per-store store-release.
Co-developed-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
Link: https://lore.kernel.org/all/ajVZBJgKn-5sxHD6@willie-the-truck/
---
Documentation/arch/arm64/silicon-errata.rst | 2 ++
arch/arm64/Kconfig | 25 +++++++++++++++++
arch/arm64/include/asm/barrier.h | 4 ++-
arch/arm64/include/asm/io.h | 31 +++++++++++++--------
arch/arm64/kernel/cpu_errata.c | 8 ++++++
arch/arm64/tools/cpucaps | 1 +
6 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index ad04d1cdc0f0..c4137f89acef 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -298,6 +298,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM |
+----------------+-----------------+-----------------+-----------------------------+
+| NVIDIA | Olympus core | T410-OLY-1027 | NVIDIA_OLYMPUS_1027_ERRATUM |
++----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Olympus core | T410-OLY-1029 | ARM64_ERRATUM_4118414 |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 10c69474f276..da4e66b19209 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -564,6 +564,31 @@ config ARM64_ERRATUM_832075
If unsure, say Y.
+config NVIDIA_OLYMPUS_1027_ERRATUM
+ bool "NVIDIA Olympus: device store/load ordering erratum"
+ default y
+ help
+ This option adds an alternative code sequence to work around an
+ NVIDIA Olympus core erratum where a Device-nGnR* store can be
+ observed by a peripheral after a younger Device-nGnR* load to the
+ same peripheral. This breaks the program order that drivers rely
+ on for MMIO and can leave a device in an incorrect state.
+
+ The workaround promotes the raw MMIO store helpers
+ (__raw_writeb/w/l/q) to Store-Release (STLR), which restores the
+ required ordering. Because writel() and writel_relaxed() are built
+ on __raw_writel(), both are covered without changes to the higher
+ layers. It also promotes the DGH hint used after write-combining
+ memcpy-to-IO sequences to a DMB, so grouped stores are ordered
+ against subsequent reads without placing a barrier in the copy loop.
+
+ The fix is applied through the alternatives framework, so enabling
+ this option does not by itself activate the workaround: it is
+ patched in only when an affected CPU is detected, and is a no-op on
+ unaffected CPUs.
+
+ If unsure, say Y.
+
config ARM64_ERRATUM_834220
bool "Cortex-A57: 834220: Stage 2 translation fault might be incorrectly reported in presence of a Stage 1 fault (rare)"
depends on KVM
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 9495c4441a46..22792d1305aa 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -38,7 +38,9 @@
* Device-GRE attributes before the hint instruction with any memory accesses
* appearing after the hint instruction.
*/
-#define dgh() asm volatile("hint #6" : : : "memory")
+#define dgh() asm volatile(ALTERNATIVE("hint #6", "dmb osh", \
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE) \
+ : : : "memory")
#define spec_bar() asm volatile(ALTERNATIVE("dsb nsh\nisb\n", \
SB_BARRIER_INSN"nop\n", \
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 8cbd1e96fd50..69e0fa004d31 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -16,7 +16,6 @@
#include <asm/memory.h>
#include <asm/early_ioremap.h>
#include <asm/alternative.h>
-#include <asm/cpufeature.h>
#include <asm/rsi.h>
/*
@@ -25,29 +24,37 @@
#define __raw_writeb __raw_writeb
static __always_inline void __raw_writeb(u8 val, volatile void __iomem *addr)
{
- volatile u8 __iomem *ptr = addr;
- asm volatile("strb %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strb %w0, [%1]",
+ "stlrb %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writew __raw_writew
static __always_inline void __raw_writew(u16 val, volatile void __iomem *addr)
{
- volatile u16 __iomem *ptr = addr;
- asm volatile("strh %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strh %w0, [%1]",
+ "stlrh %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writel __raw_writel
static __always_inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
- volatile u32 __iomem *ptr = addr;
- asm volatile("str %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %w0, [%1]",
+ "stlr %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writeq __raw_writeq
static __always_inline void __raw_writeq(u64 val, volatile void __iomem *addr)
{
- volatile u64 __iomem *ptr = addr;
- asm volatile("str %x0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %x0, [%1]",
+ "stlr %x0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_readb __raw_readb
@@ -178,7 +185,8 @@ __const_memcpy_toio_aligned32(volatile u32 __iomem *to, const u32 *from,
: "rZ"(from[0]), "rZ"(from[1]), "r"(to));
break;
case 1:
- __raw_writel(*from, to);
+ asm volatile("str %w0, [%1]"
+ : : "rZ"(from[0]), "r"(to) : "memory");
break;
default:
BUILD_BUG();
@@ -235,7 +243,8 @@ __const_memcpy_toio_aligned64(volatile u64 __iomem *to, const u64 *from,
: "rZ"(from[0]), "rZ"(from[1]), "r"(to));
break;
case 1:
- __raw_writeq(*from, to);
+ asm volatile("str %x0, [%1]"
+ : : "rZ"(from[0]), "r"(to) : "memory");
break;
default:
BUILD_BUG();
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 4b0d5d932897..76c1f8cf1ee0 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -839,6 +839,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL),
},
#endif
+#ifdef CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM
+ {
+ /* NVIDIA Olympus core */
+ .desc = "NVIDIA Olympus device load/store ordering erratum",
+ .capability = ARM64_WORKAROUND_DEVICE_STORE_RELEASE,
+ ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
+ },
+#endif
#ifdef CONFIG_ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
{
/*
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 811c2479e82d..d367257bf770 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -120,6 +120,7 @@ WORKAROUND_CAVIUM_TX2_219_PRFM
WORKAROUND_CAVIUM_TX2_219_TVM
WORKAROUND_CLEAN_CACHE
WORKAROUND_DEVICE_LOAD_ACQUIRE
+WORKAROUND_DEVICE_STORE_RELEASE
WORKAROUND_NVIDIA_CARMEL_CNP
WORKAROUND_PMUV3_IMPDEF_TRAPS
WORKAROUND_QCOM_FALKOR_E1003
--
2.54.0.windows.1
^ permalink raw reply related
* [PATCH v4 2/2] arm64: io: apply the device store-release workaround once per block write
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
In-Reply-To: <20260625182425.3194066-1-sdonthineni@nvidia.com>
The generic memset_io()/memcpy_toio() are built on __raw_write*(), so on
parts with the NVIDIA Olympus device store/load ordering erratum the
ARM64_WORKAROUND_DEVICE_STORE_RELEASE workaround promotes every store in
the block to a store-release. Each stlr* carries a barrier cost, so block
MMIO becomes O(n) store-releases, making a block copy many times slower
than a single ordered burst and growing with the transfer size.
Provide arm64 memset_io()/memcpy_toio() that emit plain str* in the loop
and order the whole block against subsequent loads with a single
trailing dmb osh on affected CPUs (a no-op elsewhere, preserving the
relaxed contract of these helpers). This keeps block MMIO writes at
one-barrier cost rather than scaling with the transfer size.
Performance (NVIDIA Olympus, write-combining MMIO to a device BAR, single
PE pinned; per-call cost in ns; consecutive writes ping-pong between two
buffers so repeated stores are not coalesced; iowrite64/iowrite32 =
__iowrite{64,32}_copy()):
Table 1 - arm64 memset_io/memcpy_toio (this patch)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 231.6 ns | 231.6 ns | 232.4 ns | 232.4 ns |
| 16B | 231.7 ns | 231.9 ns | 232.7 ns | 232.6 ns |
| 32B | 231.9 ns | 232.7 ns | 232.9 ns | 232.9 ns |
| 64B | 232.7 ns | 235.0 ns | 233.7 ns | 233.6 ns |
| 128B | 233.6 ns | 235.8 ns | 234.4 ns | 234.3 ns |
| 256B | 237.7 ns | 276.8 ns | 264.0 ns | 276.7 ns |
| 512B | 237.7 ns | 277.1 ns | 238.1 ns | 277.6 ns |
| 1KB | 253.7 ns | 279.3 ns | 276.1 ns | 294.1 ns |
| 2KB | 295.0 ns | 318.7 ns | 288.5 ns | 308.3 ns |
| 4KB | 365.9 ns | 381.4 ns | 365.7 ns | 381.3 ns |
+-------+-----------+-----------+-----------+-------------+
all four helpers end with a single trailing barrier (dmb osh).
Table 2 - generic per-store memset_io/memcpy_toio
+-------+-----------+-----------+-------------+--------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-------------+--------------+
| 8B | 231.6 ns | 231.6 ns | 229.0 ns | 229.0 ns |
| 16B | 231.7 ns | 231.9 ns | 458.4 ns | 458.5 ns |
| 32B | 231.9 ns | 232.7 ns | 917.4 ns | 917.5 ns |
| 64B | 232.7 ns | 234.8 ns | 1835.4 ns | 1835.5 ns |
| 128B | 233.6 ns | 235.8 ns | 3670.9 ns | 3670.8 ns |
| 256B | 237.7 ns | 276.7 ns | 7341.6 ns | 7341.6 ns |
| 512B | 237.7 ns | 279.4 ns | 14001.4 ns | 14001.3 ns |
| 1KB | 253.7 ns | 279.1 ns | 28631.5 ns | 28631.8 ns |
| 2KB | 279.4 ns | 317.9 ns | 57276.3 ns | 57275.2 ns |
| 4KB | 365.7 ns | 381.5 ns | 114564.4 ns | 114563.6 ns |
+-------+-----------+-----------+-------------+--------------+
the generic memset_io()/memcpy_toio() build on __raw_write*(), which the
workaround promotes to store-release, so every store is individually
ordered - hence O(n) in the store count.
The arm64 versions stay flat at one-barrier cost while the generic
per-store writers collapse to O(n): at 4KB ~314x slower (~115 us vs
~366 ns).
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
---
arch/arm64/include/asm/io.h | 5 +++
arch/arm64/kernel/io.c | 82 +++++++++++++++++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 69e0fa004d31..649503f347bc 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -266,6 +266,11 @@ __iowrite64_copy(void __iomem *to, const void *from, size_t count)
}
#define __iowrite64_copy __iowrite64_copy
+void memset_io(volatile void __iomem *dst, int c, size_t count);
+#define memset_io memset_io
+void memcpy_toio(volatile void __iomem *dst, const void *src, size_t count);
+#define memcpy_toio memcpy_toio
+
/*
* I/O memory mapping functions.
*/
diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c
index fe86ada23c7d..b5fd9ee6d9eb 100644
--- a/arch/arm64/kernel/io.c
+++ b/arch/arm64/kernel/io.c
@@ -5,9 +5,91 @@
* Copyright (C) 2012 ARM Ltd.
*/
+#include <linux/align.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/unaligned.h>
+
+#include <asm/alternative.h>
+
+/*
+ * ARM64_WORKAROUND_DEVICE_STORE_RELEASE promotes every raw MMIO store
+ * (__raw_write*()) to a store-release on affected CPUs. The generic
+ * memset_io()/memcpy_toio() are built on those helpers, so the workaround would
+ * emit one store-release per element and turn a block write into O(n) ordered
+ * stores - far more costly than the single barrier a block actually needs.
+ *
+ * Provide arm64 versions that emit plain STR in the loop and order the whole
+ * block against subsequent loads with one trailing DMB OSH, patched in only on
+ * affected CPUs (a no-op elsewhere, so the relaxed contract of these helpers is
+ * preserved).
+ *
+ * This capability is currently enabled only for the NVIDIA Olympus device
+ * store/load ordering erratum, where a Device-nGnR* load may be observed before
+ * an older, non-overlapping Device-nGnR* store to the same peripheral.
+ */
+static __always_inline void iomem_block_store_barrier(void)
+{
+ asm volatile(ALTERNATIVE("nop", "dmb osh",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : : "memory");
+}
+
+void memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+ u64 qc = (u8)c;
+
+ qc *= ~0ULL / 0xff;
+
+ while (count && !IS_ALIGNED((__force unsigned long)dst, sizeof(u64))) {
+ asm volatile("strb %w0, [%1]" : : "rZ"((u8)c), "r"(dst) : "memory");
+ dst++;
+ count--;
+ }
+ while (count >= sizeof(u64)) {
+ asm volatile("str %x0, [%1]" : : "rZ"(qc), "r"(dst) : "memory");
+ dst += sizeof(u64);
+ count -= sizeof(u64);
+ }
+ while (count) {
+ asm volatile("strb %w0, [%1]" : : "rZ"((u8)c), "r"(dst) : "memory");
+ dst++;
+ count--;
+ }
+
+ iomem_block_store_barrier();
+}
+EXPORT_SYMBOL(memset_io);
+
+void memcpy_toio(volatile void __iomem *dst, const void *src, size_t count)
+{
+ while (count && !IS_ALIGNED((__force unsigned long)dst, sizeof(u64))) {
+ asm volatile("strb %w0, [%1]"
+ : : "rZ"(*(const u8 *)src), "r"(dst) : "memory");
+ src++;
+ dst++;
+ count--;
+ }
+ while (count >= sizeof(u64)) {
+ asm volatile("str %x0, [%1]"
+ : : "rZ"(get_unaligned((const u64 *)src)), "r"(dst)
+ : "memory");
+ src += sizeof(u64);
+ dst += sizeof(u64);
+ count -= sizeof(u64);
+ }
+ while (count) {
+ asm volatile("strb %w0, [%1]"
+ : : "rZ"(*(const u8 *)src), "r"(dst) : "memory");
+ src++;
+ dst++;
+ count--;
+ }
+
+ iomem_block_store_barrier();
+}
+EXPORT_SYMBOL(memcpy_toio);
/*
* This generates a memcpy that works on a from/to address which is aligned to
--
2.54.0.windows.1
^ permalink raw reply related
* [PATCH v4 0/2] arm64: errata: NVIDIA Olympus device store/load ordering
From: Shanker Donthineni @ 2026-06-25 18:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Vladimir Murzin
Cc: Jason Gunthorpe, linux-arm-kernel, Mark Rutland, linux-kernel,
linux-doc, Shanker Donthineni, Vikram Sethi, Jason Sequeira
This series works around the NVIDIA Olympus device store/load ordering
erratum (T410-OLY-1027): a Device-nGnR* load can be observed by a
peripheral before an older, non-overlapping Device-nGnR* store to the
same peripheral, breaking the program order that drivers rely on for
MMIO and potentially leaving a device in an incorrect state.
Patch 1 adds the workaround. It promotes the raw MMIO store helpers
(__raw_writeb/w/l/q, and therefore writel()/writel_relaxed()) to
store-release on affected CPUs, and promotes the trailing DGH of the
write-combining __iowrite{32,64}_copy() helpers to dmb osh. Everything is
gated on a new ARM64_WORKAROUND_DEVICE_STORE_RELEASE cpucap and patched
in only on affected parts, so it is a no-op elsewhere.
Patch 2 provides arm64 memset_io()/memcpy_toio(). The generic versions
are built on __raw_write*(), so patch 1 would promote every store in a
block to a store-release; as each STLR drains the write-combining buffer,
block MMIO becomes O(n) store-releases. The arm64 versions emit plain
STR in the loop and order the whole block with a single trailing dmb osh,
keeping block MMIO at one-barrier cost.
Performance: NVIDIA Olympus, write-combining MMIO to a device BAR, single
PE pinned; per-call cost in ns. Consecutive writes ping-pong between two
buffers so repeated stores are not coalesced. iowrite64/iowrite32 =
__iowrite{64,32}_copy().
Table 1 - workaround off (CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM=n)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 67.9 ns | 67.8 ns | 3.6 ns | 3.6 ns |
| 16B | 67.9 ns | 67.8 ns | 4.0 ns | 4.0 ns |
| 32B | 67.9 ns | 67.9 ns | 4.6 ns | 4.6 ns |
| 64B | 69.1 ns | 69.1 ns | 69.1 ns | 69.0 ns |
| 128B | 138.3 ns | 138.3 ns | 138.4 ns | 138.3 ns |
| 256B | 276.6 ns | 276.6 ns | 276.6 ns | 276.7 ns |
| 512B | 276.6 ns | 276.5 ns | 276.6 ns | 276.6 ns |
| 1KB | 276.6 ns | 278.4 ns | 276.6 ns | 276.6 ns |
| 2KB | 278.4 ns | 278.4 ns | 275.9 ns | 276.6 ns |
| 4KB | 365.7 ns | 365.7 ns | 365.7 ns | 365.7 ns |
+-------+-----------+-----------+-----------+-------------+
relaxed/no-flush: memset_io()/memcpy_toio() issue plain stores with no
trailing dgh() or barrier, unlike __iowrite*_copy() which ends with dgh().
Table 2 - workaround on, arm64 memset_io/memcpy_toio (this series)
+-------+-----------+-----------+-----------+-------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-----------+-------------+
| 8B | 231.6 ns | 231.6 ns | 232.4 ns | 232.4 ns |
| 16B | 231.7 ns | 231.9 ns | 232.7 ns | 232.6 ns |
| 32B | 231.9 ns | 232.7 ns | 232.9 ns | 232.9 ns |
| 64B | 232.7 ns | 235.0 ns | 233.7 ns | 233.6 ns |
| 128B | 233.6 ns | 235.8 ns | 234.4 ns | 234.3 ns |
| 256B | 237.7 ns | 276.8 ns | 264.0 ns | 276.7 ns |
| 512B | 237.7 ns | 277.1 ns | 238.1 ns | 277.6 ns |
| 1KB | 253.7 ns | 279.3 ns | 276.1 ns | 294.1 ns |
| 2KB | 295.0 ns | 318.7 ns | 288.5 ns | 308.3 ns |
| 4KB | 365.9 ns | 381.4 ns | 365.7 ns | 381.3 ns |
+-------+-----------+-----------+-----------+-------------+
all four helpers end with a single trailing barrier (dmb osh).
Table 3 - workaround on, generic per-store memset_io/memcpy_toio
+-------+-----------+-----------+-------------+--------------+
| size | iowrite64 | iowrite32 | memset_io | memcpy_toio |
+-------+-----------+-----------+-------------+--------------+
| 8B | 231.6 ns | 231.6 ns | 229.0 ns | 229.0 ns |
| 16B | 231.7 ns | 231.9 ns | 458.4 ns | 458.5 ns |
| 32B | 231.9 ns | 232.7 ns | 917.4 ns | 917.5 ns |
| 64B | 232.7 ns | 234.8 ns | 1835.4 ns | 1835.5 ns |
| 128B | 233.6 ns | 235.8 ns | 3670.9 ns | 3670.8 ns |
| 256B | 237.7 ns | 276.7 ns | 7341.6 ns | 7341.6 ns |
| 512B | 237.7 ns | 279.4 ns | 14001.4 ns | 14001.3 ns |
| 1KB | 253.7 ns | 279.1 ns | 28631.5 ns | 28631.8 ns |
| 2KB | 279.4 ns | 317.9 ns | 57276.3 ns | 57275.2 ns |
| 4KB | 365.7 ns | 381.5 ns | 114564.4 ns | 114563.6 ns |
+-------+-----------+-----------+-------------+--------------+
the generic memset_io()/memcpy_toio() build on __raw_write*(), which the
workaround promotes to store-release, so every store is individually
ordered - hence O(n) in the store count.
Tables 2 and 3 show why patch 2 is needed: the generic per-store block
writers collapse to O(n) under the workaround (4KB ~314x slower, ~115 us
vs ~366 ns), while the arm64 versions stay flat at one-barrier cost.
Changes since v3:
- Split the workaround into two patches: the erratum fix (1/2) and the
arm64 memset_io()/memcpy_toio() block writers (2/2).
- Reworked the raw MMIO write helpers to use a direct base-register
str*/stlr* alternative sequence instead of a per-write static branch.
- Covered the write-combining __iowrite{32,64}_copy() path by patching
dgh() to dmb osh on affected CPUs, keeping the contiguous STR groups
and the ordering barrier outside the copy loop; the single-element
case now uses a plain str* as well.
- Added arm64 memset_io()/memcpy_toio() so the byte/word block writers
take one trailing dmb osh instead of a per-store store-release.
- Updated the commit messages to describe the offset-addressing
trade-off.
Changes since v2:
- Reworked the raw MMIO write helpers so unaffected CPUs keep the
existing offset-addressed STR sequence, while affected CPUs use the
base-register STLR path.
- Updated the commit message to match the code changes.
- Rebased on top of the arm64 for-next/errata branch:
https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=for-next/errata
Changes since v1:
- Updated the commit message based on feedback from Vladimir Murzin.
Shanker Donthineni (2):
arm64: errata: Workaround NVIDIA Olympus device store/load ordering
arm64: io: apply the device store-release workaround once per block
write
Documentation/arch/arm64/silicon-errata.rst | 2 +
arch/arm64/Kconfig | 25 +++++++++
arch/arm64/include/asm/barrier.h | 4 +-
arch/arm64/include/asm/io.h | 36 +++++++++----
arch/arm64/kernel/cpu_errata.c | 8 +++
arch/arm64/kernel/io.c | 82 +++++++++++++++++++++++++++++
arch/arm64/tools/cpucaps | 1 +
7 files changed, 146 insertions(+), 12 deletions(-)
--
2.54.0.windows.1
^ permalink raw reply
* Re: [RFC PATCH 1/3] dt-bindings: pinctrl: mt8516/mt8167: Move compatibles from mt66xx to mt6795
From: Conor Dooley @ 2026-06-25 18:17 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: linux-mediatek, Sean Wang, Linus Walleij, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, linux-gpio, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <CAORyz2JHj7i6VhKom+tVd8PWBjM=TFhbr8-mOy3GH6eDYu4WPw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2943 bytes --]
On Thu, Jun 25, 2026 at 06:47:32PM +0200, Luca Leonardo Scorcia wrote:
> Hi,
>
> > I've not done a very through analysis, but this seems like a massive ABI
> > break.
> > The change you're trying to make here will mean that new kernels will
> > not work with older devicetrees AFAICT.
>
> Correct, that's the reason I sent it as an RFC (I mentioned this in
> the cover letter). I am new to kernel work and I'm not sure how to
> deal with this change. On one hand I am almost certain now that the
> upstream driver has never been used in actual devices, since the older
> code was only partially merged and also, as Sashiko correctly pointed
> out in [1], it had serious errors when matched against the data sheet:
>
> Sashiko:
> > Does this configuration cause a regression in pin multiplexing across the SoC?
> > The legacy driver used a 4-bit shift per pin to pack 5 pins per 32-bit
> > register. By passing 3 as the width here, the framework calculates mode
> > offsets using 3 bits per pin. This causes pinmux writes to align with
> > the wrong bits and can overwrite the configurations of adjacent pins.
>
> Data sheet here clearly shows 3 bits per pin are used to choose the
> pin function.
>
> On the other hand I know that breaking the ABI is a big no. But what
> would be an appropriate solution? Maybe duplicating the driver with a
If you can substantiate a claim that the current setup doesn't actually
work for these devices (which seems plausible), you can justify changing
the ABI on that basis.
> different name, something like mediatek,mt8167-pinctrl-v2? Is there
> another driver I could have a look at to learn how to approach this
> problem?
Usually when making ABI changes because something was inaccurate (but
not wrong to the point that it didn't work at all) it's possible to
support both new and old ABIs at the same time because of new properties
etc. This is a difficult one because it's using the same properties in
different ways. A new compatible would definitely be required for a
genuine fresh start while retaining kernel support for the old mechanism
in this case.
But as I said, if what's in the kernel right now does not work at all,
then you can probably just rework in place. Your commit messages will
have to be very clear about why what you're doing is okay however.
It'd probably be best to try to detect the old devicetrees (if that's
even possible, will be tricky unless the devices you're moving are the
ones that need mediatek,pctl-regmap) and reject probe.
> Sashiko also pointed out some other minor issues with the register
> maps I already fixed locally after confirming with the data sheet, but
> did not provide clues about how to solve the ABI breakage.
>
> [1] https://sashiko.dev/#/message/20260625111629.6CD701F000E9%40smtp.kernel.org
>
> Thank you for your time!
> --
> Luca Leonardo Scorcia
> l.scorcia@gmail.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: arm: ti: Add bindings for PHYTEC AM67x based hardware
From: Rob Herring (Arm) @ 2026-06-25 17:50 UTC (permalink / raw)
To: Nathan Morrisson
Cc: linux-arm-kernel, upstream, conor+dt, krzk+dt, nm, devicetree,
kristo, linux-kernel, vigneshr
In-Reply-To: <20260625160214.4001298-1-nmorrisson@phytec.com>
On Thu, 25 Jun 2026 09:02:13 -0700, Nathan Morrisson wrote:
> Add device tree bindings for the AM67x based phyCORE-AM67x SoM and
> phyBOARD-Rigel.
>
> Signed-off-by: Nathan Morrisson <nmorrisson@phytec.com>
> ---
> Documentation/devicetree/bindings/arm/ti/k3.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/arm/ti/k3.yaml:228:1: [error] syntax error: found character '\t' that cannot start any token (syntax)
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/arm/ti/k3.yaml: ignoring, error parsing file
./Documentation/devicetree/bindings/arm/ti/k3.yaml: error loading YAML: while scanning for the next token
found character '\t' that cannot start any token
in "./Documentation/devicetree/bindings/arm/ti/k3.yaml", line 228, column 1
./Documentation/devicetree/bindings/arm/ti/k3.yaml:228:1: found character '\t' that cannot start any token
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260625160214.4001298-1-nmorrisson@phytec.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
* [PATCH v7 18/27] phy: rockchip: usbdp: Drop DP HPD handling
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Drop the HPD handling logic from the USBDP PHY. The registers involved
require the display controller power domain being enabled and thus the
HPD signal should be handled by the displayport controller itself.
Apart from that the HPD handling as it is done here is incorrect and
misses hotplug events happening after the USB-C connector (e.g. when
a USB-C to HDMI adapter is involved and the HDMI cable is replugged).
Proper USB-C DP HPD support requires some restructuring of the DP
controller driver, which will happen independent of this patch. The
mainline kernel does not yet support USB-C DP AltMode on RK3588 and
RK3576, so it is fine to drop this code without adding the counterpart
in the DRM in an atomic change.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 85 ++++---------------------------
1 file changed, 9 insertions(+), 76 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 7bac3f14d750..8b5b188fd121 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -128,7 +128,6 @@ struct rk_udphy_grf_cfg {
struct rk_udphy_vogrf_cfg {
/* vo-grf */
- struct rk_udphy_grf_reg hpd_trigger;
u32 dp_lane_reg;
};
@@ -186,14 +185,11 @@ struct rk_udphy {
u32 dp_lane_sel[4];
u32 dp_aux_dout_sel;
u32 dp_aux_din_sel;
- bool dp_sink_hpd_sel;
- bool dp_sink_hpd_cfg;
unsigned int link_rate;
unsigned int lanes;
u8 bw;
int id;
- bool dp_in_use;
int dp_lanes;
/* PHY const config */
@@ -582,19 +578,6 @@ static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
-static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
-{
- const struct rk_udphy_cfg *cfg = udphy->cfgs;
-
- udphy->dp_sink_hpd_sel = true;
- udphy->dp_sink_hpd_cfg = hpd;
-
- if (!udphy->dp_in_use)
- return;
-
- rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
-}
-
static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
{
if (udphy->mode == mode)
@@ -1030,29 +1013,6 @@ static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
rk_udphy_disable(udphy);
}
-static int rk_udphy_dp_phy_init(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
-
- udphy->dp_in_use = true;
-
- mutex_unlock(&udphy->mutex);
-
- return 0;
-}
-
-static int rk_udphy_dp_phy_exit(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
- udphy->dp_in_use = false;
- mutex_unlock(&udphy->mutex);
- return 0;
-}
-
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
@@ -1284,8 +1244,6 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
}
static const struct phy_ops rk_udphy_dp_phy_ops = {
- .init = rk_udphy_dp_phy_init,
- .exit = rk_udphy_dp_phy_exit,
.power_on = rk_udphy_dp_phy_power_on,
.power_off = rk_udphy_dp_phy_power_off,
.configure = rk_udphy_dp_phy_configure,
@@ -1339,6 +1297,14 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
u8 mode;
+ /*
+ * Ignore mux events not involving DP AltMode, because
+ * the mode field is being reused, e.g. state->mode == 4
+ * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
+ */
+ if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
+ return 0;
+
mutex_lock(&udphy->mutex);
switch (state->mode) {
@@ -1370,22 +1336,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
break;
}
- if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
- struct typec_displayport_data *data = state->data;
-
- if (!data) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- } else if (data->status & DP_STATUS_IRQ_HPD) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- usleep_range(750, 800);
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else if (data->status & DP_STATUS_HPD_STATE) {
- rk_udphy_mode_set(udphy, mode);
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- }
- }
+ rk_udphy_mode_set(udphy, mode);
mutex_unlock(&udphy->mutex);
return 0;
@@ -1541,20 +1492,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused rk_udphy_resume(struct device *dev)
-{
- struct rk_udphy *udphy = dev_get_drvdata(dev);
-
- if (udphy->dp_sink_hpd_sel)
- rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
-
- return 0;
-}
-
-static const struct dev_pm_ops rk_udphy_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
-};
-
static const char * const rk_udphy_rst_list[] = {
"init", "cmn", "lane", "pcs_apb", "pma_apb"
};
@@ -1578,7 +1515,6 @@ static const struct rk_udphy_cfg rk3576_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
},
@@ -1619,11 +1555,9 @@ static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
.dp_lane_reg = 0x0008,
},
},
@@ -1659,7 +1593,6 @@ static struct platform_driver rk_udphy_driver = {
.driver = {
.name = "rockchip-usbdp-phy",
.of_match_table = rk_udphy_dt_match,
- .pm = &rk_udphy_pm_ops,
},
};
module_platform_driver(rk_udphy_driver);
--
2.53.0
^ permalink raw reply related
* [PATCH v7 17/27] phy: rockchip: usbdp: Register DP aux bridge
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Add support to use USB-C connectors with the DP altmode helper code on
devicetree based platforms. To get this working there must be a DRM
bridge chain from the DisplayPort controller to the USB-C connector.
E.g. on Rockchip RK3576:
root@rk3576 # cat /sys/kernel/debug/dri/0/encoder-0/bridges
bridge[0]: dw_dp_bridge_funcs
refcount: 7
type: [10] DP
OF: /soc/dp@27e40000:rockchip,rk3576-dp
ops: [0x47] detect edid hpd
bridge[1]: drm_aux_bridge_funcs
refcount: 4
type: [0] Unknown
OF: /soc/phy@2b010000:rockchip,rk3576-usbdp-phy
ops: [0x0]
bridge[2]: drm_aux_hpd_bridge_funcs
refcount: 5
type: [10] DP
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
ops: [0x4] hpd
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/Kconfig | 2 ++
drivers/phy/rockchip/phy-rockchip-usbdp.c | 14 ++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 14698571b607..39759bb2fa1d 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -136,8 +136,10 @@ config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
+ depends on DRM || DRM=n
select GENERIC_PHY
select USB_COMMON
+ select DRM_AUX_BRIDGE if DRM_BRIDGE
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 71ee2f4faf0b..7bac3f14d750 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -6,6 +6,7 @@
* Copyright (C) 2024 Collabora Ltd
*/
+#include <drm/bridge/aux-bridge.h>
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
@@ -1444,6 +1445,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
+ struct fwnode_handle *dp_aux_ep;
struct resource *res;
struct rk_udphy *udphy;
void __iomem *base;
@@ -1502,6 +1504,18 @@ static int rk_udphy_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * Only register the DRM bridge, if the DP aux channel is connected.
+ * Some boards use the USBDP PHY only for its USB3 capabilities.
+ */
+ dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
+ if (dp_aux_ep) {
+ ret = drm_aux_bridge_register(dev);
+ fwnode_handle_put(dp_aux_ep);
+ if (ret)
+ return ret;
+ }
+
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
if (IS_ERR(udphy->phy_u3)) {
ret = PTR_ERR(udphy->phy_u3);
--
2.53.0
^ permalink raw reply related
* [PATCH v7 23/27] phy: rockchip: usbdp: Use guard functions for mutex
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Convert the driver to use guard functions for mutex handling as
a small cleanup. There is a small functional change in the DP PHY
power up function, which no longer sleeps if the internal powerup
code returns an error. This is not a problem as the sleep is only
relevant for successful power-up.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 54 +++++++++++++------------------
1 file changed, 23 insertions(+), 31 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 0119b6229b0a..6f3184011e5f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -10,6 +10,7 @@
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -655,14 +656,15 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
if (orien == TYPEC_ORIENTATION_NONE) {
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
/* unattached */
rk_udphy_usb_bvalid_enable(udphy, false);
- goto unlock_ret;
+
+ return 0;
}
if (udphy->flip != flipped)
@@ -672,8 +674,6 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
-unlock_ret:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1050,26 +1050,25 @@ static int rk_udphy_dp_phy_power_on(struct phy *phy)
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret;
- mutex_lock(&udphy->mutex);
+ scoped_guard(mutex, &udphy->mutex) {
+ phy_set_bus_width(phy, udphy->dp_lanes);
- phy_set_bus_width(phy, udphy->dp_lanes);
-
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
- if (ret)
- goto unlock;
+ ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
+ if (ret)
+ return ret;
- rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
- rk_udphy_dp_lane_select(udphy);
+ rk_udphy_dp_lane_select(udphy);
+ }
-unlock:
- mutex_unlock(&udphy->mutex);
/*
* If data send by aux channel too fast after phy power on,
* the aux may be not ready which will cause aux error. Adding
* delay to avoid this issue.
*/
usleep_range(10000, 11000);
+
return ret;
}
@@ -1077,10 +1076,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
rk_udphy_dp_lane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1285,35 +1284,30 @@ static const struct phy_ops rk_udphy_dp_phy_ops = {
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- int ret = 0;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed, disable U3 port */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
- goto unlock;
+ return 0;
}
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
-
-unlock:
- mutex_unlock(&udphy->mutex);
- return ret;
+ return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
- goto unlock;
+ return 0;
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
-unlock:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1337,12 +1331,10 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
state->alt->svid != USB_TYPEC_DP_SID)
return 0;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
rk_udphy_set_typec_state(udphy, state->mode);
- mutex_unlock(&udphy->mutex);
-
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v7 27/27] phy: rockchip: usbdp: Avoid xHCI SErrors
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
The USBDP PHY provides the PIPE clock to the USB3 controller, which
means the PHY must be fully running when anything tries to access
the xHCI registers.
When switching between USB3-only, USB3 + DP and DP-only mode, the
PHY must be re-initialized resulting in a short period of the PHY
being disabled. If the DWC3 driver decides to access the xHCI at
this point the system will fail with an SError.
This patch avoids the problems by disabling the USB3 port before
re-initializing it. This does a couple of things:
- forces phystatus to 0 from GRF (not from PHY)
- switches PIPE clock source from PHY to UTMI (safe fallback clock)
- num_u3_port=0
The last part will be ignored, as DWC3 already probed, but the
clock re-routing will avoid the SError. There is a small delay
afterwards to make sure the mux happened. The datasheet gives
no hints how long it takes, so delay time is a guess.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 8c5d6b8595e2..82ce80dc9dbc 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1026,8 +1026,8 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
} else if (udphy->phy_needs_reinit) {
- if (udphy->mode == UDPHY_MODE_DP)
- rk_udphy_u3_port_disable(udphy, true);
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
ret = rk_udphy_init(udphy);
if (ret)
--
2.53.0
^ permalink raw reply related
* [PATCH v7 21/27] phy: rockchip: usbdp: Factor out lane_mux_sel setup
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Avoid describing the USB+DP lane_mux_sel logic twice by introducing
a helper function to reduce code duplication.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 81 +++++++++++++++----------------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 3fa97a8c2555..0bcaf54c5645 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -587,6 +587,42 @@ static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
udphy->mode = mode;
}
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
+{
+ u8 mode;
+
+ switch (state) {
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ mode = UDPHY_MODE_DP;
+ udphy->dp_lanes = 4;
+ break;
+
+ case TYPEC_DP_STATE_D:
+ default:
+ if (udphy->flip) {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
+ } else {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ }
+ mode = UDPHY_MODE_DP_USB;
+ udphy->dp_lanes = 2;
+ break;
+ }
+
+ rk_udphy_mode_set(udphy, mode);
+}
+
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
{
if (udphy->flip) {
@@ -594,10 +630,6 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 1;
udphy->dp_lane_sel[2] = 3;
udphy->dp_lane_sel[3] = 2;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
@@ -607,18 +639,14 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 3;
udphy->dp_lane_sel[2] = 1;
udphy->dp_lane_sel[3] = 0;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
- rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
- udphy->dp_lanes = 2;
+ /* default to USB3 + DP as 4 lane USB is not supported */
+ rk_udphy_set_typec_state(udphy, TYPEC_DP_STATE_D);
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
@@ -1299,7 +1327,6 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
- u8 mode;
/*
* Ignore mux events not involving DP AltMode, because
@@ -1311,38 +1338,10 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
mutex_lock(&udphy->mutex);
- switch (state->mode) {
- case TYPEC_DP_STATE_C:
- case TYPEC_DP_STATE_E:
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- mode = UDPHY_MODE_DP;
- udphy->dp_lanes = 4;
- break;
-
- case TYPEC_DP_STATE_D:
- default:
- if (udphy->flip) {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
- } else {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- }
- mode = UDPHY_MODE_DP_USB;
- udphy->dp_lanes = 2;
- break;
- }
-
- rk_udphy_mode_set(udphy, mode);
+ rk_udphy_set_typec_state(udphy, state->mode);
mutex_unlock(&udphy->mutex);
+
return 0;
}
--
2.53.0
^ permalink raw reply related
* RE: [RFC PATCH 1/6] arm64: rsi: Add RSI host call structure and helper function
From: Kameron Carr @ 2026-06-25 17:44 UTC (permalink / raw)
To: 'Michael Kelley'
Cc: catalin.marinas, will, mark.rutland, lpieralisi, sudeep.holla,
arnd, thuth, linux-hyperv, linux-arm-kernel, linux-kernel,
linux-arch, kys, haiyangz, wei.liu, decui, longli
In-Reply-To: <SN6PR02MB4157C9AA6BA2DD14E7697F2BD4E32@SN6PR02MB4157.namprd02.prod.outlook.com>
On Thursday, June 18, 2026 10:46 AM, Michael Kelley wrote:
> From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Tuesday, June
> 9, 2026 11:10 AM
> > diff --git a/arch/arm64/include/asm/rsi_smc.h
> b/arch/arm64/include/asm/rsi_smc.h
> > index e19253f96c940..ffea93340ed7f 100644
> > --- a/arch/arm64/include/asm/rsi_smc.h
> > +++ b/arch/arm64/include/asm/rsi_smc.h
> > @@ -142,6 +142,12 @@ struct realm_config {
> > */
> > } __aligned(0x1000);
> >
> > +struct rsi_host_call {
> > + u16 immediate;
>
> I don't see the "immediate" used anywhere in this patch set.
> Is it always zero for the Hyper-V use cases? Just curious ...
Yes, the immediate value is always zero for Hyper-V host calls.
-- Kameron
^ permalink raw reply
* RE: [RFC PATCH 2/6] firmware: smccc: Detect hypervisor via RSI host call in CCA Realms
From: Kameron Carr @ 2026-06-25 17:42 UTC (permalink / raw)
To: 'Michael Kelley'
Cc: catalin.marinas, will, mark.rutland, lpieralisi, sudeep.holla,
arnd, thuth, linux-hyperv, linux-arm-kernel, linux-kernel,
linux-arch, kys, haiyangz, wei.liu, decui, longli
In-Reply-To: <SN6PR02MB4157F6A66DEDE650298E120ED4E32@SN6PR02MB4157.namprd02.prod.outlook.com>
On Thursday, June 18, 2026 10:46 AM, Michael Kelley wrote:
> From: Kameron Carr <kameroncarr@linux.microsoft.com> Sent: Tuesday, June
> 9, 2026 11:10 AM
> > diff --git a/drivers/firmware/smccc/smccc.c
> b/drivers/firmware/smccc/smccc.c
> > index bdee057db2fd3..6b465e65472b0 100644
> > --- a/drivers/firmware/smccc/smccc.c
> > +++ b/drivers/firmware/smccc/smccc.c
> > @@ -12,6 +12,12 @@
> > #include <linux/platform_device.h>
> > #include <asm/archrandom.h>
> >
> > +#ifdef CONFIG_ARM64
> > +#include <linux/cleanup.h>
> > +#include <linux/spinlock.h>
> > +#include <asm/rsi.h>
> > +#endif
> > +
> > static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
> > static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
> >
> > @@ -67,12 +73,45 @@ s32 arm_smccc_get_soc_id_revision(void)
> > }
> > EXPORT_SYMBOL_GPL(arm_smccc_get_soc_id_revision);
> >
> > +#ifdef CONFIG_ARM64
> > +static struct rsi_host_call uuid_hc;
> > +static DEFINE_SPINLOCK(uuid_hc_lock);
>
> So evidently Sashiko is wrong in saying that struct rsi_host_call must be
> in decrypted memory?
Yes, Sashiko is wrong. The RMM spec clearly states that the rsi_host_call
struct must be encrypted / "protected". The other two requirements are
256 aligned and not RIPAS_EMPTY.
^ permalink raw reply
* [PATCH v7 24/27] phy: rockchip: usbdp: Support going from DP-only mode to USB mode
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
When a USB-C adapter, which maps all Superspeed lanes to DP is plugged
in, the USB support is disabled in the PHY. When the adapter is
unplugged and a different adapter with USB functionality is plugged in
afterwards, USB functionality is not restored as the USB controller
keeps the PHY enabled for the entire time.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 6f3184011e5f..182141837596 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -178,6 +178,7 @@ struct rk_udphy {
/* utilized for USB */
bool hs; /* flag for high-speed */
+ bool usb_in_use;
/* utilized for DP */
struct gpio_desc *sbu1_dc_gpio;
@@ -1021,6 +1022,10 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
ret = rk_udphy_init(udphy);
if (ret)
return ret;
+
+ if (udphy->mode & UDPHY_MODE_USB)
+ rk_udphy_u3_port_disable(udphy, false);
+
udphy->phy_needs_reinit = false;
}
@@ -1284,6 +1289,7 @@ static const struct phy_ops rk_udphy_dp_phy_ops = {
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
+ int ret;
guard(mutex)(&udphy->mutex);
@@ -1293,7 +1299,13 @@ static int rk_udphy_usb3_phy_init(struct phy *phy)
return 0;
}
- return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
+ ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
+ if (ret)
+ return ret;
+
+ udphy->usb_in_use = true;
+
+ return 0;
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
@@ -1302,6 +1314,8 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
guard(mutex)(&udphy->mutex);
+ udphy->usb_in_use = false;
+
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
return 0;
@@ -1321,6 +1335,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
+ u8 old_mode;
/*
* Ignore mux events not involving Safe State, USB State or DP AltMode,
@@ -1333,8 +1348,20 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
guard(mutex)(&udphy->mutex);
+ old_mode = udphy->mode;
+
rk_udphy_set_typec_state(udphy, state->mode);
+ /*
+ * If the new mode includes USB but the old one didn't (e.g. leaving
+ * DP-only), and the USB PHY was already initialized by the USB
+ * controller, we need to power on the USB side now since no
+ * subsequent phy_init call will come from the controller.
+ */
+ if ((udphy->mode & UDPHY_MODE_USB) && !(old_mode & UDPHY_MODE_USB) &&
+ udphy->usb_in_use && !udphy->hs)
+ return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
+
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v7 25/27] phy: rockchip: usbdp: Hold mutex in DP PHY configure
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel, Sashiko
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
rk_udphy_dp_phy_configure() accesses some variables from the struct
rk_udphy, which are updated independently from the USB-C framework.
The USB-C mux/orientation switch functions already hold a mutex to
ensure mutual exclusive access to the struct rk_udphy states, so
simply hold the same one in the DP PHY configuration function.
Reproducing problems due to this on real hardware would be really hard,
but could be possible when quickly re-connecting the USB-C connector.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612164627.23D391F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 182141837596..fc788f2bf5fb 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1189,6 +1189,8 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
u32 i, val, lane;
int ret;
+ guard(mutex)(&udphy->mutex);
+
if (dp->set_rate) {
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
if (ret)
--
2.53.0
^ permalink raw reply related
* [PATCH v7 22/27] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel, Sashiko
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Type-C state events,
so that the muxing is properly updated when exiting DP AltMode.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619155020.CC7361F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 0bcaf54c5645..0119b6229b0a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1329,11 +1329,12 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
/*
- * Ignore mux events not involving DP AltMode, because
- * the mode field is being reused, e.g. state->mode == 4
- * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
+ * Ignore mux events not involving Safe State, USB State or DP AltMode,
+ * because the mode field is being reused, e.g. state->mode == 4 could
+ * be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
*/
- if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
+ if (state->mode >= TYPEC_STATE_MODAL || !state->alt ||
+ state->alt->svid != USB_TYPEC_DP_SID)
return 0;
mutex_lock(&udphy->mutex);
--
2.53.0
^ permalink raw reply related
* [PATCH v7 09/27] phy: rockchip: usbdp: Keep clocks running on PHY re-init
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
When a mode change is required rk_udphy_power_on() disables
the clocks and then calls rk_udphy_setup(), which then enables
all the clocks again before continuing with rk_udphy_init().
Considering that rk_udphy_init() does assert the reset lines,
re-enabling the clocks is just delaying things. Avoid it by
directly calling rk_udphy_init().
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 2324f9050f63..989a7884d8b5 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1017,8 +1017,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
- rk_udphy_disable(udphy);
- ret = rk_udphy_setup(udphy);
+ ret = rk_udphy_init(udphy);
if (ret)
return ret;
udphy->mode_change = false;
--
2.53.0
^ permalink raw reply related
* [PATCH v7 20/27] phy: rockchip: usbdp: Re-init the PHY on orientation change
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Changing the cable orientation reconfigures the lane muxing, which
requires re-initializing the PHY. Without this DP functionality
breaks, if the cable is re-plugged with swapped orientation.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index b08f0c142b0d..3fa97a8c2555 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -625,6 +625,7 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
enum typec_orientation orien)
{
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
+ bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
mutex_lock(&udphy->mutex);
@@ -636,7 +637,10 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
goto unlock_ret;
}
- udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
+ if (udphy->flip != flipped)
+ udphy->phy_needs_reinit = true;
+
+ udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
--
2.53.0
^ permalink raw reply related
* [PATCH v7 19/27] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
From: Sebastian Reichel @ 2026-06-25 17:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260625-rockchip-usbdp-cleanup-v7-0-38eb3cf654fd@collabora.com>
Right now the mode_change property is set whenever the mode changes
between USB-only, DP-only and USB-DP. It is needed, because on any
mode change the PHY needs to be re-initialized. Apparently at least
DP also requires a re-init when the cable orientation is changed,
which is currently not being done (except when the orientation switch
also involves a mode change). Prepare for this by renaming mode_change
to phy_needs_reinit.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 8b5b188fd121..b08f0c142b0d 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -171,7 +171,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool mode_change;
+ bool phy_needs_reinit;
u8 mode;
u8 status;
@@ -583,7 +583,7 @@ static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
if (udphy->mode == mode)
return;
- udphy->mode_change = true;
+ udphy->phy_needs_reinit = true;
udphy->mode = mode;
}
@@ -981,15 +981,15 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
- udphy->mode_change = false;
- } else if (udphy->mode_change) {
+ udphy->phy_needs_reinit = false;
+ } else if (udphy->phy_needs_reinit) {
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
ret = rk_udphy_init(udphy);
if (ret)
return ret;
- udphy->mode_change = false;
+ udphy->phy_needs_reinit = false;
}
udphy->status |= mode;
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox