* [PATCH v8 4/5] arm64: dts: imx943: Add pcie1 and pcie1-ep supports
From: Richard Zhu @ 2026-03-24 2:30 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, bhelgaas, frank.li, l.stach, lpieralisi,
kwilczynski, mani, s.hauer, kernel, festevam
Cc: linux-pci, linux-arm-kernel, devicetree, imx, linux-kernel,
Richard Zhu
In-Reply-To: <20260324023036.784466-1-hongxing.zhu@nxp.com>
Add pcie1 and pcie1-ep supports.
Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
---
arch/arm64/boot/dts/freescale/imx943.dtsi | 75 +++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi
index 45b8da758e87..8575134176f5 100644
--- a/arch/arm64/boot/dts/freescale/imx943.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx943.dtsi
@@ -145,4 +145,79 @@ l3_cache: l3-cache {
cache-unified;
};
};
+
+ soc {
+ pcie1: pcie@4c380000 {
+ compatible = "fsl,imx943-pcie", "fsl,imx95-pcie";
+ reg = <0 0x4c380000 0 0x10000>,
+ <8 0x80100000 0 0xfe00000>,
+ <0 0x4c3e0000 0 0x10000>,
+ <0 0x4c3c0000 0 0x4000>;
+ reg-names = "dbi", "config", "atu", "app";
+ ranges = <0x81000000 0 0x00000000 0x8 0x8ff00000 0 0x00100000>,
+ <0x82000000 0 0x10000000 0xa 0x10000000 0 0x80000000>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ linux,pci-domain = <3>;
+ msi-map = <0x0 &its 0x98 0x1>,
+ <0x100 &its 0x99 0x7>;
+ msi-map-mask = <0x1ff>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ num-viewport = <8>;
+ interrupts = <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 366 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &gic 0 0 GIC_SPI 367 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &gic 0 0 GIC_SPI 368 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &gic 0 0 GIC_SPI 369 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>,
+ <&hsio_blk_ctl 0>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux", "ref";
+ assigned-clocks = <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
+ assigned-clock-parents = <0>, <0>,
+ <&scmi_clk IMX94_CLK_SYSPLL1_PFD1_DIV2>;
+ power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
+ fsl,max-link-speed = <3>;
+ status = "disabled";
+ };
+
+ pcie1_ep: pcie-ep@4c380000 {
+ compatible = "fsl,imx943-pcie-ep", "fsl,imx95-pcie-ep";
+ reg = <0 0x4c380000 0 0x10000>,
+ <0 0x4c3e0000 0 0x1000>,
+ <0 0x4c3a0000 0 0x1000>,
+ <0 0x4c3c0000 0 0x4000>,
+ <0 0x4c3f0000 0 0x10000>,
+ <0xa 0 1 0>;
+ reg-names = "dbi", "atu", "dbi2", "app", "dma", "addr_space";
+ num-lanes = <1>;
+ interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma";
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux";
+ assigned-clocks = <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
+ assigned-clock-parents = <0>, <0>,
+ <&scmi_clk IMX94_CLK_SYSPLL1_PFD1_DIV2>;
+ msi-map = <0x0 &its 0x98 0x1>;
+ power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
+ status = "disabled";
+ };
+ };
};
--
2.37.1
^ permalink raw reply related
* [PATCH v8 3/5] arm64: dts: imx94: Add pcie0 and pcie0-ep supports
From: Richard Zhu @ 2026-03-24 2:30 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, bhelgaas, frank.li, l.stach, lpieralisi,
kwilczynski, mani, s.hauer, kernel, festevam
Cc: linux-pci, linux-arm-kernel, devicetree, imx, linux-kernel,
Richard Zhu
In-Reply-To: <20260324023036.784466-1-hongxing.zhu@nxp.com>
Add pcie0 and pcie0-ep supports.
Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
---
arch/arm64/boot/dts/freescale/imx94.dtsi | 88 ++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx94.dtsi b/arch/arm64/boot/dts/freescale/imx94.dtsi
index 38488147a3ef..5d19c34cbc2b 100644
--- a/arch/arm64/boot/dts/freescale/imx94.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx94.dtsi
@@ -66,6 +66,13 @@ sai4_mclk: clock-sai4-mclk1 {
clock-output-names = "sai4_mclk";
};
+ clk_sys100m: clock-sys100m {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "clk_sys100m";
+ };
+
firmware {
scmi {
compatible = "arm,scmi";
@@ -1230,6 +1237,14 @@ wdog3: watchdog@49220000 {
};
};
+ hsio_blk_ctl: syscon@4c0100c0 {
+ compatible = "nxp,imx95-hsio-blk-ctl", "syscon";
+ reg = <0x0 0x4c0100c0 0x0 0x1>;
+ #clock-cells = <1>;
+ clocks = <&clk_sys100m>;
+ power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
+ };
+
usb3: usb@4c100000 {
compatible = "nxp,imx94-dwc3", "nxp,imx8mp-dwc3";
reg = <0x0 0x4c100000 0x0 0x10000>,
@@ -1284,6 +1299,79 @@ usbmisc: usbmisc@4c200200 {
#index-cells = <1>;
};
+ pcie0: pcie@4c300000 {
+ compatible = "fsl,imx94-pcie", "fsl,imx95-pcie";
+ reg = <0 0x4c300000 0 0x10000>,
+ <0 0x60100000 0 0xfe00000>,
+ <0 0x4c360000 0 0x10000>,
+ <0 0x4c340000 0 0x4000>;
+ reg-names = "dbi", "config", "atu", "app";
+ ranges = <0x81000000 0x0 0x00000000 0x0 0x6ff00000 0 0x00100000>,
+ <0x82000000 0x0 0x10000000 0x9 0x10000000 0 0x80000000>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ linux,pci-domain = <2>;
+ msi-map = <0x0 &its 0x10 0x1>,
+ <0x100 &its 0x11 0x7>;
+ msi-map-mask = <0x1ff>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ num-viewport = <8>;
+ interrupts = <GIC_SPI 364 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 365 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &gic 0 0 GIC_SPI 361 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &gic 0 0 GIC_SPI 362 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &gic 0 0 GIC_SPI 363 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>,
+ <&hsio_blk_ctl 0>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux", "ref";
+ assigned-clocks = <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
+ assigned-clock-parents = <0>, <0>,
+ <&scmi_clk IMX94_CLK_SYSPLL1_PFD1_DIV2>;
+ power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
+ fsl,max-link-speed = <3>;
+ status = "disabled";
+ };
+
+ pcie0_ep: pcie-ep@4c300000 {
+ compatible = "fsl,imx94-pcie-ep", "fsl,imx95-pcie-ep";
+ reg = <0 0x4c300000 0 0x10000>,
+ <0 0x4c360000 0 0x1000>,
+ <0 0x4c320000 0 0x1000>,
+ <0 0x4c340000 0 0x4000>,
+ <0 0x4c370000 0 0x10000>,
+ <0x9 0 1 0>;
+ reg-names = "dbi", "atu", "dbi2", "app", "dma", "addr_space";
+ num-lanes = <1>;
+ interrupts = <GIC_SPI 365 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma";
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux";
+ assigned-clocks = <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>;
+ assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
+ assigned-clock-parents = <0>, <0>,
+ <&scmi_clk IMX94_CLK_SYSPLL1_PFD1_DIV2>;
+ msi-map = <0x0 &its 0x10 0x1>;
+ power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
+ status = "disabled";
+ };
+
netc_blk_ctrl: system-controller@4ceb0000 {
compatible = "nxp,imx94-netc-blk-ctrl";
reg = <0x0 0x4ceb0000 0x0 0x10000>,
--
2.37.1
^ permalink raw reply related
* [PATCH v8 5/5] arm64: dts: imx943-evk: Add pcie[0,1] and pcie-ep[0,1] support
From: Richard Zhu @ 2026-03-24 2:30 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, bhelgaas, frank.li, l.stach, lpieralisi,
kwilczynski, mani, s.hauer, kernel, festevam
Cc: linux-pci, linux-arm-kernel, devicetree, imx, linux-kernel,
Richard Zhu
In-Reply-To: <20260324023036.784466-1-hongxing.zhu@nxp.com>
Add pcie[0,1] and pcie-ep[0,1] support.
Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
---
arch/arm64/boot/dts/freescale/Makefile | 4 +
arch/arm64/boot/dts/freescale/imx943-evk.dts | 83 ++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
index bae24b53bce6..1366e25a9efd 100644
--- a/arch/arm64/boot/dts/freescale/Makefile
+++ b/arch/arm64/boot/dts/freescale/Makefile
@@ -465,6 +465,10 @@ dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk-sof.dtb
dtb-$(CONFIG_ARCH_MXC) += imx95-toradex-smarc-dev.dtb
dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596sa-mb-smarc-2.dtb
+imx943-evk-pcie0-ep-dtbs += imx943-evk.dtb imx-pcie0-ep.dtbo
+imx943-evk-pcie1-ep-dtbs += imx943-evk.dtb imx-pcie1-ep.dtbo
+dtb-$(CONFIG_ARCH_MXC) += imx943-evk-pcie0-ep.dtb imx943-evk-pcie1-ep.dtb
+
imx95-15x15-evk-pcie0-ep-dtbs = imx95-15x15-evk.dtb imx-pcie0-ep.dtbo
dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-pcie0-ep.dtb
imx95-19x19-evk-pcie0-ep-dtbs += imx95-19x19-evk.dtb imx-pcie0-ep.dtbo
diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts
index 1d834379f602..ea0100b37d5c 100644
--- a/arch/arm64/boot/dts/freescale/imx943-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts
@@ -51,6 +51,20 @@ chosen {
stdout-path = &lpuart1;
};
+ pcie_ref_clk: clock-pcie-ref {
+ compatible = "gpio-gate-clock";
+ clocks = <&xtal25m>;
+ #clock-cells = <0>;
+ enable-gpios = <&pca9670_i2c3 7 GPIO_ACTIVE_LOW>;
+ };
+
+ xtal25m: clock-xtal25m {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "xtal_25MHz";
+ };
+
dmic: dmic {
compatible = "dmic-codec";
#sound-dai-cells = <0>;
@@ -72,6 +86,15 @@ reg_m2_pwr: regulator-m2-pwr {
startup-delay-us = <5000>;
};
+ reg_slot_pwr: regulator-slot-pwr {
+ compatible = "regulator-fixed";
+ regulator-name = "PCIe slot-power";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&pcal6416_i2c3_u46 0 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
reg_m2_wlan: regulator-wlan {
compatible = "regulator-fixed";
regulator-name = "WLAN_EN";
@@ -704,6 +727,18 @@ IMX94_PAD_GPIO_IO28__LPI2C6_SCL 0x40000b9e
>;
};
+ pinctrl_pcie0: pcie0grp {
+ fsl,pins = <
+ IMX94_PAD_GPIO_IO20__PCIE1_CLKREQ_B 0x4000031e
+ >;
+ };
+
+ pinctrl_pcie1: pcie1grp {
+ fsl,pins = <
+ IMX94_PAD_GPIO_IO23__PCIE2_CLKREQ_B 0x4000031e
+ >;
+ };
+
pinctrl_pdm: pdmgrp {
fsl,pins = <
IMX94_PAD_PDM_CLK__PDM_CLK 0x31e
@@ -878,6 +913,54 @@ IMX94_PAD_XSPI1_DQS__XSPI1_A_DQS 0x3fe
};
};
+&pcie0 {
+ pinctrl-0 = <&pinctrl_pcie0>;
+ pinctrl-names = "default";
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>,
+ <&hsio_blk_ctl 0>,
+ <&pcie_ref_clk>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux",
+ "ref", "extref";
+ reset-gpio = <&pcal6416_i2c3_u46 3 GPIO_ACTIVE_LOW>;
+ vpcie3v3aux-supply = <®_m2_wlan>;
+ supports-clkreq;
+ status = "okay";
+};
+
+&pcie0_ep {
+ pinctrl-0 = <&pinctrl_pcie0>;
+ pinctrl-names = "default";
+ vpcie-supply = <®_m2_wlan>;
+ status = "disabled";
+};
+
+&pcie1 {
+ pinctrl-0 = <&pinctrl_pcie1>;
+ pinctrl-names = "default";
+ clocks = <&scmi_clk IMX94_CLK_HSIO>,
+ <&scmi_clk IMX94_CLK_HSIOPLL>,
+ <&scmi_clk IMX94_CLK_HSIOPLL_VCO>,
+ <&scmi_clk IMX94_CLK_HSIOPCIEAUX>,
+ <&hsio_blk_ctl 0>,
+ <&pcie_ref_clk>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux",
+ "ref", "extref";
+ reset-gpio = <&pcal6416_i2c3_u46 1 GPIO_ACTIVE_LOW>;
+ vpcie3v3aux-supply = <®_slot_pwr>;
+ supports-clkreq;
+ status = "okay";
+};
+
+&pcie1_ep {
+ pinctrl-0 = <&pinctrl_pcie1>;
+ pinctrl-names = "default";
+ vpcie-supply = <®_slot_pwr>;
+ status = "disabled";
+};
+
&usb2 {
dr_mode = "otg";
disable-over-current;
--
2.37.1
^ permalink raw reply related
* RE: [PATCH v5 phy-next 09/27] scsi: ufs: exynos: stop poking into struct phy guts
From: Alim Akhtar @ 2026-03-23 17:26 UTC (permalink / raw)
To: 'Vladimir Oltean', linux-phy
Cc: 'Vinod Koul', 'Neil Armstrong', dri-devel,
freedreno, linux-arm-kernel, linux-arm-msm, linux-can, linux-gpio,
linux-ide, linux-kernel, linux-media, linux-pci,
linux-renesas-soc, linux-riscv, linux-rockchip, linux-samsung-soc,
linux-scsi, linux-sunxi, linux-tegra, linux-usb, netdev, spacemit,
UNGLinuxDriver, 'Bart Van Assche',
'Peter Griffin', 'James E.J. Bottomley',
'Martin K. Petersen', 'Krzysztof Kozlowski',
'Chanho Park'
In-Reply-To: <20260319223241.1351137-10-vladimir.oltean@nxp.com>
HI Vladimir,
> -----Original Message-----
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> Sent: Friday, March 20, 2026 4:02 AM
> To: linux-phy@lists.infradead.org
> Cc: Vinod Koul <vkoul@kernel.org>; Neil Armstrong
> <neil.armstrong@linaro.org>; dri-devel@lists.freedesktop.org;
> freedreno@lists.freedesktop.org; linux-arm-kernel@lists.infradead.org;
> linux-arm-msm@vger.kernel.org; linux-can@vger.kernel.org; linux-
> gpio@vger.kernel.org; linux-ide@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-
> pci@vger.kernel.org; linux-renesas-soc@vger.kernel.org; linux-
> riscv@lists.infradead.org; linux-rockchip@lists.infradead.org;
linux-samsung-
> soc@vger.kernel.org; linux-scsi@vger.kernel.org;
linux-sunxi@lists.linux.dev;
> linux-tegra@vger.kernel.org; linux-usb@vger.kernel.org;
> netdev@vger.kernel.org; spacemit@lists.linux.dev;
> UNGLinuxDriver@microchip.com; Bart Van Assche <bvanassche@acm.org>;
> Alim Akhtar <alim.akhtar@samsung.com>; Peter Griffin
> <peter.griffin@linaro.org>; James E.J. Bottomley
> <James.Bottomley@HansenPartnership.com>; Martin K. Petersen
> <martin.petersen@oracle.com>; Krzysztof Kozlowski <krzk@kernel.org>;
> Chanho Park <chanho61.park@samsung.com>
> Subject: [PATCH v5 phy-next 09/27] scsi: ufs: exynos: stop poking into
struct
> phy guts
>
> The Exynos host controller driver is clearly a PHY consumer (gets the
> ufs->phy using devm_phy_get()), but pokes into the guts of struct phy
> to get the generic_phy->power_count.
>
> The UFS core (specifically ufshcd_link_startup()) may call the variant
> operation exynos_ufs_pre_link() -> exynos_ufs_phy_init() multiple times if
> the link startup fails and needs to be retried.
>
> However ufs-exynos shouldn't be doing what it's doing, i.e. looking at the
> generic_phy->power_count, because in the general sense of the API, a
> single Generic PHY may have multiple consumers. If ufs-exynos looks at
> generic_phy->power_count, there's no guarantee that this ufs-exynos
> instance is the one who previously bumped that power count. So it may be
> powering down the PHY on behalf of another consumer.
>
> The correct way in which this should be handled is ufs-exynos should
> *remember* whether it has initialized and powered up the PHY before, and
> power it down during link retries. Not rely on the power_count (which,
btw,
> on the writer side is modified under &phy->mutex, but on the reader side
is
> accessed unlocked). This is a discouraged pattern even if here it doesn't
> cause functional problems.
>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Reviewed-by: Bart Van Assche <bvanassche@acm.org>
> ---
Thanks for the patch
Acked-by: Alim Akhtar <alim.akhtar@samsung.com>
Tested this patch for basic UFS functionality, UFS still works.
Feel free to add
Tested-by: Alim Akhtar <alim.akhtar@samsung.com>
^ permalink raw reply
* Re: [PATCH v1 2/3] arm64: mm: Handle invalid large leaf mappings correctly
From: Jinjiang Tu @ 2026-03-24 2:30 UTC (permalink / raw)
To: Ryan Roberts, Catalin Marinas, Will Deacon,
David Hildenbrand (Arm), Dev Jain, Yang Shi, Suzuki K Poulose,
Kevin Brodsky
Cc: linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260323130317.1737522-3-ryan.roberts@arm.com>
在 2026/3/23 21:03, Ryan Roberts 写道:
> It has been possible for a long time to mark ptes in the linear map as
> invalid. This is done for secretmem, kfence, realm dma memory un/share,
> and others, by simply clearing the PTE_VALID bit. But until commit
> a166563e7ec37 ("arm64: mm: support large block mapping when
> rodata=full") large leaf mappings were never made invalid in this way.
>
> It turns out various parts of the code base are not equipped to handle
> invalid large leaf mappings (in the way they are currently encoded) and
> I've observed a kernel panic while booting a realm guest on a
> BBML2_NOABORT system as a result:
>
> [ 15.432706] software IO TLB: Memory encryption is active and system is using DMA bounce buffers
> [ 15.476896] Unable to handle kernel paging request at virtual address ffff000019600000
> [ 15.513762] Mem abort info:
> [ 15.527245] ESR = 0x0000000096000046
> [ 15.548553] EC = 0x25: DABT (current EL), IL = 32 bits
> [ 15.572146] SET = 0, FnV = 0
> [ 15.592141] EA = 0, S1PTW = 0
> [ 15.612694] FSC = 0x06: level 2 translation fault
> [ 15.640644] Data abort info:
> [ 15.661983] ISV = 0, ISS = 0x00000046, ISS2 = 0x00000000
> [ 15.694875] CM = 0, WnR = 1, TnD = 0, TagAccess = 0
> [ 15.723740] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
> [ 15.755776] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000081f3f000
> [ 15.800410] [ffff000019600000] pgd=0000000000000000, p4d=180000009ffff403, pud=180000009fffe403, pmd=00e8000199600704
> [ 15.855046] Internal error: Oops: 0000000096000046 [#1] SMP
> [ 15.886394] Modules linked in:
> [ 15.900029] CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.0.0-rc4-dirty #4 PREEMPT
> [ 15.935258] Hardware name: linux,dummy-virt (DT)
> [ 15.955612] pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
> [ 15.986009] pc : __pi_memcpy_generic+0x128/0x22c
> [ 16.006163] lr : swiotlb_bounce+0xf4/0x158
> [ 16.024145] sp : ffff80008000b8f0
> [ 16.038896] x29: ffff80008000b8f0 x28: 0000000000000000 x27: 0000000000000000
> [ 16.069953] x26: ffffb3976d261ba8 x25: 0000000000000000 x24: ffff000019600000
> [ 16.100876] x23: 0000000000000001 x22: ffff0000043430d0 x21: 0000000000007ff0
> [ 16.131946] x20: 0000000084570010 x19: 0000000000000000 x18: ffff00001ffe3fcc
> [ 16.163073] x17: 0000000000000000 x16: 00000000003fffff x15: 646e612065766974
> [ 16.194131] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
> [ 16.225059] x11: 0000000000000000 x10: 0000000000000010 x9 : 0000000000000018
> [ 16.256113] x8 : 0000000000000018 x7 : 0000000000000000 x6 : 0000000000000000
> [ 16.287203] x5 : ffff000019607ff0 x4 : ffff000004578000 x3 : ffff000019600000
> [ 16.318145] x2 : 0000000000007ff0 x1 : ffff000004570010 x0 : ffff000019600000
> [ 16.349071] Call trace:
> [ 16.360143] __pi_memcpy_generic+0x128/0x22c (P)
> [ 16.380310] swiotlb_tbl_map_single+0x154/0x2b4
> [ 16.400282] swiotlb_map+0x5c/0x228
> [ 16.415984] dma_map_phys+0x244/0x2b8
> [ 16.432199] dma_map_page_attrs+0x44/0x58
> [ 16.449782] virtqueue_map_page_attrs+0x38/0x44
> [ 16.469596] virtqueue_map_single_attrs+0xc0/0x130
> [ 16.490509] virtnet_rq_alloc.isra.0+0xa4/0x1fc
> [ 16.510355] try_fill_recv+0x2a4/0x584
> [ 16.526989] virtnet_open+0xd4/0x238
> [ 16.542775] __dev_open+0x110/0x24c
> [ 16.558280] __dev_change_flags+0x194/0x20c
> [ 16.576879] netif_change_flags+0x24/0x6c
> [ 16.594489] dev_change_flags+0x48/0x7c
> [ 16.611462] ip_auto_config+0x258/0x1114
> [ 16.628727] do_one_initcall+0x80/0x1c8
> [ 16.645590] kernel_init_freeable+0x208/0x2f0
> [ 16.664917] kernel_init+0x24/0x1e0
> [ 16.680295] ret_from_fork+0x10/0x20
> [ 16.696369] Code: 927cec03 cb0e0021 8b0e0042 a9411c26 (a900340c)
> [ 16.723106] ---[ end trace 0000000000000000 ]---
> [ 16.752866] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
> [ 16.792556] Kernel Offset: 0x3396ea200000 from 0xffff800080000000
> [ 16.818966] PHYS_OFFSET: 0xfff1000080000000
> [ 16.837237] CPU features: 0x0000000,00060005,13e38581,957e772f
> [ 16.862904] Memory Limit: none
> [ 16.876526] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
>
> This panic occurs because the swiotlb memory was previously shared to
> the host (__set_memory_enc_dec()), which involves transitioning the
> (large) leaf mappings to invalid, sharing to the host, then marking the
> mappings valid again. But pageattr_p[mu]d_entry() would only update the
> entry if it is a section mapping, since otherwise it concluded it must
> be a table entry so shouldn't be modified. But p[mu]d_sect() only
> returns true if the entry is valid. So the result was that the large
Maybe I missed something, pmd_sect() only checks PMD_TYPE_SECT, doesn't check PTE_VALID?
Why it only returns true if the entry is valid?
#define pmd_sect(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \
PMD_TYPE_SECT)
> leaf entry was made invalid in the first pass then ignored in the second
> pass. It remains invalid until the above code tries to access it and
> blows up.
>
> The simple fix would be to update pageattr_pmd_entry() to use
> !pmd_table() instead of pmd_sect(). That would solve this problem.
>
> But the ptdump code also suffers from a similar issue. It checks
> pmd_leaf() and doesn't call into the arch-specific note_page() machinery
> if it returns false. As a result of this, ptdump wasn't even able to
> show the invalid large leaf mappings; it looked like they were valid
> which made this super fun to debug. the ptdump code is core-mm and
> pmd_table() is arm64-specific so we can't use the same trick to solve
> that.
>
> But we already support the concept of "present-invalid" for user space
> entries. And even better, pmd_leaf() will return true for a leaf mapping
> that is marked present-invalid. So let's just use that encoding for
> present-invalid kernel mappings too. Then we can use pmd_leaf() where we
> previously used pmd_sect() and everything is magically fixed.
>
> Additionally, from inspection kernel_page_present() was broken in a
> similar way, so I'm also updating that to use pmd_leaf().
>
> I haven't spotted any other issues of this shape but plan to do a follow
> up patch to remove pmd_sect() and pud_sect() in favour of the more
> sophisticated pmd_leaf()/pud_leaf() which are core-mm APIs and will
> simplify arm64 code a bit.
>
> Fixes: a166563e7ec37 ("arm64: mm: support large block mapping when rodata=full")
> Cc: stable@vger.kernel.org
> Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
> ---
> arch/arm64/mm/pageattr.c | 50 ++++++++++++++++++++++------------------
> 1 file changed, 28 insertions(+), 22 deletions(-)
>
> diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
> index 358d1dc9a576f..87dfe4c82fa92 100644
> --- a/arch/arm64/mm/pageattr.c
> +++ b/arch/arm64/mm/pageattr.c
> @@ -25,6 +25,11 @@ static ptdesc_t set_pageattr_masks(ptdesc_t val, struct mm_walk *walk)
> {
> struct page_change_data *masks = walk->private;
>
> + /*
> + * Some users clear and set bits which alias eachother (e.g. PTE_NG and
> + * PTE_PRESENT_INVALID). It is therefore important that we always clear
> + * first then set.
> + */
> val &= ~(pgprot_val(masks->clear_mask));
> val |= (pgprot_val(masks->set_mask));
>
> @@ -36,7 +41,7 @@ static int pageattr_pud_entry(pud_t *pud, unsigned long addr,
> {
> pud_t val = pudp_get(pud);
>
> - if (pud_sect(val)) {
> + if (pud_leaf(val)) {
> if (WARN_ON_ONCE((next - addr) != PUD_SIZE))
> return -EINVAL;
> val = __pud(set_pageattr_masks(pud_val(val), walk));
> @@ -52,7 +57,7 @@ static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr,
> {
> pmd_t val = pmdp_get(pmd);
>
> - if (pmd_sect(val)) {
> + if (pmd_leaf(val)) {
> if (WARN_ON_ONCE((next - addr) != PMD_SIZE))
> return -EINVAL;
> val = __pmd(set_pageattr_masks(pmd_val(val), walk));
> @@ -132,11 +137,12 @@ static int __change_memory_common(unsigned long start, unsigned long size,
> ret = update_range_prot(start, size, set_mask, clear_mask);
>
> /*
> - * If the memory is being made valid without changing any other bits
> - * then a TLBI isn't required as a non-valid entry cannot be cached in
> - * the TLB.
> + * If the memory is being switched from present-invalid to valid without
> + * changing any other bits then a TLBI isn't required as a non-valid
> + * entry cannot be cached in the TLB.
> */
> - if (pgprot_val(set_mask) != PTE_VALID || pgprot_val(clear_mask))
> + if (pgprot_val(set_mask) != (PTE_MAYBE_NG | PTE_VALID) ||
> + pgprot_val(clear_mask) != PTE_PRESENT_INVALID)
> flush_tlb_kernel_range(start, start + size);
> return ret;
> }
> @@ -237,18 +243,18 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
> {
> if (enable)
> return __change_memory_common(addr, PAGE_SIZE * numpages,
> - __pgprot(PTE_VALID),
> - __pgprot(0));
> + __pgprot(PTE_MAYBE_NG | PTE_VALID),
> + __pgprot(PTE_PRESENT_INVALID));
> else
> return __change_memory_common(addr, PAGE_SIZE * numpages,
> - __pgprot(0),
> - __pgprot(PTE_VALID));
> + __pgprot(PTE_PRESENT_INVALID),
> + __pgprot(PTE_MAYBE_NG | PTE_VALID));
> }
>
> int set_direct_map_invalid_noflush(struct page *page)
> {
> - pgprot_t clear_mask = __pgprot(PTE_VALID);
> - pgprot_t set_mask = __pgprot(0);
> + pgprot_t clear_mask = __pgprot(PTE_MAYBE_NG | PTE_VALID);
> + pgprot_t set_mask = __pgprot(PTE_PRESENT_INVALID);
>
> if (!can_set_direct_map())
> return 0;
> @@ -259,8 +265,8 @@ int set_direct_map_invalid_noflush(struct page *page)
>
> int set_direct_map_default_noflush(struct page *page)
> {
> - pgprot_t set_mask = __pgprot(PTE_VALID | PTE_WRITE);
> - pgprot_t clear_mask = __pgprot(PTE_RDONLY);
> + pgprot_t set_mask = __pgprot(PTE_MAYBE_NG | PTE_VALID | PTE_WRITE);
> + pgprot_t clear_mask = __pgprot(PTE_PRESENT_INVALID | PTE_RDONLY);
>
> if (!can_set_direct_map())
> return 0;
> @@ -296,8 +302,8 @@ static int __set_memory_enc_dec(unsigned long addr,
> * entries or Synchronous External Aborts caused by RIPAS_EMPTY
> */
> ret = __change_memory_common(addr, PAGE_SIZE * numpages,
> - __pgprot(set_prot),
> - __pgprot(clear_prot | PTE_VALID));
> + __pgprot(set_prot | PTE_PRESENT_INVALID),
> + __pgprot(clear_prot | PTE_MAYBE_NG | PTE_VALID));
>
> if (ret)
> return ret;
> @@ -311,8 +317,8 @@ static int __set_memory_enc_dec(unsigned long addr,
> return ret;
>
> return __change_memory_common(addr, PAGE_SIZE * numpages,
> - __pgprot(PTE_VALID),
> - __pgprot(0));
> + __pgprot(PTE_MAYBE_NG | PTE_VALID),
> + __pgprot(PTE_PRESENT_INVALID));
> }
>
> static int realm_set_memory_encrypted(unsigned long addr, int numpages)
> @@ -404,15 +410,15 @@ bool kernel_page_present(struct page *page)
> pud = READ_ONCE(*pudp);
> if (pud_none(pud))
> return false;
> - if (pud_sect(pud))
> - return true;
> + if (pud_leaf(pud))
> + return pud_valid(pud);
>
> pmdp = pmd_offset(pudp, addr);
> pmd = READ_ONCE(*pmdp);
> if (pmd_none(pmd))
> return false;
> - if (pmd_sect(pmd))
> - return true;
> + if (pmd_leaf(pmd))
> + return pmd_valid(pmd);
>
> ptep = pte_offset_kernel(pmdp, addr);
> return pte_valid(__ptep_get(ptep));
^ permalink raw reply
* RE: [PATCH V2] arm64: dts: imx8mp-evk: Disable PCIe bus in the default dts
From: Sherry Sun @ 2026-03-24 2:32 UTC (permalink / raw)
To: Frank Li
Cc: s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Hongxing Zhu, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <acGN-QsB65Kbs7Gf@lizhi-Precision-Tower-5810>
> Subject: Re: [PATCH V2] arm64: dts: imx8mp-evk: Disable PCIe bus in the
> default dts
>
> On Fri, Mar 20, 2026 at 05:03:53PM +0800, Sherry Sun wrote:
> > Disable the PCIe bus in the default device tree to avoid shared
> > regulator conflicts between SDIO and PCIe buses. The non-deterministic
> > probe order between these two buses can break the PCIe initialization
> > sequence, causing PCIe devices to fail detection intermittently.
> >
> > On i.MX8MP EVK board, the M.2 connector is physically wired to both
> > USDHC1 and PCIe0, however the out-of-box module is SDIO IW612 WiFi, so
> > enable the SDIO WiFi in the default imx8mp-evk.dts, and provide a
> > separate device tree overlay (imx8mp-evk-pcie.dtso) to enable the PCIe
> > bus when needed.
> >
> > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > ---
> > Chanegs in V2:
> > 1. Improve the commit message to clarify SDIO WiFi is the out-of-box
> module on
> > i.MX8MP EVK board.
> > ---
> > arch/arm64/boot/dts/freescale/Makefile | 4 +++-
> > .../boot/dts/freescale/imx8mp-evk-pcie.dtso | 19 +++++++++++++++++++
> > arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 4 ++--
> > 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644
> > arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> >
> > diff --git a/arch/arm64/boot/dts/freescale/Makefile
> > b/arch/arm64/boot/dts/freescale/Makefile
> > index 780682258e71..107ca270ef32 100644
> > --- a/arch/arm64/boot/dts/freescale/Makefile
> > +++ b/arch/arm64/boot/dts/freescale/Makefile
> > @@ -333,12 +333,14 @@ imx8mp-evk-lvds0-imx-lvds-hdmi-dtbs +=
> > imx8mp-evk.dtb imx8mp-evk-lvds0-imx-lvds-
> > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0-dtbs += imx8mp-evk.dtb
> > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0.dtbo
> > imx8mp-evk-lvds1-imx-lvds-hdmi-dtbs += imx8mp-evk.dtb
> > imx8mp-evk-lvds1-imx-lvds-hdmi.dtbo
> > imx8mp-evk-mx8-dlvds-lcd1-dtbs += imx8mp-evk.dtb
> > imx8mp-evk-mx8-dlvds-lcd1.dtbo -imx8mp-evk-pcie-ep-dtbs +=
> > imx8mp-evk.dtb imx-pcie0-ep.dtbo
> > +imx8mp-evk-pcie-dtbs := imx8mp-evk.dtb imx8mp-evk-pcie.dtbo
> > +imx8mp-evk-pcie-ep-dtbs += imx8mp-evk-pcie.dtb imx-pcie0-ep.dtbo
> > dtb-$(CONFIG_ARCH_MXC) +=
> > imx8mp-evk-lvds0-imx-dlvds-hdmi-channel0.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-lvds0-imx-lvds-hdmi.dtb
> > dtb-$(CONFIG_ARCH_MXC) +=
> > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-lvds1-imx-lvds-hdmi.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-mx8-dlvds-lcd1.dtb
> > +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-pcie.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-pcie-ep.dtb
> >
> > imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33-dtbs +=
> > imx8mp-tqma8mpql-mba8mpxl.dtb
> > imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo
> > diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > b/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > new file mode 100644
> > index 000000000000..4f6546d442bf
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > @@ -0,0 +1,19 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Copyright 2026 NXP
> > + */
> > +
> > +/dts-v1/;
> > +/plugin/;
> > +
> > +&pcie_phy {
> > + status = "okay";
> > +};
> > +
> > +&pcie0 {
> > + status = "okay";
> > +};
> > +
> > +&usdhc1 {
> > + status = "disabled";
> > +};
>
> Please use one overlay for both imx95 and imx8mp to enable pcie0 and
> disable usdhc1.
Hi Frank,
imx8mp has a specific pcie_phy, I disabled it in the default imx8mp-evk.dts
and enabled it in xxx-pcie.dtso, if we use one overlay for both imx95 and imx8mp,
is it acceptable that we need to keep the 8mp pcie_phy enabled even in non-PCIe
usage scenarios?
Best Regards
Sherry
>
> Frank
>
> > diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
> > b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
> > index aedc09937716..f09335e6388d 100644
> > --- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
> > +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
> > @@ -763,7 +763,7 @@ &pcie_phy {
> > fsl,refclk-pad-mode = <IMX8_PCIE_REFCLK_PAD_INPUT>;
> > clocks = <&pcie0_refclk>;
> > clock-names = "ref";
> > - status = "okay";
> > + status = "disabled";
> > };
> >
> > &pcie0 {
> > @@ -773,7 +773,7 @@ &pcie0 {
> > vpcie-supply = <®_pcie0>;
> > vpcie3v3aux-supply = <®_pcie0>;
> > supports-clkreq;
> > - status = "okay";
> > + status = "disabled";
> > };
> >
> > &pcie0_ep {
> > --
> > 2.37.1
> >
^ permalink raw reply
* RE: [PATCH V2] arm64: dts: imx8mp-evk: Disable PCIe bus in the default dts
From: Sherry Sun @ 2026-03-24 2:45 UTC (permalink / raw)
To: Frank Li
Cc: s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Hongxing Zhu, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <VI0PR04MB121147DEDEA2B8058666867F19248A@VI0PR04MB12114.eurprd04.prod.outlook.com>
> > Subject: Re: [PATCH V2] arm64: dts: imx8mp-evk: Disable PCIe bus in
> > the default dts
> >
> > On Fri, Mar 20, 2026 at 05:03:53PM +0800, Sherry Sun wrote:
> > > Disable the PCIe bus in the default device tree to avoid shared
> > > regulator conflicts between SDIO and PCIe buses. The
> > > non-deterministic probe order between these two buses can break the
> > > PCIe initialization sequence, causing PCIe devices to fail detection
> intermittently.
> > >
> > > On i.MX8MP EVK board, the M.2 connector is physically wired to both
> > > USDHC1 and PCIe0, however the out-of-box module is SDIO IW612 WiFi,
> > > so enable the SDIO WiFi in the default imx8mp-evk.dts, and provide a
> > > separate device tree overlay (imx8mp-evk-pcie.dtso) to enable the
> > > PCIe bus when needed.
> > >
> > > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > > ---
> > > Chanegs in V2:
> > > 1. Improve the commit message to clarify SDIO WiFi is the out-of-box
> > module on
> > > i.MX8MP EVK board.
> > > ---
> > > arch/arm64/boot/dts/freescale/Makefile | 4 +++-
> > > .../boot/dts/freescale/imx8mp-evk-pcie.dtso | 19
> +++++++++++++++++++
> > > arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 4 ++--
> > > 3 files changed, 24 insertions(+), 3 deletions(-) create mode
> > > 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > >
> > > diff --git a/arch/arm64/boot/dts/freescale/Makefile
> > > b/arch/arm64/boot/dts/freescale/Makefile
> > > index 780682258e71..107ca270ef32 100644
> > > --- a/arch/arm64/boot/dts/freescale/Makefile
> > > +++ b/arch/arm64/boot/dts/freescale/Makefile
> > > @@ -333,12 +333,14 @@ imx8mp-evk-lvds0-imx-lvds-hdmi-dtbs +=
> > > imx8mp-evk.dtb imx8mp-evk-lvds0-imx-lvds-
> > > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0-dtbs += imx8mp-evk.dtb
> > > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0.dtbo
> > > imx8mp-evk-lvds1-imx-lvds-hdmi-dtbs += imx8mp-evk.dtb
> > > imx8mp-evk-lvds1-imx-lvds-hdmi.dtbo
> > > imx8mp-evk-mx8-dlvds-lcd1-dtbs += imx8mp-evk.dtb
> > > imx8mp-evk-mx8-dlvds-lcd1.dtbo -imx8mp-evk-pcie-ep-dtbs +=
> > > imx8mp-evk.dtb imx-pcie0-ep.dtbo
> > > +imx8mp-evk-pcie-dtbs := imx8mp-evk.dtb imx8mp-evk-pcie.dtbo
> > > +imx8mp-evk-pcie-ep-dtbs += imx8mp-evk-pcie.dtb imx-pcie0-ep.dtbo
> > > dtb-$(CONFIG_ARCH_MXC) +=
> > > imx8mp-evk-lvds0-imx-dlvds-hdmi-channel0.dtb
> > > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-lvds0-imx-lvds-hdmi.dtb
> > > dtb-$(CONFIG_ARCH_MXC) +=
> > > imx8mp-evk-lvds1-imx-dlvds-hdmi-channel0.dtb
> > > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-lvds1-imx-lvds-hdmi.dtb
> > > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-mx8-dlvds-lcd1.dtb
> > > +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-pcie.dtb
> > > dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-pcie-ep.dtb
> > >
> > > imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33-dtbs +=
> > > imx8mp-tqma8mpql-mba8mpxl.dtb
> > > imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo
> > > diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > > b/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > > new file mode 100644
> > > index 000000000000..4f6546d442bf
> > > --- /dev/null
> > > +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-pcie.dtso
> > > @@ -0,0 +1,19 @@
> > > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > > +/*
> > > + * Copyright 2026 NXP
> > > + */
> > > +
> > > +/dts-v1/;
> > > +/plugin/;
> > > +
> > > +&pcie_phy {
> > > + status = "okay";
> > > +};
> > > +
> > > +&pcie0 {
> > > + status = "okay";
> > > +};
> > > +
> > > +&usdhc1 {
> > > + status = "disabled";
> > > +};
> >
> > Please use one overlay for both imx95 and imx8mp to enable pcie0 and
> > disable usdhc1.
>
> Hi Frank,
>
> imx8mp has a specific pcie_phy, I disabled it in the default imx8mp-evk.dts
> and enabled it in xxx-pcie.dtso, if we use one overlay for both imx95 and
> imx8mp, is it acceptable that we need to keep the 8mp pcie_phy enabled
> even in non-PCIe usage scenarios?
>
In addition, another difference between imx8mp and imx95 is that imx8mp connects usdhc1 to the M.2 connector, while imx95 connects usdhc3 to the M.2 connector. The usdhc interfaces are different, so we cannot use the same DTSO file.
Best Regards
Sherry
^ permalink raw reply
* [PATCH 0/2] *** SUBJECT HERE ***
From: Meiker Gao @ 2026-03-24 2:47 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno
Cc: Bayi Cheng, Project_Global_Chrome_Upstream_Group, sirius.wang,
vince-wl.liu, jh.hsu, linux-spi, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Meiker Gao
*** BLURB HERE ***
This patch series refactors the clock management for the Mediatek SPI NOR driver,
making it more flexible and compatible with different platforms.
Patch 1 updates the device tree binding to support variable clock-names.
Patch 2 migrates the driver to use clk_bulk API and optimizes error handling.
Changes in v1:
- Dynamically allocate clk_bulk_data array.
- Simplify error handling.
- Code cleanup and style improvements.
Meiker Gao (2):
dt-bindings: spi: Fix clock-names definition
[v3] spi: spi-mtk-nor: Modify and optimization the SNFC.
.../bindings/spi/mediatek,spi-mtk-nor.yaml | 6 ++++++
drivers/spi/spi-mtk-nor.c | 19 ++++++++-----------
2 files changed, 14 insertions(+), 11 deletions(-)
--
2.45.2
^ permalink raw reply
* [PATCH v2] tty: atmel_serial: update outdated reference to atmel_tasklet_func()
From: Kexin Sun @ 2026-03-24 2:48 UTC (permalink / raw)
To: gregkh, jirislaby
Cc: nicolas.ferre, alexandre.belloni, claudiu.beznea, richard.genoud,
linux-kernel, linux-serial, linux-arm-kernel, julia.lawall,
xutong.ma, yunbolyu, ratnadiraw, Kexin Sun
In-Reply-To: <3bd295ee-1607-49db-a12e-5a4d4fc81c5c@bootlin.com>
The modem-status comparison that used irq_status_prev was
moved from atmel_tasklet_func() into atmel_handle_status() in
commit d033e82db9a5 ("tty/serial: at91: handle IRQ status
more safely"). Update the comment accordingly.
Assisted-by: unnamed:deepseek-v3.2 coccinelle
Signed-off-by: Kexin Sun <kexinsun@smail.nju.edu.cn>
---
drivers/tty/serial/atmel_serial.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 08dd8f887956..5d8c1cfc1c60 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1927,7 +1927,7 @@ static int atmel_startup(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_FMR, fmr);
}
- /* Save current CSR for comparison in atmel_tasklet_func() */
+ /* Save current CSR for comparison in atmel_handle_status() */
atmel_port->irq_status_prev = atmel_uart_readl(port, ATMEL_US_CSR);
/*
--
2.25.1
^ permalink raw reply related
* [PATCH v5 04/10] clk: realtek: Introduce a common probe()
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add rtk_clk_probe() to set up the shared regmap, register clock hardware,
add the clock provider, and optionally register a reset controller when
reset bank data is provided.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Synchronized with reset controller changes.
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/realtek/Kconfig | 28 +++++++++++++++++
drivers/clk/realtek/Makefile | 4 +++
drivers/clk/realtek/common.c | 59 ++++++++++++++++++++++++++++++++++++
drivers/clk/realtek/common.h | 43 ++++++++++++++++++++++++++
7 files changed, 137 insertions(+)
create mode 100644 drivers/clk/realtek/Kconfig
create mode 100644 drivers/clk/realtek/Makefile
create mode 100644 drivers/clk/realtek/common.c
create mode 100644 drivers/clk/realtek/common.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 9419b0497e0b..31f4a5a66394 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22229,6 +22229,7 @@ L: devicetree@vger.kernel.org
L: linux-clk@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/clock/realtek*
+F: drivers/clk/realtek/*
F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3d803b4cf5c1..d60f6415b0a3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -519,6 +519,7 @@ source "drivers/clk/nuvoton/Kconfig"
source "drivers/clk/pistachio/Kconfig"
source "drivers/clk/qcom/Kconfig"
source "drivers/clk/ralink/Kconfig"
+source "drivers/clk/realtek/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f7bce3951a30..69b84d1e7bcc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-y += ralink/
+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
new file mode 100644
index 000000000000..63eb3fb3e79a
--- /dev/null
+++ b/drivers/clk/realtek/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_CLK_REALTEK
+ bool "Clock driver for Realtek SoCs"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ default y
+ help
+ Enable the common clock framework infrastructure for Realtek
+ system-on-chip platforms.
+
+ This provides the base support required by individual Realtek
+ clock controller drivers to expose clocks to peripheral devices.
+
+ If you have a Realtek-based platform, say Y.
+
+if COMMON_CLK_REALTEK
+
+config RTK_CLK_COMMON
+ tristate "Realtek Clock Common"
+ depends on RESET_CONTROLLER
+ select RESET_RTK_COMMON
+ help
+ Common helper code shared by Realtek clock controller drivers.
+
+ This provides utility functions and data structures used by
+ multiple Realtek clock implementations, and include integration
+ with reset controllers where required.
+
+endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
new file mode 100644
index 000000000000..377ec776ee47
--- /dev/null
+++ b/drivers/clk/realtek/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
+
+clk-rtk-y += common.o
diff --git a/drivers/clk/realtek/common.c b/drivers/clk/realtek/common.c
new file mode 100644
index 000000000000..18a0d82ded0c
--- /dev/null
+++ b/drivers/clk/realtek/common.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include "common.h"
+
+int rtk_clk_probe(struct platform_device *pdev, const struct rtk_clk_desc *desc)
+{
+ int i, ret;
+ struct regmap *regmap;
+ struct device *dev = &pdev->dev;
+ struct rtk_reset_initdata reset_initdata = {0};
+
+ regmap = device_node_to_regmap(pdev->dev.of_node);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "failed to get regmap\n");
+
+ for (i = 0; i < desc->num_clks; i++)
+ desc->clks[i]->regmap = regmap;
+
+ for (i = 0; i < desc->clk_data->num; i++) {
+ struct clk_hw *hw = desc->clk_data->hws[i];
+
+ if (!hw)
+ continue;
+
+ ret = devm_clk_hw_register(dev, hw);
+
+ if (ret) {
+ dev_warn(dev, "failed to register hw of clk%d: %d\n", i,
+ ret);
+ desc->clk_data->hws[i] = NULL;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ desc->clk_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add clock provider\n");
+
+ if (!desc->num_reset_descs)
+ return 0;
+
+ reset_initdata.regmap = regmap;
+ reset_initdata.num_descs = desc->num_reset_descs;
+ reset_initdata.descs = desc->reset_descs;
+
+ return rtk_reset_controller_add(dev, &reset_initdata);
+}
+EXPORT_SYMBOL_GPL(rtk_clk_probe);
+
+MODULE_DESCRIPTION("Realtek clock infrastructure");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/realtek/common.h b/drivers/clk/realtek/common.h
new file mode 100644
index 000000000000..9cf013f9ba2b
--- /dev/null
+++ b/drivers/clk/realtek/common.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_COMMON_H
+#define __CLK_REALTEK_COMMON_H
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset/realtek.h>
+
+#define __clk_regmap_hw(_p) ((_p)->hw)
+
+struct device;
+struct platform_device;
+
+struct clk_regmap {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+struct rtk_clk_desc {
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_regmap **clks;
+ size_t num_clks;
+ struct rtk_reset_desc *reset_descs;
+ size_t num_reset_descs;
+};
+
+static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw)
+{
+ return container_of(hw, struct clk_regmap, hw);
+}
+
+int rtk_clk_probe(struct platform_device *pdev,
+ const struct rtk_clk_desc *desc);
+
+#endif /* __CLK_REALTEK_COMMON_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 02/10] arm64: dts: realtek: Add clock support for RTD1625
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
Add the clock controller nodes and osc27m fixed clock for the
Realtek RTD1625 SoC.
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Reordered device nodes alphabetically.
---
arch/arm64/boot/dts/realtek/kent.dtsi | 30 +++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/boot/dts/realtek/kent.dtsi b/arch/arm64/boot/dts/realtek/kent.dtsi
index ae006ce24420..3f3383872a49 100644
--- a/arch/arm64/boot/dts/realtek/kent.dtsi
+++ b/arch/arm64/boot/dts/realtek/kent.dtsi
@@ -26,6 +26,15 @@ timer {
<GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
};
+ clocks {
+ osc27m: osc {
+ compatible = "fixed-clock";
+ clock-frequency = <27000000>;
+ clock-output-names = "osc27m";
+ #clock-cells = <0>;
+ };
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -141,6 +150,13 @@ rbus: bus@98000000 {
#address-cells = <1>;
#size-cells = <1>;
+ cc: clock-controller@0 {
+ compatible = "realtek,rtd1625-crt-clk";
+ reg = <0x0 0x900>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
uart0: serial@7800 {
compatible = "snps,dw-apb-uart";
reg = <0x7800 0x100>;
@@ -150,6 +166,20 @@ uart0: serial@7800 {
reg-shift = <2>;
status = "disabled";
};
+
+ ic: clock-controller@7088 {
+ compatible = "realtek,rtd1625-iso-clk";
+ reg = <0x7088 0x8>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ iso_s_cc: clock-controller@146310 {
+ compatible = "realtek,rtd1625-iso-s-clk";
+ reg = <0x146310 0x8>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
};
gic: interrupt-controller@ff100000 {
--
2.34.1
^ permalink raw reply related
* [PATCH v5 07/10] clk: realtek: Add support for mux clock
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add a simple regmap-based clk_ops implementation for Realtek mux clocks.
The implementation supports parent selection and rate determination through
regmap-backed register access.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-regmap-mux.c | 46 ++++++++++++++++++++++++++++
drivers/clk/realtek/clk-regmap-mux.h | 43 ++++++++++++++++++++++++++
3 files changed, 90 insertions(+)
create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 74375f8127ac..f90dc57fcfdb 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -5,4 +5,5 @@ clk-rtk-y += common.o
clk-rtk-y += clk-pll.o
clk-rtk-y += clk-regmap-gate.o
+clk-rtk-y += clk-regmap-mux.o
clk-rtk-y += freq_table.o
diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
new file mode 100644
index 000000000000..d1612de2e2bc
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include "clk-regmap-mux.h"
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+ int num_parents = clk_hw_get_num_parents(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->mux_ofs, &val);
+ if (ret)
+ return 0;
+
+ val = val >> clkm->shift & clkm->mask;
+
+ if (val >= num_parents)
+ return 0;
+
+ return val;
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+
+ return regmap_update_bits(clkm->clkr.regmap, clkm->mux_ofs,
+ clkm->mask << clkm->shift, index << clkm->shift);
+}
+
+const struct clk_ops rtk_clk_regmap_mux_ops = {
+ .set_parent = clk_regmap_mux_set_parent,
+ .get_parent = clk_regmap_mux_get_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_regmap_mux_ops);
+
+const struct clk_ops rtk_clk_regmap_mux_ro_ops = {
+ .get_parent = clk_regmap_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_regmap_mux_ro_ops);
diff --git a/drivers/clk/realtek/clk-regmap-mux.h b/drivers/clk/realtek/clk-regmap-mux.h
new file mode 100644
index 000000000000..cf7ab6a0604c
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_REGMAP_MUX_H
+#define __CLK_REALTEK_CLK_REGMAP_MUX_H
+
+#include "common.h"
+
+struct clk_regmap_mux {
+ struct clk_regmap clkr;
+ int mux_ofs;
+ unsigned int mask;
+ unsigned int shift;
+};
+
+#define __clk_regmap_mux_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+#define __CLK_REGMAP_MUX(_name, _parents, _ops, _flags, _ofs, _sft, _mask) \
+ struct clk_regmap_mux _name = { \
+ .clkr.hw.init = \
+ CLK_HW_INIT_PARENTS(#_name, _parents, _ops, _flags), \
+ .mux_ofs = _ofs, \
+ .shift = _sft, \
+ .mask = _mask, \
+ }
+
+#define CLK_REGMAP_MUX(_name, _parents, _flags, _ofs, _sft, _mask) \
+ __CLK_REGMAP_MUX(_name, _parents, &rtk_clk_regmap_mux_ops, _flags, _ofs, \
+ _sft, _mask)
+
+static inline struct clk_regmap_mux *to_clk_regmap_mux(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_regmap_mux, clkr);
+}
+
+extern const struct clk_ops rtk_clk_regmap_mux_ops;
+
+#endif /* __CLK_REALTEK_CLK_REGMAP_MUX_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 05/10] clk: realtek: Add support for phase locked loops (PLLs)
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Provide a full set of PLL operations for programmable PLLs and a read-only
variant for fixed or hardware-managed PLLs.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-pll.c | 156 +++++++++++++++++++++++++++++++
drivers/clk/realtek/clk-pll.h | 47 ++++++++++
drivers/clk/realtek/freq_table.c | 35 +++++++
drivers/clk/realtek/freq_table.h | 23 +++++
5 files changed, 263 insertions(+)
create mode 100644 drivers/clk/realtek/clk-pll.c
create mode 100644 drivers/clk/realtek/clk-pll.h
create mode 100644 drivers/clk/realtek/freq_table.c
create mode 100644 drivers/clk/realtek/freq_table.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 377ec776ee47..a89ad77993e9 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -2,3 +2,5 @@
obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
clk-rtk-y += common.o
+clk-rtk-y += clk-pll.o
+clk-rtk-y += freq_table.o
diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
new file mode 100644
index 000000000000..6bb9084956b8
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include "clk-pll.h"
+
+#define TIMEOUT 2000
+
+static int wait_freq_ready(struct clk_pll *clkp)
+{
+ u32 pollval;
+
+ if (!clkp->freq_ready_valid)
+ return 0;
+
+ return regmap_read_poll_timeout_atomic(clkp->clkr.regmap, clkp->freq_ready_reg, pollval,
+ (pollval & clkp->freq_ready_mask)
+ == clkp->freq_ready_val, 0, TIMEOUT);
+}
+
+static bool is_power_on(struct clk_pll *clkp)
+{
+ u32 val;
+
+ if (!clkp->power_reg)
+ return true;
+
+ if (regmap_read(clkp->clkr.regmap, clkp->power_reg, &val))
+ return true;
+
+ return (val & clkp->power_mask) == clkp->power_val_on;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+
+ if (!clkp->seq_power_off)
+ return;
+
+ regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_off,
+ clkp->num_seq_power_off);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+
+ return is_power_on(clkp);
+}
+
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *ftblv = NULL;
+
+ ftblv = ftbl_find_by_rate(clkp->freq_tbl, req->rate);
+ if (!ftblv)
+ return -EINVAL;
+
+ req->rate = ftblv->rate;
+ return 0;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *fv;
+ u32 freq_val;
+
+ if (regmap_read(clkp->clkr.regmap, clkp->freq_reg, &freq_val))
+ return 0;
+
+ freq_val &= clkp->freq_mask;
+
+ fv = ftbl_find_by_val_with_mask(clkp->freq_tbl, clkp->freq_mask,
+ freq_val);
+ return fv ? fv->rate : 0;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *fv;
+ int ret;
+
+ fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+ if (!fv || fv->rate != rate)
+ return -EINVAL;
+
+ if (clkp->seq_pre_set_freq) {
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_pre_set_freq,
+ clkp->num_seq_pre_set_freq);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_update_bits(clkp->clkr.regmap, clkp->freq_reg,
+ clkp->freq_mask, fv->val);
+ if (ret)
+ return ret;
+
+ if (clkp->seq_post_set_freq) {
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_post_set_freq,
+ clkp->num_seq_post_set_freq);
+ if (ret)
+ return ret;
+ }
+
+ if (is_power_on(clkp)) {
+ ret = wait_freq_ready(clkp);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ int ret;
+
+ if (!clkp->seq_power_on)
+ return 0;
+
+ if (is_power_on(clkp))
+ return 0;
+
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_on,
+ clkp->num_seq_power_on);
+ if (ret)
+ return ret;
+
+ return wait_freq_ready(clkp);
+}
+
+const struct clk_ops rtk_clk_pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .is_enabled = clk_pll_is_enabled,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_pll_ops);
+
+const struct clk_ops rtk_clk_pll_ro_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_pll_ro_ops);
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
new file mode 100644
index 000000000000..2d27a44a270c
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_PLL_H
+#define __CLK_REALTEK_CLK_PLL_H
+
+#include "common.h"
+#include "freq_table.h"
+
+struct clk_pll {
+ struct clk_regmap clkr;
+ const struct reg_sequence *seq_power_on;
+ u32 num_seq_power_on;
+ const struct reg_sequence *seq_power_off;
+ u32 num_seq_power_off;
+ const struct reg_sequence *seq_pre_set_freq;
+ u32 num_seq_pre_set_freq;
+ const struct reg_sequence *seq_post_set_freq;
+ u32 num_seq_post_set_freq;
+ const struct freq_table *freq_tbl;
+ u32 freq_reg;
+ u32 freq_mask;
+ u32 freq_ready_valid;
+ u32 freq_ready_mask;
+ u32 freq_ready_reg;
+ u32 freq_ready_val;
+ u32 power_reg;
+ u32 power_mask;
+ u32 power_val_on;
+};
+
+#define __clk_pll_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+static inline struct clk_pll *to_clk_pll(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_pll, clkr);
+}
+
+extern const struct clk_ops rtk_clk_pll_ops;
+extern const struct clk_ops rtk_clk_pll_ro_ops;
+
+#endif /* __CLK_REALTEK_CLK_PLL_H */
diff --git a/drivers/clk/realtek/freq_table.c b/drivers/clk/realtek/freq_table.c
new file mode 100644
index 000000000000..26a0d2d3e851
--- /dev/null
+++ b/drivers/clk/realtek/freq_table.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "freq_table.h"
+
+const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+ unsigned long rate)
+{
+ unsigned long best_rate = 0;
+ const struct freq_table *best = NULL;
+
+ for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if (ftbl->rate == rate)
+ return ftbl;
+
+ if (ftbl->rate > rate)
+ continue;
+
+ if (ftbl->rate > best_rate) {
+ best_rate = ftbl->rate;
+ best = ftbl;
+ }
+ }
+
+ return best;
+}
+
+const struct freq_table *
+ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value)
+{
+ for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if ((ftbl->val & mask) == (value & mask))
+ return ftbl;
+ }
+ return NULL;
+};
diff --git a/drivers/clk/realtek/freq_table.h b/drivers/clk/realtek/freq_table.h
new file mode 100644
index 000000000000..383ae939fd1e
--- /dev/null
+++ b/drivers/clk/realtek/freq_table.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/bitops.h>
+
+struct freq_table {
+ u32 val;
+ unsigned long rate;
+};
+
+/* ofs check */
+#define CLK_OFS_INVALID -1
+#define CLK_OFS_IS_VALID(_ofs) ((_ofs) != CLK_OFS_INVALID)
+
+#define FREQ_TABLE_END \
+ { \
+ .rate = 0 \
+ }
+#define IS_FREQ_TABLE_END(_f) ((_f)->rate == 0)
+
+const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+ unsigned long rate);
+const struct freq_table *
+ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value);
--
2.34.1
^ permalink raw reply related
* [PATCH v5 10/10] clk: realtek: Add RTD1625-ISO clock controller driver
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the ISO (Isolation) domain clock controller on the Realtek
RTD1625 SoC. This controller manages clocks in the always-on power domain,
ensuring essential services remain functional even when the main system
power is gated.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Added '#include <dt-bindings/reset/realtek,rtd1625.h>'.
- Replaced rtk_reset_bank arrays with rtk_reset_desc descriptors.
- Implemented complete mapping tables for all reset IDs.
---
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-rtd1625-iso.c | 188 ++++++++++++++++++++++++++
2 files changed, 189 insertions(+)
create mode 100644 drivers/clk/realtek/clk-rtd1625-iso.c
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index c992f97dfbc7..1680435e1e0f 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -10,3 +10,4 @@ clk-rtk-y += freq_table.o
clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
obj-$(CONFIG_COMMON_CLK_RTD1625) += clk-rtd1625-crt.o
+obj-$(CONFIG_COMMON_CLK_RTD1625) += clk-rtd1625-iso.o
diff --git a/drivers/clk/realtek/clk-rtd1625-iso.c b/drivers/clk/realtek/clk-rtd1625-iso.c
new file mode 100644
index 000000000000..cfe82c9d4457
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1625-iso.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <dt-bindings/clock/realtek,rtd1625-clk.h>
+#include <dt-bindings/reset/realtek,rtd1625.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-regmap-gate.h"
+
+#define RTD1625_ISO_CLK_MAX 19
+#define RTD1625_ISO_RSTN_MAX 29
+#define RTD1625_ISO_S_CLK_MAX 5
+#define RTD1625_ISO_S_RSTN_MAX 5
+
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_p4, 0, 0x4, 0, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_p3, 0, 0x4, 1, 0);
+static CLK_REGMAP_GATE(clk_en_misc_cec0, "clk_en_misc", 0, 0x4, 2, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbusrx_sys, 0, 0x4, 3, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbustx_sys, 0, 0x4, 4, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbus_sys, 0, 0x4, 5, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbus_osc, 0, 0x4, 6, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_i2c0, 0, 0x4, 9, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_i2c1, 0, 0x4, 10, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_etn_250m, 0, 0x4, 11, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_etn_sys, 0, 0x4, 12, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_drd, 0, 0x4, 13, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_host, 0, 0x4, 14, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_u3_host, 0, 0x4, 15, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb, 0, 0x4, 16, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_vtc, 0, 0x4, 17, 0);
+static CLK_REGMAP_GATE(clk_en_misc_vfd, "clk_en_misc", 0, 0x4, 18, 0);
+
+static struct clk_regmap *rtd1625_clk_regmap_list[] = {
+ &clk_en_usb_p4.clkr,
+ &clk_en_usb_p3.clkr,
+ &clk_en_misc_cec0.clkr,
+ &clk_en_cbusrx_sys.clkr,
+ &clk_en_cbustx_sys.clkr,
+ &clk_en_cbus_sys.clkr,
+ &clk_en_cbus_osc.clkr,
+ &clk_en_i2c0.clkr,
+ &clk_en_i2c1.clkr,
+ &clk_en_etn_250m.clkr,
+ &clk_en_etn_sys.clkr,
+ &clk_en_usb_drd.clkr,
+ &clk_en_usb_host.clkr,
+ &clk_en_usb_u3_host.clkr,
+ &clk_en_usb.clkr,
+ &clk_en_vtc.clkr,
+ &clk_en_misc_vfd.clkr,
+};
+
+static struct clk_hw_onecell_data rtd1625_iso_clk_data = {
+ .num = RTD1625_ISO_CLK_MAX,
+ .hws = {
+ [RTD1625_ISO_CLK_EN_USB_P4] = &__clk_regmap_gate_hw(&clk_en_usb_p4),
+ [RTD1625_ISO_CLK_EN_USB_P3] = &__clk_regmap_gate_hw(&clk_en_usb_p3),
+ [RTD1625_ISO_CLK_EN_MISC_CEC0] = &__clk_regmap_gate_hw(&clk_en_misc_cec0),
+ [RTD1625_ISO_CLK_EN_CBUSRX_SYS] = &__clk_regmap_gate_hw(&clk_en_cbusrx_sys),
+ [RTD1625_ISO_CLK_EN_CBUSTX_SYS] = &__clk_regmap_gate_hw(&clk_en_cbustx_sys),
+ [RTD1625_ISO_CLK_EN_CBUS_SYS] = &__clk_regmap_gate_hw(&clk_en_cbus_sys),
+ [RTD1625_ISO_CLK_EN_CBUS_OSC] = &__clk_regmap_gate_hw(&clk_en_cbus_osc),
+ [RTD1625_ISO_CLK_EN_I2C0] = &__clk_regmap_gate_hw(&clk_en_i2c0),
+ [RTD1625_ISO_CLK_EN_I2C1] = &__clk_regmap_gate_hw(&clk_en_i2c1),
+ [RTD1625_ISO_CLK_EN_ETN_250M] = &__clk_regmap_gate_hw(&clk_en_etn_250m),
+ [RTD1625_ISO_CLK_EN_ETN_SYS] = &__clk_regmap_gate_hw(&clk_en_etn_sys),
+ [RTD1625_ISO_CLK_EN_USB_DRD] = &__clk_regmap_gate_hw(&clk_en_usb_drd),
+ [RTD1625_ISO_CLK_EN_USB_HOST] = &__clk_regmap_gate_hw(&clk_en_usb_host),
+ [RTD1625_ISO_CLK_EN_USB_U3_HOST] = &__clk_regmap_gate_hw(&clk_en_usb_u3_host),
+ [RTD1625_ISO_CLK_EN_USB] = &__clk_regmap_gate_hw(&clk_en_usb),
+ [RTD1625_ISO_CLK_EN_VTC] = &__clk_regmap_gate_hw(&clk_en_vtc),
+ [RTD1625_ISO_CLK_EN_MISC_VFD] = &__clk_regmap_gate_hw(&clk_en_misc_vfd),
+ [RTD1625_ISO_CLK_MAX] = NULL,
+ },
+};
+
+static struct rtk_reset_desc rtd1625_iso_reset_descs[] = {
+ [RTD1625_ISO_RSTN_VFD] = { .ofs = 0x88, .bit = 0 },
+ [RTD1625_ISO_RSTN_CEC0] = { .ofs = 0x88, .bit = 2 },
+ [RTD1625_ISO_RSTN_CEC1] = { .ofs = 0x88, .bit = 3 },
+ [RTD1625_ISO_RSTN_CBUSTX] = { .ofs = 0x88, .bit = 5 },
+ [RTD1625_ISO_RSTN_CBUSRX] = { .ofs = 0x88, .bit = 6 },
+ [RTD1625_ISO_RSTN_USB3_PHY2_XTAL_POW] = { .ofs = 0x88, .bit = 7 },
+ [RTD1625_ISO_RSTN_UR0] = { .ofs = 0x88, .bit = 8 },
+ [RTD1625_ISO_RSTN_GMAC] = { .ofs = 0x88, .bit = 9 },
+ [RTD1625_ISO_RSTN_GPHY] = { .ofs = 0x88, .bit = 10 },
+ [RTD1625_ISO_RSTN_I2C_0] = { .ofs = 0x88, .bit = 11 },
+ [RTD1625_ISO_RSTN_I2C_1] = { .ofs = 0x88, .bit = 12 },
+ [RTD1625_ISO_RSTN_CBUS] = { .ofs = 0x88, .bit = 13 },
+ [RTD1625_ISO_RSTN_USB_DRD] = { .ofs = 0x88, .bit = 14 },
+ [RTD1625_ISO_RSTN_USB_HOST] = { .ofs = 0x88, .bit = 15 },
+ [RTD1625_ISO_RSTN_USB_PHY_0] = { .ofs = 0x88, .bit = 16 },
+ [RTD1625_ISO_RSTN_USB_PHY_1] = { .ofs = 0x88, .bit = 17 },
+ [RTD1625_ISO_RSTN_USB_PHY_2] = { .ofs = 0x88, .bit = 18 },
+ [RTD1625_ISO_RSTN_USB] = { .ofs = 0x88, .bit = 19 },
+ [RTD1625_ISO_RSTN_TYPE_C] = { .ofs = 0x88, .bit = 20 },
+ [RTD1625_ISO_RSTN_USB_U3_HOST] = { .ofs = 0x88, .bit = 21 },
+ [RTD1625_ISO_RSTN_USB3_PHY0_POW] = { .ofs = 0x88, .bit = 22 },
+ [RTD1625_ISO_RSTN_USB3_P0_MDIO] = { .ofs = 0x88, .bit = 23 },
+ [RTD1625_ISO_RSTN_USB3_PHY1_POW] = { .ofs = 0x88, .bit = 24 },
+ [RTD1625_ISO_RSTN_USB3_P1_MDIO] = { .ofs = 0x88, .bit = 25 },
+ [RTD1625_ISO_RSTN_VTC] = { .ofs = 0x88, .bit = 26 },
+ [RTD1625_ISO_RSTN_USB3_PHY2_POW] = { .ofs = 0x88, .bit = 27 },
+ [RTD1625_ISO_RSTN_USB3_P2_MDIO] = { .ofs = 0x88, .bit = 28 },
+ [RTD1625_ISO_RSTN_USB_PHY_3] = { .ofs = 0x88, .bit = 29 },
+ [RTD1625_ISO_RSTN_USB_PHY_4] = { .ofs = 0x88, .bit = 30 },
+};
+
+static const struct rtk_clk_desc rtd1625_iso_desc = {
+ .clk_data = &rtd1625_iso_clk_data,
+ .clks = rtd1625_clk_regmap_list,
+ .num_clks = ARRAY_SIZE(rtd1625_clk_regmap_list),
+ .reset_descs = rtd1625_iso_reset_descs,
+ .num_reset_descs = RTD1625_ISO_RSTN_MAX,
+};
+
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_irda, 0, 0x4, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ur10, 0, 0x4, 8, 1);
+
+static struct clk_regmap *rtd1625_iso_s_clk_regmap_list[] = {
+ &clk_en_irda.clkr,
+ &clk_en_ur10.clkr,
+};
+
+static struct clk_hw_onecell_data rtd1625_iso_s_clk_data = {
+ .num = RTD1625_ISO_S_CLK_MAX,
+ .hws = {
+ [RTD1625_ISO_S_CLK_EN_IRDA] = &__clk_regmap_gate_hw(&clk_en_irda),
+ [RTD1625_ISO_S_CLK_EN_UR10] = &__clk_regmap_gate_hw(&clk_en_ur10),
+ [RTD1625_ISO_S_CLK_MAX] = NULL,
+ },
+};
+
+static struct rtk_reset_desc rtd1625_iso_s_reset_descs[] = {
+ [RTD1625_ISO_S_RSTN_ISOM_MIS] = { .ofs = 0x310, .bit = 0, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_GPIOM] = { .ofs = 0x310, .bit = 2, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_TIMER7] = { .ofs = 0x310, .bit = 4, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_IRDA] = { .ofs = 0x310, .bit = 6, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_UR10] = { .ofs = 0x310, .bit = 8, .write_en = 1 },
+};
+
+static const struct rtk_clk_desc rtd1625_iso_s_desc = {
+ .clk_data = &rtd1625_iso_s_clk_data,
+ .clks = rtd1625_iso_s_clk_regmap_list,
+ .num_clks = ARRAY_SIZE(rtd1625_iso_s_clk_regmap_list),
+ .reset_descs = rtd1625_iso_s_reset_descs,
+ .num_reset_descs = RTD1625_ISO_S_RSTN_MAX,
+};
+
+static int rtd1625_iso_probe(struct platform_device *pdev)
+{
+ const struct rtk_clk_desc *desc;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+ return rtk_clk_probe(pdev, desc);
+}
+
+static const struct of_device_id rtd1625_iso_match[] = {
+ {.compatible = "realtek,rtd1625-iso-clk", .data = &rtd1625_iso_desc},
+ {.compatible = "realtek,rtd1625-iso-s-clk", .data = &rtd1625_iso_s_desc},
+ {/* sentinel */}
+};
+
+static struct platform_driver rtd1625_iso_driver = {
+ .probe = rtd1625_iso_probe,
+ .driver = {
+ .name = "rtk-rtd1625-iso-clk",
+ .of_match_table = rtd1625_iso_match,
+ },
+};
+
+static int __init rtd1625_iso_init(void)
+{
+ return platform_driver_register(&rtd1625_iso_driver);
+}
+subsys_initcall(rtd1625_iso_init);
+
+MODULE_DESCRIPTION("Realtek RTD1625 ISO Controller Driver");
+MODULE_AUTHOR("Cheng-Yu Lee <cylee12@realtek.com>");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH v5 00/10] clk: realtek: Add RTD1625 clock support
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
Hello,
This patch series adds clock support for Realtek's RTD1625 platform
The series includes:
1. Device Tree: Add clock controller nodes.
2. Infrastructure: reset controller, basic clocks, PLLs, gate clocks, mux
clocks, and MMC-tuned PLLs.
3. Platform drivers: two clock controller drivers for RTD1625-CRT and
RTD1625-ISO.
Best regards,
Yu-Chun Lin
---
Changes in v5:
patch 1:
- Removed hardware bit position encoding from the reset binding and redesgined
reset IDs as sequential values.
patch 2:
- Reordered device nodes alphabetically.
patch 3:
- Created drivers/reset/realtek/ directory and include/linux/reset/realtek.h.
- Extracted the common reset helpers:
- Moved the source code to drivers/reset/realtek/common.c and
- the header to include/linux/reset/realtek.h
- Renamed rtk_reset_bank to rtk_reset_desc.
- Added 'bits' member to rtk_reset_desc structure.
- Removed rtk_reset_get_id() and rtk_reset_get_bank() helper.
- Introduced rtk_reset_get_desc().
patch 4:
- Synchronized with reset controller changes.
patches 9-10:
- Added #include <dt-bindings/reset/realtek,rtd1625.h> in drivers.
- Replaced rtk_reset_bank arrays with rtk_reset_desc descriptors.
- Implemented complete mapping tables for all reset IDs.
v4: https://lore.kernel.org/lkml/20260313081100.596224-1-eleanor.lin@realtek.com/
v3: https://lore.kernel.org/lkml/20260122110857.12995-1-eleanor.lin@realtek.com/
v2: https://lore.kernel.org/lkml/20260113112333.821-1-eleanor.lin@realtek.com/
v1: https://lore.kernel.org/lkml/20251229075313.27254-1-eleanor.lin@realtek.com/
Yu-Chun Lin (10):
dt-bindings: clock: Add Realtek RTD1625 Clock & Reset Controller
arm64: dts: realtek: Add clock support for RTD1625
reset: Add Realtek basic reset support
clk: realtek: Introduce a common probe()
clk: realtek: Add support for phase locked loops (PLLs)
clk: realtek: Add support for gate clock
clk: realtek: Add support for mux clock
clk: realtek: Add support for MMC-tuned PLL clocks
clk: realtek: Add RTD1625-CRT clock controller driver
clk: realtek: Add RTD1625-ISO clock controller driver
.../bindings/clock/realtek,rtd1625-clk.yaml | 52 +
MAINTAINERS | 19 +
arch/arm64/boot/dts/realtek/kent.dtsi | 30 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/realtek/Kconfig | 45 +
drivers/clk/realtek/Makefile | 13 +
drivers/clk/realtek/clk-pll-mmc.c | 399 ++++++++
drivers/clk/realtek/clk-pll.c | 156 +++
drivers/clk/realtek/clk-pll.h | 68 ++
drivers/clk/realtek/clk-regmap-gate.c | 66 ++
drivers/clk/realtek/clk-regmap-gate.h | 65 ++
drivers/clk/realtek/clk-regmap-mux.c | 46 +
drivers/clk/realtek/clk-regmap-mux.h | 43 +
drivers/clk/realtek/clk-rtd1625-crt.c | 912 ++++++++++++++++++
drivers/clk/realtek/clk-rtd1625-iso.c | 186 ++++
drivers/clk/realtek/common.c | 59 ++
drivers/clk/realtek/common.h | 43 +
drivers/clk/realtek/freq_table.c | 35 +
drivers/clk/realtek/freq_table.h | 23 +
drivers/reset/Kconfig | 1 +
drivers/reset/Makefile | 1 +
drivers/reset/realtek/Kconfig | 3 +
drivers/reset/realtek/Makefile | 2 +
drivers/reset/realtek/common.c | 91 ++
.../dt-bindings/clock/realtek,rtd1625-clk.h | 164 ++++
include/dt-bindings/reset/realtek,rtd1625.h | 186 ++++
include/linux/reset/realtek.h | 25 +
28 files changed, 2735 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml
create mode 100644 drivers/clk/realtek/Kconfig
create mode 100644 drivers/clk/realtek/Makefile
create mode 100644 drivers/clk/realtek/clk-pll-mmc.c
create mode 100644 drivers/clk/realtek/clk-pll.c
create mode 100644 drivers/clk/realtek/clk-pll.h
create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
create mode 100644 drivers/clk/realtek/clk-rtd1625-crt.c
create mode 100644 drivers/clk/realtek/clk-rtd1625-iso.c
create mode 100644 drivers/clk/realtek/common.c
create mode 100644 drivers/clk/realtek/common.h
create mode 100644 drivers/clk/realtek/freq_table.c
create mode 100644 drivers/clk/realtek/freq_table.h
create mode 100644 drivers/reset/realtek/Kconfig
create mode 100644 drivers/reset/realtek/Makefile
create mode 100644 drivers/reset/realtek/common.c
create mode 100644 include/dt-bindings/clock/realtek,rtd1625-clk.h
create mode 100644 include/dt-bindings/reset/realtek,rtd1625.h
create mode 100644 include/linux/reset/realtek.h
--
2.34.1
^ permalink raw reply
* [PATCH v5 08/10] clk: realtek: Add support for MMC-tuned PLL clocks
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add clk_pll_mmc_ops for enable/disable, prepare, rate control, and status
operations on MMC PLL clocks.
Also add clk_pll_mmc_phase_ops to support phase get/set operations.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Jyan Chou <jyanchou@realtek.com>
Signed-off-by: Jyan Chou <jyanchou@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
MAINTAINERS | 8 +
drivers/clk/realtek/Kconfig | 3 +
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-pll-mmc.c | 399 ++++++++++++++++++++++++++++++
drivers/clk/realtek/clk-pll.h | 21 ++
5 files changed, 433 insertions(+)
create mode 100644 drivers/clk/realtek/clk-pll-mmc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 31f4a5a66394..074d7f33c32b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22233,6 +22233,14 @@ F: drivers/clk/realtek/*
F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
+REALTEK SOC PLL CLOCK FOR MMC SUPPORT
+M: Cheng-Yu Lee <cylee12@realtek.com>
+M: Jyan Chou <jyanchou@realtek.com>
+M: Yu-Chun Lin <eleanor.lin@realtek.com>
+L: linux-clk@vger.kernel.org
+S: Supported
+F: drivers/clk/realtek/clk-pll-mmc.c
+
REALTEK SPI-NAND
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
S: Maintained
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 63eb3fb3e79a..5d790ce526ab 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -25,4 +25,7 @@ config RTK_CLK_COMMON
multiple Realtek clock implementations, and include integration
with reset controllers where required.
+config RTK_CLK_PLL_MMC
+ bool
+
endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index f90dc57fcfdb..fd7d777902c8 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -7,3 +7,5 @@ clk-rtk-y += clk-pll.o
clk-rtk-y += clk-regmap-gate.o
clk-rtk-y += clk-regmap-mux.o
clk-rtk-y += freq_table.o
+
+clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
diff --git a/drivers/clk/realtek/clk-pll-mmc.c b/drivers/clk/realtek/clk-pll-mmc.c
new file mode 100644
index 000000000000..017663738c1f
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-mmc.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include "clk-pll.h"
+
+#define PLL_EMMC1_OFFSET 0x0
+#define PLL_EMMC2_OFFSET 0x4
+#define PLL_EMMC3_OFFSET 0x8
+#define PLL_EMMC4_OFFSET 0xc
+#define PLL_SSC_DIG_EMMC1_OFFSET 0x0
+#define PLL_SSC_DIG_EMMC3_OFFSET 0xc
+#define PLL_SSC_DIG_EMMC4_OFFSET 0x10
+
+#define PLL_MMC_SSC_DIV_N_VAL 0x1b
+
+#define PLL_PHRT0_MASK BIT(1)
+#define PLL_PHSEL_MASK GENMASK(4, 0)
+#define PLL_SSCPLL_RS_MASK GENMASK(12, 10)
+#define PLL_SSCPLL_ICP_MASK GENMASK(9, 5)
+#define PLL_SSC_DIV_EXT_F_MASK GENMASK(25, 13)
+#define PLL_PI_IBSELH_MASK GENMASK(28, 27)
+#define PLL_SSC_DIV_N_MASK GENMASK(23, 16)
+#define PLL_NCODE_SSC_EMMC_MASK GENMASK(20, 13)
+#define PLL_FCODE_SSC_EMMC_MASK GENMASK(12, 0)
+#define PLL_GRAN_EST_EM_MC_MASK GENMASK(20, 0)
+#define PLL_EN_SSC_EMMC_MASK BIT(0)
+#define PLL_FLAG_INITAL_EMMC_MASK BIT(1)
+
+#define PLL_PHRT0_SHIFT 1
+#define PLL_SSCPLL_RS_SHIFT 10
+#define PLL_SSCPLL_ICP_SHIFT 5
+#define PLL_SSC_DIV_EXT_F_SHIFT 13
+#define PLL_PI_IBSELH_SHIFT 27
+#define PLL_SSC_DIV_N_SHIFT 16
+#define PLL_NCODE_SSC_EMMC_SHIFT 13
+#define PLL_FLAG_INITAL_EMMC_SHIFT 8
+
+#define CYCLE_DEGREES 360
+#define PHASE_STEPS 32
+#define PHASE_SCALE_FACTOR 1125
+
+static inline int get_phrt0(struct clk_pll_mmc *clkm, u32 *val)
+{
+ u32 reg;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, ®);
+ if (ret)
+ return ret;
+
+ *val = (reg >> PLL_PHRT0_SHIFT) & PLL_PHRT0_MASK;
+ return 0;
+}
+
+static inline int set_phrt0(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
+ PLL_PHRT0_MASK, val << PLL_PHRT0_SHIFT);
+}
+
+static inline int get_phsel(struct clk_pll_mmc *clkm, int id, u32 *val)
+{
+ int ret;
+ u32 raw_val;
+ u32 sft = id ? 8 : 3;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val >> sft) & PLL_PHSEL_MASK;
+ return 0;
+}
+
+static inline int set_phsel(struct clk_pll_mmc *clkm, int id, u32 val)
+{
+ u32 sft = id ? 8 : 3;
+
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
+ PLL_PHSEL_MASK << sft, val << sft);
+}
+
+static inline int set_sscpll_rs(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSCPLL_RS_MASK, val << PLL_SSCPLL_RS_SHIFT);
+}
+
+static inline int set_sscpll_icp(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSCPLL_ICP_MASK, val << PLL_SSCPLL_ICP_SHIFT);
+}
+
+static inline int get_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 *val)
+{
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val & PLL_SSC_DIV_EXT_F_MASK) >> PLL_SSC_DIV_EXT_F_SHIFT;
+ return 0;
+}
+
+static inline int set_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSC_DIV_EXT_F_MASK,
+ val << PLL_SSC_DIV_EXT_F_SHIFT);
+}
+
+static inline int set_pi_ibselh(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_PI_IBSELH_MASK, val << PLL_PI_IBSELH_SHIFT);
+}
+
+static inline int set_ssc_div_n(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET,
+ PLL_SSC_DIV_N_MASK, val << PLL_SSC_DIV_N_SHIFT);
+}
+
+static inline int get_ssc_div_n(struct clk_pll_mmc *clkm, u32 *val)
+{
+ int ret;
+ u32 raw_val;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val & PLL_SSC_DIV_N_MASK) >> PLL_SSC_DIV_N_SHIFT;
+ return 0;
+}
+
+static inline int set_pow_ctl(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_write(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, val);
+}
+
+static inline int get_pow_ctl(struct clk_pll_mmc *clkm, u32 *val)
+{
+ int ret;
+ u32 raw_val;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, &raw_val);
+
+ *val = raw_val;
+
+ return ret;
+}
+
+static int clk_pll_mmc_phase_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_hw *hwp = clk_hw_get_parent(hw);
+ struct clk_pll_mmc *clkm;
+ int phase_id;
+ u32 val;
+ int ret;
+
+ if (!hwp)
+ return -ENOENT;
+
+ clkm = to_clk_pll_mmc(hwp);
+ phase_id = (hw - &clkm->phase0_hw) ? 1 : 0;
+ val = DIV_ROUND_CLOSEST(degrees * 100, PHASE_SCALE_FACTOR);
+ ret = set_phsel(clkm, phase_id, val);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 20);
+ return 0;
+}
+
+static int clk_pll_mmc_phase_get_phase(struct clk_hw *hw)
+{
+ struct clk_hw *hwp;
+ struct clk_pll_mmc *clkm;
+ int phase_id;
+ int ret;
+ u32 val;
+
+ hwp = clk_hw_get_parent(hw);
+ if (!hwp)
+ return -ENOENT;
+
+ clkm = to_clk_pll_mmc(hwp);
+ phase_id = (hw - &clkm->phase0_hw) ? 1 : 0;
+ ret = get_phsel(clkm, phase_id, &val);
+ if (ret)
+ return ret;
+
+ val = DIV_ROUND_CLOSEST(val * CYCLE_DEGREES, PHASE_STEPS);
+
+ return val;
+}
+
+const struct clk_ops rtk_clk_pll_mmc_phase_ops = {
+ .set_phase = clk_pll_mmc_phase_set_phase,
+ .get_phase = clk_pll_mmc_phase_get_phase,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_pll_mmc_phase_ops);
+
+static int clk_pll_mmc_prepare(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ return set_pow_ctl(clkm, 7);
+}
+
+static void clk_pll_mmc_unprepare(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ set_pow_ctl(clkm, 0);
+}
+
+static int clk_pll_mmc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val;
+ int ret;
+
+ ret = get_pow_ctl(clkm, &val);
+ if (ret)
+ return 1;
+
+ return val != 0x0;
+}
+
+static int clk_pll_mmc_enable(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ int ret;
+
+ ret = set_phrt0(clkm, 1);
+ if (ret)
+ return ret;
+
+ udelay(10);
+ return 0;
+}
+
+static void clk_pll_mmc_disable(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ set_phrt0(clkm, 0);
+ udelay(10);
+}
+
+static int clk_pll_mmc_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val;
+ int ret;
+
+ ret = get_phrt0(clkm, &val);
+ if (ret)
+ return 1;
+
+ return val == 0x1;
+}
+
+static unsigned long clk_pll_mmc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val, ext_f;
+ int ret;
+
+ ret = get_ssc_div_n(clkm, &val);
+ if (ret)
+ return ret;
+
+ ret = get_ssc_div_ext_f(clkm, &ext_f);
+ if (ret)
+ return ret;
+
+ return parent_rate / 4 * (val + 2) + (parent_rate / 4 * ext_f) / 8192;
+}
+
+static int clk_pll_mmc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ u32 val = DIV_ROUND_CLOSEST(req->rate * 4, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * val / 4;
+ return 0;
+}
+
+static int clk_pll_mmc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val = PLL_MMC_SSC_DIV_N_VAL;
+ int ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_FLAG_INITAL_EMMC_MASK, 0x0 << PLL_FLAG_INITAL_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = set_ssc_div_n(clkm, val);
+ if (ret)
+ return ret;
+
+ ret = set_ssc_div_ext_f(clkm, 1517);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 31 ... 46:
+ ret |= set_pi_ibselh(clkm, 3);
+ ret |= set_sscpll_rs(clkm, 3);
+ ret |= set_sscpll_icp(clkm, 2);
+ break;
+
+ case 20 ... 30:
+ ret |= set_pi_ibselh(clkm, 2);
+ ret |= set_sscpll_rs(clkm, 3);
+ ret |= set_sscpll_icp(clkm, 1);
+ break;
+
+ case 10 ... 19:
+ ret |= set_pi_ibselh(clkm, 1);
+ ret |= set_sscpll_rs(clkm, 2);
+ ret |= set_sscpll_icp(clkm, 1);
+ break;
+
+ case 5 ... 9:
+ ret |= set_pi_ibselh(clkm, 0);
+ ret |= set_sscpll_rs(clkm, 2);
+ ret |= set_sscpll_icp(clkm, 0);
+ break;
+ }
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
+ PLL_NCODE_SSC_EMMC_MASK,
+ 27 << PLL_NCODE_SSC_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
+ PLL_FCODE_SSC_EMMC_MASK, 321);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC4_OFFSET,
+ PLL_GRAN_EST_EM_MC_MASK, 5985);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_EN_SSC_EMMC_MASK, 0x1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_EN_SSC_EMMC_MASK, 0x0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_FLAG_INITAL_EMMC_MASK,
+ 0x1 << PLL_FLAG_INITAL_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 20);
+ return 0;
+}
+
+const struct clk_ops rtk_clk_pll_mmc_ops = {
+ .prepare = clk_pll_mmc_prepare,
+ .unprepare = clk_pll_mmc_unprepare,
+ .is_prepared = clk_pll_mmc_is_prepared,
+ .enable = clk_pll_mmc_enable,
+ .disable = clk_pll_mmc_disable,
+ .is_enabled = clk_pll_mmc_is_enabled,
+ .recalc_rate = clk_pll_mmc_recalc_rate,
+ .determine_rate = clk_pll_mmc_determine_rate,
+ .set_rate = clk_pll_mmc_set_rate,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_pll_mmc_ops);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
index 2d27a44a270c..9cf219871218 100644
--- a/drivers/clk/realtek/clk-pll.h
+++ b/drivers/clk/realtek/clk-pll.h
@@ -44,4 +44,25 @@ static inline struct clk_pll *to_clk_pll(struct clk_hw *hw)
extern const struct clk_ops rtk_clk_pll_ops;
extern const struct clk_ops rtk_clk_pll_ro_ops;
+struct clk_pll_mmc {
+ struct clk_regmap clkr;
+ int pll_ofs;
+ int ssc_dig_ofs;
+ struct clk_hw phase0_hw;
+ struct clk_hw phase1_hw;
+ u32 set_rate_val_53_97_set_ipc: 1;
+};
+
+#define __clk_pll_mmc_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+static inline struct clk_pll_mmc *to_clk_pll_mmc(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_pll_mmc, clkr);
+}
+
+extern const struct clk_ops rtk_clk_pll_mmc_ops;
+extern const struct clk_ops rtk_clk_pll_mmc_phase_ops;
+
#endif /* __CLK_REALTEK_CLK_PLL_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 03/10] reset: Add Realtek basic reset support
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Define the reset operations backed by a regmap-based register
interface and prepare the reset controller to be registered
through the reset framework.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Created drivers/reset/realtek/ directory and include/linux/reset/realtek.h.
- Extracted the common reset helpers:
- Moved the source code to drivers/reset/realtek/common.c and
- the header to include/linux/reset/realtek.h
- Renamed rtk_reset_bank to rtk_reset_desc.
- Added 'bits' member to rtk_reset_desc structure.
- Removed rtk_reset_get_id() and rtk_reset_get_bank() helper.
- Introduced rtk_reset_get_desc().
---
MAINTAINERS | 1 +
drivers/reset/Kconfig | 1 +
drivers/reset/Makefile | 1 +
drivers/reset/realtek/Kconfig | 3 ++
drivers/reset/realtek/Makefile | 2 +
drivers/reset/realtek/common.c | 91 ++++++++++++++++++++++++++++++++++
include/linux/reset/realtek.h | 25 ++++++++++
7 files changed, 124 insertions(+)
create mode 100644 drivers/reset/realtek/Kconfig
create mode 100644 drivers/reset/realtek/Makefile
create mode 100644 drivers/reset/realtek/common.c
create mode 100644 include/linux/reset/realtek.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 721356d4c02c..9419b0497e0b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22229,6 +22229,7 @@ L: devicetree@vger.kernel.org
L: linux-clk@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/clock/realtek*
+F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
REALTEK SPI-NAND
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 7ce151f6a7e4..03be1931f264 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -398,6 +398,7 @@ config RESET_ZYNQMP
source "drivers/reset/amlogic/Kconfig"
source "drivers/reset/hisilicon/Kconfig"
+source "drivers/reset/realtek/Kconfig"
source "drivers/reset/spacemit/Kconfig"
source "drivers/reset/starfive/Kconfig"
source "drivers/reset/sti/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index fc0cc99f8514..4407d1630070 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -2,6 +2,7 @@
obj-y += core.o
obj-y += amlogic/
obj-y += hisilicon/
+obj-y += realtek/
obj-y += spacemit/
obj-y += starfive/
obj-y += sti/
diff --git a/drivers/reset/realtek/Kconfig b/drivers/reset/realtek/Kconfig
new file mode 100644
index 000000000000..99a14d355803
--- /dev/null
+++ b/drivers/reset/realtek/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config RESET_RTK_COMMON
+ bool
diff --git a/drivers/reset/realtek/Makefile b/drivers/reset/realtek/Makefile
new file mode 100644
index 000000000000..b59a3f7f2453
--- /dev/null
+++ b/drivers/reset/realtek/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_RESET_RTK_COMMON) += common.o
diff --git a/drivers/reset/realtek/common.c b/drivers/reset/realtek/common.c
new file mode 100644
index 000000000000..b9e3219dc8f7
--- /dev/null
+++ b/drivers/reset/realtek/common.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/realtek.h>
+
+struct rtk_reset_data {
+ struct reset_controller_dev rcdev;
+ struct rtk_reset_desc *descs;
+ struct regmap *regmap;
+};
+
+static inline struct rtk_reset_data *to_rtk_reset_controller(struct reset_controller_dev *r)
+{
+ return container_of(r, struct rtk_reset_data, rcdev);
+}
+
+static inline struct rtk_reset_desc *rtk_reset_get_desc(struct rtk_reset_data *data,
+ unsigned long idx)
+{
+ return &data->descs[idx];
+}
+
+static int rtk_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
+ u32 mask = desc->write_en ? (0x3 << desc->bit) : BIT(desc->bit);
+ u32 val = desc->write_en ? (0x2 << desc->bit) : 0;
+
+ return regmap_update_bits(data->regmap, desc->ofs, mask, val);
+}
+
+static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
+ u32 mask = desc->write_en ? (0x3 << desc->bit) : BIT(desc->bit);
+ u32 val = mask;
+
+ return regmap_update_bits(data->regmap, desc->ofs, mask, val);
+}
+
+static int rtk_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(data->regmap, desc->ofs, &val);
+ if (ret)
+ return ret;
+
+ return !((val >> desc->bit) & 1);
+}
+
+static const struct reset_control_ops rtk_reset_ops = {
+ .assert = rtk_reset_assert,
+ .deassert = rtk_reset_deassert,
+ .status = rtk_reset_status,
+};
+
+int rtk_reset_controller_add(struct device *dev,
+ struct rtk_reset_initdata *initdata)
+{
+ struct rtk_reset_data *data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = initdata->regmap;
+ data->descs = initdata->descs;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &rtk_reset_ops;
+ data->rcdev.dev = dev;
+ data->rcdev.of_node = dev->of_node;
+ data->rcdev.nr_resets = initdata->num_descs;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+EXPORT_SYMBOL_GPL(rtk_reset_controller_add);
diff --git a/include/linux/reset/realtek.h b/include/linux/reset/realtek.h
new file mode 100644
index 000000000000..3c2226e3ed26
--- /dev/null
+++ b/include/linux/reset/realtek.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_RESET_REALTEK_H__
+#define __LINUX_RESET_REALTEK_H__
+
+#include <linux/types.h>
+
+struct device;
+struct regmap;
+
+struct rtk_reset_desc {
+ u32 ofs;
+ u32 bit;
+ u32 write_en;
+};
+
+struct rtk_reset_initdata {
+ struct rtk_reset_desc *descs;
+ u32 num_descs;
+ struct regmap *regmap;
+};
+
+int rtk_reset_controller_add(struct device *dev,
+ struct rtk_reset_initdata *initdata);
+
+#endif /* __LINUX_RESET_REALTEK_H__ */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 01/10] dt-bindings: clock: Add Realtek RTD1625 Clock & Reset Controller
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
Add DT binding schema for Realtek RTD1625 clock and reset controller
Co-developed-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Removed hardware bit position encoding from the reset binding and redesgined
reset IDs as sequential values.
---
.../bindings/clock/realtek,rtd1625-clk.yaml | 52 ++++++
MAINTAINERS | 9 +
.../dt-bindings/clock/realtek,rtd1625-clk.h | 164 +++++++++++++++++
include/dt-bindings/reset/realtek,rtd1625.h | 171 ++++++++++++++++++
4 files changed, 396 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml
create mode 100644 include/dt-bindings/clock/realtek,rtd1625-clk.h
create mode 100644 include/dt-bindings/reset/realtek,rtd1625.h
diff --git a/Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml b/Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml
new file mode 100644
index 000000000000..6fabc2da3975
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/realtek,rtd1625-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTD1625 Clock & Reset Controller
+
+maintainers:
+ - Yu-Chun Lin <eleanor.lin@realtek.com>
+
+description: |
+ The Realtek RTD1625 Clock Controller manages and distributes clock
+ signals to various controllers and implements a Reset Controller for the
+ SoC peripherals.
+
+ Clocks and resets are referenced by unique identifiers, which are defined as
+ preprocessor macros in include/dt-bindings/clock/realtek,rtd1625-clk.h and
+ include/dt-bindings/reset/realtek,rtd1625.h.
+
+properties:
+ compatible:
+ enum:
+ - realtek,rtd1625-crt-clk
+ - realtek,rtd1625-iso-clk
+ - realtek,rtd1625-iso-s-clk
+
+ reg:
+ maxItems: 1
+
+ "#clock-cells":
+ const: 1
+
+ "#reset-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - "#clock-cells"
+ - "#reset-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ clock-controller@98000000 {
+ compatible = "realtek,rtd1625-crt-clk";
+ reg = <98000000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index d7241695df96..721356d4c02c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22222,6 +22222,15 @@ S: Maintained
F: Documentation/devicetree/bindings/net/dsa/realtek.yaml
F: drivers/net/dsa/realtek/*
+REALTEK SOC CLOCK AND RESET DRIVERS
+M: Cheng-Yu Lee <cylee12@realtek.com>
+M: Yu-Chun Lin <eleanor.lin@realtek.com>
+L: devicetree@vger.kernel.org
+L: linux-clk@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/clock/realtek*
+F: include/dt-bindings/clock/realtek*
+
REALTEK SPI-NAND
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
S: Maintained
diff --git a/include/dt-bindings/clock/realtek,rtd1625-clk.h b/include/dt-bindings/clock/realtek,rtd1625-clk.h
new file mode 100644
index 000000000000..61ca652d6880
--- /dev/null
+++ b/include/dt-bindings/clock/realtek,rtd1625-clk.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2025 Realtek Semiconductor Corp.
+ */
+#ifndef __DT_BINDINGS_RTK_CLOCK_RTD1625_H
+#define __DT_BINDINGS_RTK_CLOCK_RTD1625_H
+
+#define RTD1625_CRT_CLK_EN_MISC 0
+#define RTD1625_CRT_CLK_EN_PCIE0 1
+#define RTD1625_CRT_CLK_EN_DIP 2
+#define RTD1625_CRT_CLK_EN_GSPI 3
+#define RTD1625_CRT_CLK_EN_ISO_MISC 5
+#define RTD1625_CRT_CLK_EN_SDS 6
+#define RTD1625_CRT_CLK_EN_HDMI 7
+#define RTD1625_CRT_CLK_EN_GPU 9
+#define RTD1625_CRT_CLK_EN_VE1 10
+#define RTD1625_CRT_CLK_EN_VE2 11
+#define RTD1625_CRT_CLK_EN_MD 18
+#define RTD1625_CRT_CLK_EN_TP 19
+#define RTD1625_CRT_CLK_EN_RCIC 20
+#define RTD1625_CRT_CLK_EN_NF 21
+#define RTD1625_CRT_CLK_EN_EMMC 22
+#define RTD1625_CRT_CLK_EN_SD 23
+#define RTD1625_CRT_CLK_EN_SDIO_IP 24
+#define RTD1625_CRT_CLK_EN_MIPI_CSI 25
+#define RTD1625_CRT_CLK_EN_EMMC_IP 26
+#define RTD1625_CRT_CLK_EN_SDIO 27
+#define RTD1625_CRT_CLK_EN_SD_IP 28
+#define RTD1625_CRT_CLK_EN_TPB 30
+#define RTD1625_CRT_CLK_EN_MISC_SC1 31
+#define RTD1625_CRT_CLK_EN_MISC_I2C_3 32
+#define RTD1625_CRT_CLK_EN_ACPU 33
+#define RTD1625_CRT_CLK_EN_JPEG 34
+#define RTD1625_CRT_CLK_EN_MISC_SC0 37
+#define RTD1625_CRT_CLK_EN_HDMIRX 45
+#define RTD1625_CRT_CLK_EN_HSE 46
+#define RTD1625_CRT_CLK_EN_FAN 49
+#define RTD1625_CRT_CLK_EN_SATA_WRAP_SYS 52
+#define RTD1625_CRT_CLK_EN_SATA_WRAP_SYSH 53
+#define RTD1625_CRT_CLK_EN_SATA_MAC_SYSH 54
+#define RTD1625_CRT_CLK_EN_R2RDSC 55
+#define RTD1625_CRT_CLK_EN_TPC 56
+#define RTD1625_CRT_CLK_EN_PCIE1 57
+#define RTD1625_CRT_CLK_EN_MISC_I2C_4 58
+#define RTD1625_CRT_CLK_EN_MISC_I2C_5 59
+#define RTD1625_CRT_CLK_EN_TSIO 60
+#define RTD1625_CRT_CLK_EN_VE4 61
+#define RTD1625_CRT_CLK_EN_EDP 62
+#define RTD1625_CRT_CLK_EN_TSIO_TRX 63
+#define RTD1625_CRT_CLK_EN_PCIE2 64
+#define RTD1625_CRT_CLK_EN_EARC 66
+#define RTD1625_CRT_CLK_EN_LITE 67
+#define RTD1625_CRT_CLK_EN_MIPI_DSI 68
+#define RTD1625_CRT_CLK_EN_NPUPP 69
+#define RTD1625_CRT_CLK_EN_NPU 70
+#define RTD1625_CRT_CLK_EN_AUCPU0 71
+#define RTD1625_CRT_CLK_EN_AUCPU1 72
+#define RTD1625_CRT_CLK_EN_NSRAM 73
+#define RTD1625_CRT_CLK_EN_HDMITOP 74
+#define RTD1625_CRT_CLK_EN_AUCPU_ISO_NPU 76
+#define RTD1625_CRT_CLK_EN_KEYLADDER 77
+#define RTD1625_CRT_CLK_EN_IFCP_KLM 78
+#define RTD1625_CRT_CLK_EN_IFCP 79
+#define RTD1625_CRT_CLK_EN_MDL_GENPW 80
+#define RTD1625_CRT_CLK_EN_MDL_CHIP 81
+#define RTD1625_CRT_CLK_EN_MDL_IP 82
+#define RTD1625_CRT_CLK_EN_MDLM2M 83
+#define RTD1625_CRT_CLK_EN_MDL_XTAL 84
+#define RTD1625_CRT_CLK_EN_TEST_MUX 85
+#define RTD1625_CRT_CLK_EN_DLA 86
+#define RTD1625_CRT_CLK_EN_TPCW 88
+#define RTD1625_CRT_CLK_EN_GPU_TS_SRC 89
+#define RTD1625_CRT_CLK_EN_VI 91
+#define RTD1625_CRT_CLK_EN_LVDS1 92
+#define RTD1625_CRT_CLK_EN_LVDS2 93
+#define RTD1625_CRT_CLK_EN_AUCPU 94
+#define RTD1625_CRT_CLK_EN_UR1 96
+#define RTD1625_CRT_CLK_EN_UR2 97
+#define RTD1625_CRT_CLK_EN_UR3 98
+#define RTD1625_CRT_CLK_EN_UR4 99
+#define RTD1625_CRT_CLK_EN_UR5 100
+#define RTD1625_CRT_CLK_EN_UR6 101
+#define RTD1625_CRT_CLK_EN_UR7 102
+#define RTD1625_CRT_CLK_EN_UR8 103
+#define RTD1625_CRT_CLK_EN_UR9 104
+#define RTD1625_CRT_CLK_EN_UR_TOP 105
+#define RTD1625_CRT_CLK_EN_MISC_I2C_7 110
+#define RTD1625_CRT_CLK_EN_MISC_I2C_6 111
+#define RTD1625_CRT_CLK_EN_SPI0 112
+#define RTD1625_CRT_CLK_EN_SPI1 113
+#define RTD1625_CRT_CLK_EN_SPI2 114
+#define RTD1625_CRT_CLK_EN_LSADC0 120
+#define RTD1625_CRT_CLK_EN_LSADC1 121
+#define RTD1625_CRT_CLK_EN_ISOMIS_DMA 122
+#define RTD1625_CRT_CLK_EN_DPTX 124
+#define RTD1625_CRT_CLK_EN_NPU_MIPI_CSI 125
+#define RTD1625_CRT_CLK_EN_EDPTX 126
+#define RTD1625_CRT_CLK_HIFI 128
+#define RTD1625_CRT_CLK_NPU_MIPI_CSI 129
+#define RTD1625_CRT_CLK_NPU 130
+#define RTD1625_CRT_CLK_NPU_SYSH 132
+#define RTD1625_CRT_CLK_HIFI_SCPU 133
+#define RTD1625_CRT_CLK_GPU 134
+#define RTD1625_CRT_CLK_GPU2D 135
+#define RTD1625_CRT_CLK_MIPI_DSI_PCLK 136
+#define RTD1625_CRT_CLK_VE1 137
+#define RTD1625_CRT_CLK_VE2 138
+#define RTD1625_CRT_CLK_VE4 139
+#define RTD1625_CRT_CLK_SYS 141
+#define RTD1625_CRT_CLK_SYSH 142
+#define RTD1625_CRT_PLL_SDIO_REF 145
+#define RTD1625_CRT_PLL_CR_REF 146
+#define RTD1625_CRT_PLL_EMMC_REF 147
+#define RTD1625_CRT_CLK_MIS_SC0 148
+#define RTD1625_CRT_CLK_MIS_SC1 149
+#define RTD1625_CRT_PLL_SCPU 150
+#define RTD1625_CRT_PLL_VE1 151
+#define RTD1625_CRT_PLL_DDSA 152
+#define RTD1625_CRT_PLL_PSAUDA1 153
+#define RTD1625_CRT_PLL_PSAUDA2 154
+#define RTD1625_CRT_PLL_BUS 155
+#define RTD1625_CRT_PLL_SDIO 156
+#define RTD1625_CRT_PLL_SDIO_VP0 157
+#define RTD1625_CRT_PLL_SDIO_VP1 158
+#define RTD1625_CRT_PLL_DCSB 159
+#define RTD1625_CRT_PLL_GPU 160
+#define RTD1625_CRT_PLL_NPU 161
+#define RTD1625_CRT_PLL_VE2 162
+#define RTD1625_CRT_PLL_HIFI 163
+#define RTD1625_CRT_PLL_SD 164
+#define RTD1625_CRT_PLL_SD_VP0 165
+#define RTD1625_CRT_PLL_SD_VP1 166
+#define RTD1625_CRT_PLL_EMMC 167
+#define RTD1625_CRT_PLL_EMMC_VP0 168
+#define RTD1625_CRT_PLL_EMMC_VP1 169
+#define RTD1625_CRT_PLL_ACPU 170
+#define RTD1625_CRT_CLK_DET 171
+
+#define RTD1625_ISO_CLK_EN_USB_P4 0
+#define RTD1625_ISO_CLK_EN_USB_P3 1
+#define RTD1625_ISO_CLK_EN_MISC_CEC0 2
+#define RTD1625_ISO_CLK_EN_CBUSRX_SYS 3
+#define RTD1625_ISO_CLK_EN_CBUSTX_SYS 4
+#define RTD1625_ISO_CLK_EN_CBUS_SYS 5
+#define RTD1625_ISO_CLK_EN_CBUS_OSC 6
+#define RTD1625_ISO_CLK_EN_MISC_UR0 8
+#define RTD1625_ISO_CLK_EN_I2C0 9
+#define RTD1625_ISO_CLK_EN_I2C1 10
+#define RTD1625_ISO_CLK_EN_ETN_250M 11
+#define RTD1625_ISO_CLK_EN_ETN_SYS 12
+#define RTD1625_ISO_CLK_EN_USB_DRD 13
+#define RTD1625_ISO_CLK_EN_USB_HOST 14
+#define RTD1625_ISO_CLK_EN_USB_U3_HOST 15
+#define RTD1625_ISO_CLK_EN_USB 16
+#define RTD1625_ISO_CLK_EN_VTC 17
+#define RTD1625_ISO_CLK_EN_MISC_VFD 18
+
+#define RTD1625_ISO_S_CLK_EN_ISOM_MIS 0
+#define RTD1625_ISO_S_CLK_EN_ISOM_GPIOM 1
+#define RTD1625_ISO_S_CLK_EN_TIMER7 2
+#define RTD1625_ISO_S_CLK_EN_IRDA 3
+#define RTD1625_ISO_S_CLK_EN_UR10 4
+
+#endif /* __DT_BINDINGS_RTK_CLOCK_RTD1625_H */
diff --git a/include/dt-bindings/reset/realtek,rtd1625.h b/include/dt-bindings/reset/realtek,rtd1625.h
new file mode 100644
index 000000000000..31e7fa66ef31
--- /dev/null
+++ b/include/dt-bindings/reset/realtek,rtd1625.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2025 Realtek Semiconductor Corp.
+ */
+
+#ifndef __DT_BINDINGS_RTK_RESET_RTD1625_H
+#define __DT_BINDINGS_RTK_RESET_RTD1625_H
+
+#define RTD1625_CRT_RSTN_MISC 0
+#define RTD1625_CRT_RSTN_DIP 1
+#define RTD1625_CRT_RSTN_GSPI 2
+#define RTD1625_CRT_RSTN_SDS 3
+#define RTD1625_CRT_RSTN_SDS_REG 4
+#define RTD1625_CRT_RSTN_SDS_PHY 5
+#define RTD1625_CRT_RSTN_GPU2D 6
+#define RTD1625_CRT_RSTN_DC_PHY 7
+#define RTD1625_CRT_RSTN_DCPHY_CRT 8
+#define RTD1625_CRT_RSTN_LSADC 9
+#define RTD1625_CRT_RSTN_SE 10
+#define RTD1625_CRT_RSTN_DLA 11
+#define RTD1625_CRT_RSTN_JPEG 12
+#define RTD1625_CRT_RSTN_SD 13
+#define RTD1625_CRT_RSTN_SDIO 14
+#define RTD1625_CRT_RSTN_PCR_CNT 15
+#define RTD1625_CRT_RSTN_PCIE0_STITCH 16
+#define RTD1625_CRT_RSTN_PCIE0_PHY 17
+#define RTD1625_CRT_RSTN_PCIE0 18
+#define RTD1625_CRT_RSTN_PCIE0_CORE 19
+#define RTD1625_CRT_RSTN_PCIE0_POWER 20
+#define RTD1625_CRT_RSTN_PCIE0_NONSTICH 21
+#define RTD1625_CRT_RSTN_PCIE0_PHY_MDIO 22
+#define RTD1625_CRT_RSTN_PCIE0_SGMII_MDIO 23
+#define RTD1625_CRT_RSTN_VO2 24
+#define RTD1625_CRT_RSTN_MISC_SC0 25
+#define RTD1625_CRT_RSTN_MD 26
+#define RTD1625_CRT_RSTN_LVDS1 27
+#define RTD1625_CRT_RSTN_LVDS2 28
+#define RTD1625_CRT_RSTN_MISC_SC1 29
+#define RTD1625_CRT_RSTN_I2C_3 30
+#define RTD1625_CRT_RSTN_FAN 31
+#define RTD1625_CRT_RSTN_TVE 32
+#define RTD1625_CRT_RSTN_AIO 33
+#define RTD1625_CRT_RSTN_VO 34
+#define RTD1625_CRT_RSTN_MIPI_CSI 35
+#define RTD1625_CRT_RSTN_HDMIRX 36
+#define RTD1625_CRT_RSTN_HDMIRX_WRAP 37
+#define RTD1625_CRT_RSTN_HDMI 38
+#define RTD1625_CRT_RSTN_DISP 39
+#define RTD1625_CRT_RSTN_SATA_PHY_POW1 40
+#define RTD1625_CRT_RSTN_SATA_PHY_POW0 41
+#define RTD1625_CRT_RSTN_SATA_MDIO1 42
+#define RTD1625_CRT_RSTN_SATA_MDIO0 43
+#define RTD1625_CRT_RSTN_SATA_WRAP 44
+#define RTD1625_CRT_RSTN_SATA_MAC_P1 45
+#define RTD1625_CRT_RSTN_SATA_MAC_P0 46
+#define RTD1625_CRT_RSTN_SATA_MAC_COM 47
+#define RTD1625_CRT_RSTN_PCIE1_STITCH 48
+#define RTD1625_CRT_RSTN_PCIE1_PHY 49
+#define RTD1625_CRT_RSTN_PCIE1 50
+#define RTD1625_CRT_RSTN_PCIE1_CORE 51
+#define RTD1625_CRT_RSTN_PCIE1_POWER 52
+#define RTD1625_CRT_RSTN_PCIE1_NONSTICH 53
+#define RTD1625_CRT_RSTN_PCIE1_PHY_MDIO 54
+#define RTD1625_CRT_RSTN_HDMITOP 55
+#define RTD1625_CRT_RSTN_I2C_4 56
+#define RTD1625_CRT_RSTN_I2C_5 57
+#define RTD1625_CRT_RSTN_TSIO 58
+#define RTD1625_CRT_RSTN_VI 59
+#define RTD1625_CRT_RSTN_EDP 60
+#define RTD1625_CRT_RSTN_VE1_MMU 61
+#define RTD1625_CRT_RSTN_VE1_MMU_FUNC 62
+#define RTD1625_CRT_RSTN_HSE_MMU 63
+#define RTD1625_CRT_RSTN_HSE_MMU_FUNC 64
+#define RTD1625_CRT_RSTN_MDLM2M 65
+#define RTD1625_CRT_RSTN_ISO_GSPI 66
+#define RTD1625_CRT_RSTN_SOFT_NPU 67
+#define RTD1625_CRT_RSTN_SPI2EMMC 68
+#define RTD1625_CRT_RSTN_EARC 69
+#define RTD1625_CRT_RSTN_VE1 70
+#define RTD1625_CRT_RSTN_PCIE2_STITCH 71
+#define RTD1625_CRT_RSTN_PCIE2_PHY 72
+#define RTD1625_CRT_RSTN_PCIE2 73
+#define RTD1625_CRT_RSTN_PCIE2_CORE 74
+#define RTD1625_CRT_RSTN_PCIE2_POWER 75
+#define RTD1625_CRT_RSTN_PCIE2_NONSTICH 76
+#define RTD1625_CRT_RSTN_PCIE2_PHY_MDIO 77
+#define RTD1625_CRT_RSTN_DCPHY_UMCTL2 78
+#define RTD1625_CRT_RSTN_MIPI_DSI 79
+#define RTD1625_CRT_RSTN_HIFM 80
+#define RTD1625_CRT_RSTN_NSRAM 81
+#define RTD1625_CRT_RSTN_AUCPU0_REG 82
+#define RTD1625_CRT_RSTN_MDL_GENPW 83
+#define RTD1625_CRT_RSTN_MDL_CHIP 84
+#define RTD1625_CRT_RSTN_MDL_IP 85
+#define RTD1625_CRT_RSTN_TEST_MUX 86
+#define RTD1625_CRT_RSTN_ISO_BIST 87
+#define RTD1625_CRT_RSTN_MAIN_BIST 88
+#define RTD1625_CRT_RSTN_MAIN2_BIST 89
+#define RTD1625_CRT_RSTN_VE1_BIST 90
+#define RTD1625_CRT_RSTN_VE2_BIST 91
+#define RTD1625_CRT_RSTN_DCPHY_BIST 92
+#define RTD1625_CRT_RSTN_GPU_BIST 93
+#define RTD1625_CRT_RSTN_DISP_BIST 94
+#define RTD1625_CRT_RSTN_NPU_BIST 95
+#define RTD1625_CRT_RSTN_CAS_BIST 96
+#define RTD1625_CRT_RSTN_VE4_BIST 97
+#define RTD1625_CRT_RSTN_EMMC 98
+#define RTD1625_CRT_RSTN_GPU 99
+#define RTD1625_CRT_RSTN_VE2 100
+#define RTD1625_CRT_RSTN_UR1 101
+#define RTD1625_CRT_RSTN_UR2 102
+#define RTD1625_CRT_RSTN_UR3 103
+#define RTD1625_CRT_RSTN_UR4 104
+#define RTD1625_CRT_RSTN_UR5 105
+#define RTD1625_CRT_RSTN_UR6 106
+#define RTD1625_CRT_RSTN_UR7 107
+#define RTD1625_CRT_RSTN_UR8 108
+#define RTD1625_CRT_RSTN_UR9 109
+#define RTD1625_CRT_RSTN_UR_TOP 110
+#define RTD1625_CRT_RSTN_I2C_7 111
+#define RTD1625_CRT_RSTN_I2C_6 112
+#define RTD1625_CRT_RSTN_SPI0 113
+#define RTD1625_CRT_RSTN_SPI1 114
+#define RTD1625_CRT_RSTN_SPI2 115
+#define RTD1625_CRT_RSTN_LSADC0 116
+#define RTD1625_CRT_RSTN_LSADC1 117
+#define RTD1625_CRT_RSTN_ISOMIS_DMA 118
+#define RTD1625_CRT_RSTN_AUDIO_ADC 119
+#define RTD1625_CRT_RSTN_DPTX 120
+#define RTD1625_CRT_RSTN_AUCPU1_REG 121
+#define RTD1625_CRT_RSTN_EDPTX 122
+
+/* ISO reset */
+#define RTD1625_ISO_RSTN_VFD 0
+#define RTD1625_ISO_RSTN_CEC0 1
+#define RTD1625_ISO_RSTN_CEC1 2
+#define RTD1625_ISO_RSTN_CBUSTX 3
+#define RTD1625_ISO_RSTN_CBUSRX 4
+#define RTD1625_ISO_RSTN_USB3_PHY2_XTAL_POW 5
+#define RTD1625_ISO_RSTN_UR0 6
+#define RTD1625_ISO_RSTN_GMAC 7
+#define RTD1625_ISO_RSTN_GPHY 8
+#define RTD1625_ISO_RSTN_I2C_0 9
+#define RTD1625_ISO_RSTN_I2C_1 10
+#define RTD1625_ISO_RSTN_CBUS 11
+#define RTD1625_ISO_RSTN_USB_DRD 12
+#define RTD1625_ISO_RSTN_USB_HOST 13
+#define RTD1625_ISO_RSTN_USB_PHY_0 14
+#define RTD1625_ISO_RSTN_USB_PHY_1 15
+#define RTD1625_ISO_RSTN_USB_PHY_2 16
+#define RTD1625_ISO_RSTN_USB 17
+#define RTD1625_ISO_RSTN_TYPE_C 18
+#define RTD1625_ISO_RSTN_USB_U3_HOST 19
+#define RTD1625_ISO_RSTN_USB3_PHY0_POW 20
+#define RTD1625_ISO_RSTN_USB3_P0_MDIO 21
+#define RTD1625_ISO_RSTN_USB3_PHY1_POW 22
+#define RTD1625_ISO_RSTN_USB3_P1_MDIO 23
+#define RTD1625_ISO_RSTN_VTC 24
+#define RTD1625_ISO_RSTN_USB3_PHY2_POW 25
+#define RTD1625_ISO_RSTN_USB3_P2_MDIO 26
+#define RTD1625_ISO_RSTN_USB_PHY_3 27
+#define RTD1625_ISO_RSTN_USB_PHY_4 28
+
+/* ISO_S reset */
+#define RTD1625_ISO_S_RSTN_ISOM_MIS 0
+#define RTD1625_ISO_S_RSTN_GPIOM 1
+#define RTD1625_ISO_S_RSTN_TIMER7 2
+#define RTD1625_ISO_S_RSTN_IRDA 3
+#define RTD1625_ISO_S_RSTN_UR10 4
+
+#endif /* __DT_BINDINGS_RTK_RESET_RTD1625_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 09/10] clk: realtek: Add RTD1625-CRT clock controller driver
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the CRT (Clock, Reset, and Test) controller on the Realtek
RTD1625 SoC. This driver provides clock and reset management for the
system, allowing peripheral clients to request necessary resources for
operation.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Added '#include <dt-bindings/reset/realtek,rtd1625.h>'.
- Replaced rtk_reset_bank array with rtk_reset_desc descriptor.
- Implemented complete a mapping table for all reset IDs.
---
drivers/clk/realtek/Kconfig | 14 +
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-rtd1625-crt.c | 913 ++++++++++++++++++++++++++
3 files changed, 928 insertions(+)
create mode 100644 drivers/clk/realtek/clk-rtd1625-crt.c
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 5d790ce526ab..046f86af266d 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -28,4 +28,18 @@ config RTK_CLK_COMMON
config RTK_CLK_PLL_MMC
bool
+config COMMON_CLK_RTD1625
+ tristate "RTD1625 Clock Controller"
+ select RTK_CLK_COMMON
+ select RTK_CLK_PLL_MMC
+ default y
+ help
+ Support for the clock controller on Realtek RTD1625 SoCs.
+
+ This driver provides clock sources, gating, multiplexing, and
+ reset control for peripherals on the RTD1625 platform.
+
+ Say Y here if your system is based on the RTD1625 and you need
+ its peripheral devices to function.
+
endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index fd7d777902c8..c992f97dfbc7 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -9,3 +9,4 @@ clk-rtk-y += clk-regmap-mux.o
clk-rtk-y += freq_table.o
clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
+obj-$(CONFIG_COMMON_CLK_RTD1625) += clk-rtd1625-crt.o
diff --git a/drivers/clk/realtek/clk-rtd1625-crt.c b/drivers/clk/realtek/clk-rtd1625-crt.c
new file mode 100644
index 000000000000..a56978aef83a
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1625-crt.c
@@ -0,0 +1,913 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <dt-bindings/clock/realtek,rtd1625-clk.h>
+#include <dt-bindings/reset/realtek,rtd1625.h>
+#include <linux/platform_device.h>
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+
+#define RTD1625_CRT_CLK_MAX 172
+#define RTD1625_CRT_RSTN_MAX 123
+
+#define RTD1625_REG_PLL_ACPU1 0x10c
+#define RTD1625_REG_PLL_ACPU2 0x110
+#define RTD1625_REG_PLL_SSC_DIG_ACPU0 0x5c0
+#define RTD1625_REG_PLL_SSC_DIG_ACPU1 0x5c4
+#define RTD1625_REG_PLL_SSC_DIG_ACPU2 0x5c8
+#define RTD1625_REG_PLL_SSC_DIG_ACPU_DBG2 0x5dc
+
+#define RTD1625_REG_PLL_VE1_1 0x114
+#define RTD1625_REG_PLL_VE1_2 0x118
+#define RTD1625_REG_PLL_SSC_DIG_VE1_0 0x580
+#define RTD1625_REG_PLL_SSC_DIG_VE1_1 0x584
+#define RTD1625_REG_PLL_SSC_DIG_VE1_2 0x588
+#define RTD1625_REG_PLL_SSC_DIG_VE1_DBG2 0x59c
+
+#define RTD1625_REG_PLL_GPU1 0x1c0
+#define RTD1625_REG_PLL_GPU2 0x1c4
+#define RTD1625_REG_PLL_SSC_DIG_GPU0 0x5a0
+#define RTD1625_REG_PLL_SSC_DIG_GPU1 0x5a4
+#define RTD1625_REG_PLL_SSC_DIG_GPU2 0x5a8
+#define RTD1625_REG_PLL_SSC_DIG_GPU_DBG2 0x5bc
+
+#define RTD1625_REG_PLL_NPU1 0x1c8
+#define RTD1625_REG_PLL_NPU2 0x1cc
+#define RTD1625_REG_PLL_SSC_DIG_NPU0 0x800
+#define RTD1625_REG_PLL_SSC_DIG_NPU1 0x804
+#define RTD1625_REG_PLL_SSC_DIG_NPU2 0x808
+#define RTD1625_REG_PLL_SSC_DIG_NPU_DBG2 0x81c
+
+#define RTD1625_REG_PLL_VE2_1 0x1d0
+#define RTD1625_REG_PLL_VE2_2 0x1d4
+#define RTD1625_REG_PLL_SSC_DIG_VE2_0 0x5e0
+#define RTD1625_REG_PLL_SSC_DIG_VE2_1 0x5e4
+#define RTD1625_REG_PLL_SSC_DIG_VE2_2 0x5e8
+#define RTD1625_REG_PLL_SSC_DIG_VE2_DBG2 0x5fc
+
+#define RTD1625_REG_PLL_HIFI1 0x1d8
+#define RTD1625_REG_PLL_HIFI2 0x1dc
+#define RTD1625_REG_PLL_SSC_DIG_HIFI0 0x6e0
+#define RTD1625_REG_PLL_SSC_DIG_HIFI1 0x6e4
+#define RTD1625_REG_PLL_SSC_DIG_HIFI2 0x6e8
+#define RTD1625_REG_PLL_SSC_DIG_HIFI_DBG2 0x6fc
+
+#define RTD1625_REG_PLL_BUS1 0x524
+
+#define RTD1625_REG_PLL_SSC_DIG_DDSA1 0x564
+
+#define RTD1625_REG_PLL_SSC_DIG_DCSB1 0x544
+
+static const char * const clk_gpu_parents[] = {"pll_gpu", "clk_sys"};
+static CLK_REGMAP_MUX(clk_gpu, clk_gpu_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x28, 12, 0x1);
+static const char * const clk_ve_parents[] = {"pll_vo", "clk_sysh", "pll_ve1", "pll_ve2"};
+static CLK_REGMAP_MUX(clk_ve1, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 0, 0x3);
+static CLK_REGMAP_MUX(clk_ve2, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 3, 0x3);
+static CLK_REGMAP_MUX(clk_ve4, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 6, 0x3);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_misc, CLK_IS_CRITICAL, 0x50, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie0, 0, 0x50, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_gspi, 0, 0x50, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_iso_misc, 0, 0x50, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sds, 0, 0x50, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmi, 0, 0x50, 14, 1);
+static CLK_REGMAP_GATE(clk_en_gpu, "clk_gpu", CLK_SET_RATE_PARENT, 0x50, 18, 1);
+static CLK_REGMAP_GATE(clk_en_ve1, "clk_ve1", CLK_SET_RATE_PARENT, 0x50, 20, 1);
+static CLK_REGMAP_GATE(clk_en_ve2, "clk_ve2", CLK_SET_RATE_PARENT, 0x50, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_se, 0, 0x50, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_md, 0, 0x54, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tp, CLK_IS_CRITICAL, 0x54, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_rcic, 0, 0x54, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_nf, 0, 0x54, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_emmc, 0, 0x54, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sd, 0, 0x54, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sdio_ip, 0, 0x54, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mipi_csi, 0, 0x54, 18, 1);
+static CLK_REGMAP_GATE(clk_en_emmc_ip, "pll_emmc", CLK_SET_RATE_PARENT, 0x54, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sdio, 0, 0x54, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sd_ip, 0, 0x54, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tpb, 0, 0x54, 28, 1);
+static CLK_REGMAP_GATE(clk_en_misc_sc1, "clk_en_misc", 0, 0x54, 30, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_3, "clk_en_misc", 0, 0x58, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_jpeg, 0, 0x58, 4, 1);
+static CLK_REGMAP_GATE(clk_en_acpu, "pll_acpu", CLK_SET_RATE_PARENT,
+ 0x58, 6, 1);
+static CLK_REGMAP_GATE(clk_en_misc_sc0, "clk_en_misc", 0, 0x58, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmirx, 0, 0x58, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hse, CLK_IS_CRITICAL, 0x58, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_fan, 0, 0x5c, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_wrap_sys, 0, 0x5c, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_wrap_sysh, 0, 0x5c, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_mac_sysh, 0, 0x5c, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_r2rdsc, 0, 0x5c, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie1, 0, 0x5c, 18, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_4, "clk_en_misc", 0, 0x5c, 20, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_5, "clk_en_misc", 0, 0x5c, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tsio, 0, 0x5c, 24, 1);
+static CLK_REGMAP_GATE(clk_en_ve4, "clk_ve4", CLK_SET_RATE_PARENT,
+ 0x5c, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_edp, 0, 0x5c, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tsio_trx, 0, 0x5c, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie2, 0, 0x8c, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_earc, 0, 0x8c, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lite, 0, 0x8c, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mipi_dsi, 0, 0x8c, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npupp, 0, 0x8c, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npu, 0, 0x8c, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu0, 0, 0x8c, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu1, 0, 0x8c, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_nsram, 0, 0x8c, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmitop, 0, 0x8c, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu_iso_npu, 0, 0x8c, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_keyladder, 0, 0x8c, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ifcp_klm, 0, 0x8c, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ifcp, 0, 0x8c, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_genpw, 0, 0xb0, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_chip, 0, 0xb0, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_ip, 0, 0xb0, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdlm2m, 0, 0xb0, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_xtal, 0, 0xb0, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_test_mux, 0, 0xb0, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_dla, 0, 0xb0, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tpcw, 0, 0xb0, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_gpu_ts_src, 0, 0xb0, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_vi, 0, 0xb0, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lvds1, 0, 0xb0, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lvds2, 0, 0xb0, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu, 0, 0xb0, 28, 1);
+static CLK_REGMAP_GATE(clk_en_ur1, "clk_en_ur_top", 0, 0x884, 0, 1);
+static CLK_REGMAP_GATE(clk_en_ur2, "clk_en_ur_top", 0, 0x884, 2, 1);
+static CLK_REGMAP_GATE(clk_en_ur3, "clk_en_ur_top", 0, 0x884, 4, 1);
+static CLK_REGMAP_GATE(clk_en_ur4, "clk_en_ur_top", 0, 0x884, 6, 1);
+static CLK_REGMAP_GATE(clk_en_ur5, "clk_en_ur_top", 0, 0x884, 8, 1);
+static CLK_REGMAP_GATE(clk_en_ur6, "clk_en_ur_top", 0, 0x884, 10, 1);
+static CLK_REGMAP_GATE(clk_en_ur7, "clk_en_ur_top", 0, 0x884, 12, 1);
+static CLK_REGMAP_GATE(clk_en_ur8, "clk_en_ur_top", 0, 0x884, 14, 1);
+static CLK_REGMAP_GATE(clk_en_ur9, "clk_en_ur_top", 0, 0x884, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ur_top, CLK_IS_CRITICAL, 0x884, 18, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_7, "clk_en_misc", 0, 0x884, 28, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_6, "clk_en_misc", 0, 0x884, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi0, 0, 0x894, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi1, 0, 0x894, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi2, 0, 0x894, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lsadc0, 0, 0x894, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lsadc1, 0, 0x894, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_isomis_dma, 0, 0x894, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_dptx, 0, 0x894, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npu_mipi_csi, 0, 0x894, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_edptx, 0, 0x894, 28, 1);
+
+#define FREQ_NF_MASK 0x7ffff
+#define FREQ_NF(_r, _nf) {.rate = _r, .val = (_nf),}
+
+static const struct freq_table acpu_tbl[] = {
+ FREQ_NF(513000000, 0x11000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table ve_tbl[] = {
+ FREQ_NF(553500000, 0x12800),
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(688500000, 0x17800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table bus_tbl[] = {
+ FREQ_NF(513000000, 0x11000),
+ FREQ_NF(540000000, 0x12000),
+ FREQ_NF(553500000, 0x12800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table ddsa_tbl[] = {
+ FREQ_NF(432000000, 0xe000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table gpu_tbl[] = {
+ FREQ_NF(405000000, 0xd000),
+ FREQ_NF(540000000, 0x12000),
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(729000000, 0x19000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_NF(850500000, 0x1d800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table hifi_tbl[] = {
+ FREQ_NF(756000000, 0x1a000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table npu_tbl[] = {
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(729000000, 0x19000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_TABLE_END
+};
+
+static const struct reg_sequence pll_acpu_seq_power_on[] = {
+ {RTD1625_REG_PLL_ACPU2, 0x5},
+ {RTD1625_REG_PLL_ACPU2, 0x7},
+ {RTD1625_REG_PLL_ACPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU2, 0x1e1f8e},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x5, 200},
+ {RTD1625_REG_PLL_ACPU2, 0x3},
+};
+
+static const struct reg_sequence pll_acpu_seq_power_off[] = {
+ {RTD1625_REG_PLL_ACPU2, 0x4},
+};
+
+static const struct reg_sequence pll_acpu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x4},
+};
+
+static const struct reg_sequence pll_acpu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x5},
+};
+
+static struct clk_pll pll_acpu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_acpu", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_acpu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_acpu_seq_power_on),
+ .seq_power_off = pll_acpu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_acpu_seq_power_off),
+ .seq_pre_set_freq = pll_acpu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_acpu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_acpu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_acpu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_ACPU1,
+ .freq_tbl = acpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_ACPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_ACPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static const struct reg_sequence pll_ve1_seq_power_on[] = {
+ {RTD1625_REG_PLL_VE1_2, 0x5},
+ {RTD1625_REG_PLL_VE1_2, 0x7},
+ {RTD1625_REG_PLL_VE1_1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x5, 200},
+ {RTD1625_REG_PLL_VE1_2, 0x3},
+};
+
+static const struct reg_sequence pll_ve1_seq_power_off[] = {
+ {RTD1625_REG_PLL_VE1_2, 0x4},
+};
+
+static const struct reg_sequence pll_ve1_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x4},
+};
+
+static const struct reg_sequence pll_ve1_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x5},
+};
+
+static struct clk_pll pll_ve1 = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ve1", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_ve1_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_ve1_seq_power_on),
+ .seq_power_off = pll_ve1_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_ve1_seq_power_off),
+ .seq_pre_set_freq = pll_ve1_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_ve1_seq_pre_set_freq),
+ .seq_post_set_freq = pll_ve1_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_ve1_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_VE1_1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_VE1_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_VE1_2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static struct clk_pll pll_ddsa = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ddsa", "osc27m", &rtk_clk_pll_ro_ops,
+ CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_DDSA1,
+ .freq_tbl = ddsa_tbl,
+ .freq_mask = FREQ_NF_MASK,
+};
+
+static struct clk_pll pll_bus = {
+ .clkr.hw.init = CLK_HW_INIT("pll_bus", "osc27m", &rtk_clk_pll_ro_ops, CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_BUS1,
+ .freq_tbl = bus_tbl,
+ .freq_mask = FREQ_NF_MASK,
+};
+
+static CLK_FIXED_FACTOR(clk_sys, "clk_sys", "pll_bus", 2, 1, 0);
+
+static struct clk_pll pll_dcsb = {
+ .clkr.hw.init = CLK_HW_INIT("pll_dcsb", "osc27m", &rtk_clk_pll_ro_ops,
+ CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_DCSB1,
+ .freq_tbl = bus_tbl,
+ .freq_mask = FREQ_NF_MASK,
+};
+
+static CLK_FIXED_FACTOR(clk_sysh, "clk_sysh", "pll_dcsb", 1, 1, 0);
+
+static const struct reg_sequence pll_gpu_seq_power_on[] = {
+ {RTD1625_REG_PLL_GPU2, 0x5},
+ {RTD1625_REG_PLL_GPU2, 0x7},
+ {RTD1625_REG_PLL_GPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x5, 200},
+ {RTD1625_REG_PLL_GPU2, 0x3},
+};
+
+static const struct reg_sequence pll_gpu_seq_power_off[] = {
+ {RTD1625_REG_PLL_GPU2, 0x4},
+};
+
+static const struct reg_sequence pll_gpu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x4},
+};
+
+static const struct reg_sequence pll_gpu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x5},
+};
+
+static struct clk_pll pll_gpu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_gpu", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_gpu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_gpu_seq_power_on),
+ .seq_power_off = pll_gpu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_gpu_seq_power_off),
+ .seq_pre_set_freq = pll_gpu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_gpu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_gpu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_gpu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_GPU1,
+ .freq_tbl = gpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_GPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_GPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static const struct reg_sequence pll_npu_seq_power_on[] = {
+ {RTD1625_REG_PLL_NPU2, 0x5},
+ {RTD1625_REG_PLL_NPU2, 0x7},
+ {RTD1625_REG_PLL_NPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x5, 200},
+ {RTD1625_REG_PLL_NPU2, 0x3},
+};
+
+static const struct reg_sequence pll_npu_seq_power_off[] = {
+ {RTD1625_REG_PLL_NPU2, 0x4},
+ {RTD1625_REG_PLL_NPU1, 0x54010},
+};
+
+static const struct reg_sequence pll_npu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x4},
+};
+
+static const struct reg_sequence pll_npu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x5},
+};
+
+static struct clk_pll pll_npu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_npu", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_npu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_npu_seq_power_on),
+ .seq_power_off = pll_npu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_npu_seq_power_off),
+ .seq_pre_set_freq = pll_npu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_npu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_npu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_npu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_NPU1,
+ .freq_tbl = npu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_NPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_NPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static CLK_FIXED_FACTOR(clk_npu, "clk_npu", "pll_npu", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(clk_npu_mipi_csi, "clk_npu_mipi_csi", "pll_npu", 1, 1,
+ CLK_SET_RATE_PARENT);
+
+static const struct reg_sequence pll_ve2_seq_power_on[] = {
+ {RTD1625_REG_PLL_VE2_2, 0x5},
+ {RTD1625_REG_PLL_VE2_2, 0x7},
+ {RTD1625_REG_PLL_VE2_1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x5, 200},
+ {RTD1625_REG_PLL_VE2_2, 0x3},
+};
+
+static const struct reg_sequence pll_ve2_seq_power_off[] = {
+ {RTD1625_REG_PLL_VE2_2, 0x4},
+};
+
+static const struct reg_sequence pll_ve2_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x4},
+};
+
+static const struct reg_sequence pll_ve2_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x5},
+};
+
+static struct clk_pll pll_ve2 = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ve2", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_ve2_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_ve2_seq_power_on),
+ .seq_power_off = pll_ve2_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_ve2_seq_power_off),
+ .seq_pre_set_freq = pll_ve2_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_ve2_seq_pre_set_freq),
+ .seq_post_set_freq = pll_ve2_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_ve2_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_VE2_1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_VE2_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_VE2_2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static const struct reg_sequence pll_hifi_seq_power_on[] = {
+ {RTD1625_REG_PLL_HIFI2, 0x5},
+ {RTD1625_REG_PLL_HIFI2, 0x7},
+ {RTD1625_REG_PLL_HIFI1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x5, 200},
+ {RTD1625_REG_PLL_HIFI2, 0x3},
+};
+
+static const struct reg_sequence pll_hifi_seq_power_off[] = {
+ {RTD1625_REG_PLL_HIFI2, 0x4},
+};
+
+static const struct reg_sequence pll_hifi_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x4},
+};
+
+static const struct reg_sequence pll_hifi_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x5},
+};
+
+static struct clk_pll pll_hifi = {
+ .clkr.hw.init = CLK_HW_INIT("pll_hifi", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_hifi_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_hifi_seq_power_on),
+ .seq_power_off = pll_hifi_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_hifi_seq_power_off),
+ .seq_pre_set_freq = pll_hifi_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_hifi_seq_pre_set_freq),
+ .seq_post_set_freq = pll_hifi_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_hifi_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_HIFI1,
+ .freq_tbl = hifi_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_HIFI_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_HIFI2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+};
+
+static CLK_FIXED_FACTOR(pll_emmc_ref, "pll_emmc_ref", "osc27m", 1, 1, 0);
+
+static struct clk_pll_mmc pll_emmc = {
+ .pll_ofs = 0x1f0,
+ .ssc_dig_ofs = 0x6b0,
+ .clkr.hw.init = CLK_HW_INIT("pll_emmc", "pll_emmc_ref", &rtk_clk_pll_mmc_ops, 0),
+ .phase0_hw.init = CLK_HW_INIT("pll_emmc_vp0", "pll_emmc", &rtk_clk_pll_mmc_phase_ops, 0),
+ .phase1_hw.init = CLK_HW_INIT("pll_emmc_vp1", "pll_emmc", &rtk_clk_pll_mmc_phase_ops, 0),
+};
+
+static struct clk_regmap *rtd1625_crt_regmap_clks[] = {
+ &clk_en_misc.clkr,
+ &clk_en_pcie0.clkr,
+ &clk_en_gspi.clkr,
+ &clk_en_iso_misc.clkr,
+ &clk_en_sds.clkr,
+ &clk_en_hdmi.clkr,
+ &clk_en_gpu.clkr,
+ &clk_en_ve1.clkr,
+ &clk_en_ve2.clkr,
+ &clk_en_se.clkr,
+ &clk_en_md.clkr,
+ &clk_en_tp.clkr,
+ &clk_en_rcic.clkr,
+ &clk_en_nf.clkr,
+ &clk_en_emmc.clkr,
+ &clk_en_sd.clkr,
+ &clk_en_sdio_ip.clkr,
+ &clk_en_mipi_csi.clkr,
+ &clk_en_emmc_ip.clkr,
+ &clk_en_sdio.clkr,
+ &clk_en_sd_ip.clkr,
+ &clk_en_tpb.clkr,
+ &clk_en_misc_sc1.clkr,
+ &clk_en_misc_i2c_3.clkr,
+ &clk_en_jpeg.clkr,
+ &clk_en_acpu.clkr,
+ &clk_en_misc_sc0.clkr,
+ &clk_en_hdmirx.clkr,
+ &clk_en_hse.clkr,
+ &clk_en_fan.clkr,
+ &clk_en_sata_wrap_sys.clkr,
+ &clk_en_sata_wrap_sysh.clkr,
+ &clk_en_sata_mac_sysh.clkr,
+ &clk_en_r2rdsc.clkr,
+ &clk_en_pcie1.clkr,
+ &clk_en_misc_i2c_4.clkr,
+ &clk_en_misc_i2c_5.clkr,
+ &clk_en_tsio.clkr,
+ &clk_en_ve4.clkr,
+ &clk_en_edp.clkr,
+ &clk_en_tsio_trx.clkr,
+ &clk_en_pcie2.clkr,
+ &clk_en_earc.clkr,
+ &clk_en_lite.clkr,
+ &clk_en_mipi_dsi.clkr,
+ &clk_en_npupp.clkr,
+ &clk_en_npu.clkr,
+ &clk_en_aucpu0.clkr,
+ &clk_en_aucpu1.clkr,
+ &clk_en_nsram.clkr,
+ &clk_en_hdmitop.clkr,
+ &clk_en_aucpu_iso_npu.clkr,
+ &clk_en_keyladder.clkr,
+ &clk_en_ifcp_klm.clkr,
+ &clk_en_ifcp.clkr,
+ &clk_en_mdl_genpw.clkr,
+ &clk_en_mdl_chip.clkr,
+ &clk_en_mdl_ip.clkr,
+ &clk_en_mdlm2m.clkr,
+ &clk_en_mdl_xtal.clkr,
+ &clk_en_test_mux.clkr,
+ &clk_en_dla.clkr,
+ &clk_en_tpcw.clkr,
+ &clk_en_gpu_ts_src.clkr,
+ &clk_en_vi.clkr,
+ &clk_en_lvds1.clkr,
+ &clk_en_lvds2.clkr,
+ &clk_en_aucpu.clkr,
+ &clk_en_ur1.clkr,
+ &clk_en_ur2.clkr,
+ &clk_en_ur3.clkr,
+ &clk_en_ur4.clkr,
+ &clk_en_ur5.clkr,
+ &clk_en_ur6.clkr,
+ &clk_en_ur7.clkr,
+ &clk_en_ur8.clkr,
+ &clk_en_ur9.clkr,
+ &clk_en_ur_top.clkr,
+ &clk_en_misc_i2c_7.clkr,
+ &clk_en_misc_i2c_6.clkr,
+ &clk_en_spi0.clkr,
+ &clk_en_spi1.clkr,
+ &clk_en_spi2.clkr,
+ &clk_en_lsadc0.clkr,
+ &clk_en_lsadc1.clkr,
+ &clk_en_isomis_dma.clkr,
+ &clk_en_dptx.clkr,
+ &clk_en_npu_mipi_csi.clkr,
+ &clk_en_edptx.clkr,
+ &clk_gpu.clkr,
+ &clk_ve1.clkr,
+ &clk_ve2.clkr,
+ &clk_ve4.clkr,
+ &pll_ve1.clkr,
+ &pll_ddsa.clkr,
+ &pll_bus.clkr,
+ &pll_dcsb.clkr,
+ &pll_gpu.clkr,
+ &pll_npu.clkr,
+ &pll_ve2.clkr,
+ &pll_hifi.clkr,
+ &pll_emmc.clkr,
+ &pll_acpu.clkr,
+};
+
+static struct rtk_reset_desc rtd1625_crt_reset_descs[] = {
+ /* Bank 0: offset 0x0 */
+ [RTD1625_CRT_RSTN_MISC] = { .ofs = 0x0, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DIP] = { .ofs = 0x0, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GSPI] = { .ofs = 0x0, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS] = { .ofs = 0x0, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS_REG] = { .ofs = 0x0, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS_PHY] = { .ofs = 0x0, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GPU2D] = { .ofs = 0x0, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DC_PHY] = { .ofs = 0x0, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_CRT] = { .ofs = 0x0, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC] = { .ofs = 0x0, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SE] = { .ofs = 0x0, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DLA] = { .ofs = 0x0, .bit = 30, .write_en = 1 },
+ /* Bank 1: offset 0x4 */
+ [RTD1625_CRT_RSTN_JPEG] = { .ofs = 0x4, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SD] = { .ofs = 0x4, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDIO] = { .ofs = 0x4, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCR_CNT] = { .ofs = 0x4, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_STITCH] = { .ofs = 0x4, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_PHY] = { .ofs = 0x4, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0] = { .ofs = 0x4, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_CORE] = { .ofs = 0x4, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_POWER] = { .ofs = 0x4, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_NONSTICH] = { .ofs = 0x4, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_PHY_MDIO] = { .ofs = 0x4, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_SGMII_MDIO] = { .ofs = 0x4, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VO2] = { .ofs = 0x4, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MISC_SC0] = { .ofs = 0x4, .bit = 30, .write_en = 1 },
+ /* Bank 2: offset 0x8 */
+ [RTD1625_CRT_RSTN_MD] = { .ofs = 0x8, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LVDS1] = { .ofs = 0x8, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LVDS2] = { .ofs = 0x8, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MISC_SC1] = { .ofs = 0x8, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_3] = { .ofs = 0x8, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_FAN] = { .ofs = 0x8, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TVE] = { .ofs = 0x8, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AIO] = { .ofs = 0x8, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VO] = { .ofs = 0x8, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MIPI_CSI] = { .ofs = 0x8, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMIRX] = { .ofs = 0x8, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMIRX_WRAP] = { .ofs = 0x8, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMI] = { .ofs = 0x8, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DISP] = { .ofs = 0x8, .bit = 30, .write_en = 1 },
+ /* Bank 3: offset 0xc */
+ [RTD1625_CRT_RSTN_SATA_PHY_POW1] = { .ofs = 0xc, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_PHY_POW0] = { .ofs = 0xc, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MDIO1] = { .ofs = 0xc, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MDIO0] = { .ofs = 0xc, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_WRAP] = { .ofs = 0xc, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_P1] = { .ofs = 0xc, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_P0] = { .ofs = 0xc, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_COM] = { .ofs = 0xc, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_STITCH] = { .ofs = 0xc, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_PHY] = { .ofs = 0xc, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1] = { .ofs = 0xc, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_CORE] = { .ofs = 0xc, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_POWER] = { .ofs = 0xc, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_NONSTICH] = { .ofs = 0xc, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_PHY_MDIO] = { .ofs = 0xc, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMITOP] = { .ofs = 0xc, .bit = 30, .write_en = 1 },
+ /* Bank 4: offset 0x68 */
+ [RTD1625_CRT_RSTN_I2C_4] = { .ofs = 0x68, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_5] = { .ofs = 0x68, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TSIO] = { .ofs = 0x68, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VI] = { .ofs = 0x68, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EDP] = { .ofs = 0x68, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_MMU] = { .ofs = 0x68, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_MMU_FUNC] = { .ofs = 0x68, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HSE_MMU] = { .ofs = 0x68, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HSE_MMU_FUNC] = { .ofs = 0x68, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDLM2M] = { .ofs = 0x68, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_ISO_GSPI] = { .ofs = 0x68, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SOFT_NPU] = { .ofs = 0x68, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI2EMMC] = { .ofs = 0x68, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EARC] = { .ofs = 0x68, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1] = { .ofs = 0x68, .bit = 30, .write_en = 1 },
+ /* Bank 5: offset 0x90 */
+ [RTD1625_CRT_RSTN_PCIE2_STITCH] = { .ofs = 0x90, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_PHY] = { .ofs = 0x90, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2] = { .ofs = 0x90, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_CORE] = { .ofs = 0x90, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_POWER] = { .ofs = 0x90, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_NONSTICH] = { .ofs = 0x90, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_PHY_MDIO] = { .ofs = 0x90, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_UMCTL2] = { .ofs = 0x90, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MIPI_DSI] = { .ofs = 0x90, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HIFM] = { .ofs = 0x90, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_NSRAM] = { .ofs = 0x90, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUCPU0_REG] = { .ofs = 0x90, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_GENPW] = { .ofs = 0x90, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_CHIP] = { .ofs = 0x90, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_IP] = { .ofs = 0x90, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TEST_MUX] = { .ofs = 0x90, .bit = 30, .write_en = 1 },
+ /* Bank 6: offset 0xb8 */
+ [RTD1625_CRT_RSTN_ISO_BIST] = { .ofs = 0xb8, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MAIN_BIST] = { .ofs = 0xb8, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MAIN2_BIST] = { .ofs = 0xb8, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_BIST] = { .ofs = 0xb8, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE2_BIST] = { .ofs = 0xb8, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_BIST] = { .ofs = 0xb8, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GPU_BIST] = { .ofs = 0xb8, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DISP_BIST] = { .ofs = 0xb8, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_NPU_BIST] = { .ofs = 0xb8, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_CAS_BIST] = { .ofs = 0xb8, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE4_BIST] = { .ofs = 0xb8, .bit = 20, .write_en = 1 },
+ /* Bank 7: offset 0x454 (DUMMY0, no write_en) */
+ [RTD1625_CRT_RSTN_EMMC] = { .ofs = 0x454, .bit = 0 },
+ /* Bank 8: offset 0x458 (DUMMY1, no write_en) */
+ [RTD1625_CRT_RSTN_GPU] = { .ofs = 0x458, .bit = 0 },
+ /* Bank 9: offset 0x464 (DUMMY4, no write_en) */
+ [RTD1625_CRT_RSTN_VE2] = { .ofs = 0x464, .bit = 0 },
+ /* Bank 10: offset 0x880 */
+ [RTD1625_CRT_RSTN_UR1] = { .ofs = 0x880, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR2] = { .ofs = 0x880, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR3] = { .ofs = 0x880, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR4] = { .ofs = 0x880, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR5] = { .ofs = 0x880, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR6] = { .ofs = 0x880, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR7] = { .ofs = 0x880, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR8] = { .ofs = 0x880, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR9] = { .ofs = 0x880, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR_TOP] = { .ofs = 0x880, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_7] = { .ofs = 0x880, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_6] = { .ofs = 0x880, .bit = 30, .write_en = 1 },
+ /* Bank 11: offset 0x890 */
+ [RTD1625_CRT_RSTN_SPI0] = { .ofs = 0x890, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI1] = { .ofs = 0x890, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI2] = { .ofs = 0x890, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC0] = { .ofs = 0x890, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC1] = { .ofs = 0x890, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_ISOMIS_DMA] = { .ofs = 0x890, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUDIO_ADC] = { .ofs = 0x890, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DPTX] = { .ofs = 0x890, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUCPU1_REG] = { .ofs = 0x890, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EDPTX] = { .ofs = 0x890, .bit = 28, .write_en = 1 },
+};
+
+static struct clk_hw_onecell_data rtd1625_crt_hw_data = {
+ .num = RTD1625_CRT_CLK_MAX,
+ .hws = {
+ [RTD1625_CRT_CLK_EN_MISC] = &__clk_regmap_gate_hw(&clk_en_misc),
+ [RTD1625_CRT_CLK_EN_PCIE0] = &__clk_regmap_gate_hw(&clk_en_pcie0),
+ [RTD1625_CRT_CLK_EN_GSPI] = &__clk_regmap_gate_hw(&clk_en_gspi),
+ [RTD1625_CRT_CLK_EN_ISO_MISC] = &__clk_regmap_gate_hw(&clk_en_iso_misc),
+ [RTD1625_CRT_CLK_EN_SDS] = &__clk_regmap_gate_hw(&clk_en_sds),
+ [RTD1625_CRT_CLK_EN_HDMI] = &__clk_regmap_gate_hw(&clk_en_hdmi),
+ [RTD1625_CRT_CLK_EN_GPU] = &__clk_regmap_gate_hw(&clk_en_gpu),
+ [RTD1625_CRT_CLK_EN_VE1] = &__clk_regmap_gate_hw(&clk_en_ve1),
+ [RTD1625_CRT_CLK_EN_VE2] = &__clk_regmap_gate_hw(&clk_en_ve2),
+ [RTD1625_CRT_CLK_EN_MD] = &__clk_regmap_gate_hw(&clk_en_md),
+ [RTD1625_CRT_CLK_EN_TP] = &__clk_regmap_gate_hw(&clk_en_tp),
+ [RTD1625_CRT_CLK_EN_RCIC] = &__clk_regmap_gate_hw(&clk_en_rcic),
+ [RTD1625_CRT_CLK_EN_NF] = &__clk_regmap_gate_hw(&clk_en_nf),
+ [RTD1625_CRT_CLK_EN_EMMC] = &__clk_regmap_gate_hw(&clk_en_emmc),
+ [RTD1625_CRT_CLK_EN_SD] = &__clk_regmap_gate_hw(&clk_en_sd),
+ [RTD1625_CRT_CLK_EN_SDIO_IP] = &__clk_regmap_gate_hw(&clk_en_sdio_ip),
+ [RTD1625_CRT_CLK_EN_MIPI_CSI] = &__clk_regmap_gate_hw(&clk_en_mipi_csi),
+ [RTD1625_CRT_CLK_EN_EMMC_IP] = &__clk_regmap_gate_hw(&clk_en_emmc_ip),
+ [RTD1625_CRT_CLK_EN_SDIO] = &__clk_regmap_gate_hw(&clk_en_sdio),
+ [RTD1625_CRT_CLK_EN_SD_IP] = &__clk_regmap_gate_hw(&clk_en_sd_ip),
+ [RTD1625_CRT_CLK_EN_TPB] = &__clk_regmap_gate_hw(&clk_en_tpb),
+ [RTD1625_CRT_CLK_EN_MISC_SC1] = &__clk_regmap_gate_hw(&clk_en_misc_sc1),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_3] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_3),
+ [RTD1625_CRT_CLK_EN_ACPU] = &__clk_regmap_gate_hw(&clk_en_acpu),
+ [RTD1625_CRT_CLK_EN_JPEG] = &__clk_regmap_gate_hw(&clk_en_jpeg),
+ [RTD1625_CRT_CLK_EN_MISC_SC0] = &__clk_regmap_gate_hw(&clk_en_misc_sc0),
+ [RTD1625_CRT_CLK_EN_HDMIRX] = &__clk_regmap_gate_hw(&clk_en_hdmirx),
+ [RTD1625_CRT_CLK_EN_HSE] = &__clk_regmap_gate_hw(&clk_en_hse),
+ [RTD1625_CRT_CLK_EN_FAN] = &__clk_regmap_gate_hw(&clk_en_fan),
+ [RTD1625_CRT_CLK_EN_SATA_WRAP_SYS] = &__clk_regmap_gate_hw(&clk_en_sata_wrap_sys),
+ [RTD1625_CRT_CLK_EN_SATA_WRAP_SYSH] = &__clk_regmap_gate_hw(&clk_en_sata_wrap_sysh),
+ [RTD1625_CRT_CLK_EN_SATA_MAC_SYSH] = &__clk_regmap_gate_hw(&clk_en_sata_mac_sysh),
+ [RTD1625_CRT_CLK_EN_R2RDSC] = &__clk_regmap_gate_hw(&clk_en_r2rdsc),
+ [RTD1625_CRT_CLK_EN_PCIE1] = &__clk_regmap_gate_hw(&clk_en_pcie1),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_4] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_4),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_5] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_5),
+ [RTD1625_CRT_CLK_EN_TSIO] = &__clk_regmap_gate_hw(&clk_en_tsio),
+ [RTD1625_CRT_CLK_EN_VE4] = &__clk_regmap_gate_hw(&clk_en_ve4),
+ [RTD1625_CRT_CLK_EN_EDP] = &__clk_regmap_gate_hw(&clk_en_edp),
+ [RTD1625_CRT_CLK_EN_TSIO_TRX] = &__clk_regmap_gate_hw(&clk_en_tsio_trx),
+ [RTD1625_CRT_CLK_EN_PCIE2] = &__clk_regmap_gate_hw(&clk_en_pcie2),
+ [RTD1625_CRT_CLK_EN_EARC] = &__clk_regmap_gate_hw(&clk_en_earc),
+ [RTD1625_CRT_CLK_EN_LITE] = &__clk_regmap_gate_hw(&clk_en_lite),
+ [RTD1625_CRT_CLK_EN_MIPI_DSI] = &__clk_regmap_gate_hw(&clk_en_mipi_dsi),
+ [RTD1625_CRT_CLK_EN_NPUPP] = &__clk_regmap_gate_hw(&clk_en_npupp),
+ [RTD1625_CRT_CLK_EN_NPU] = &__clk_regmap_gate_hw(&clk_en_npu),
+ [RTD1625_CRT_CLK_EN_AUCPU0] = &__clk_regmap_gate_hw(&clk_en_aucpu0),
+ [RTD1625_CRT_CLK_EN_AUCPU1] = &__clk_regmap_gate_hw(&clk_en_aucpu1),
+ [RTD1625_CRT_CLK_EN_NSRAM] = &__clk_regmap_gate_hw(&clk_en_nsram),
+ [RTD1625_CRT_CLK_EN_HDMITOP] = &__clk_regmap_gate_hw(&clk_en_hdmitop),
+ [RTD1625_CRT_CLK_EN_AUCPU_ISO_NPU] = &__clk_regmap_gate_hw(&clk_en_aucpu_iso_npu),
+ [RTD1625_CRT_CLK_EN_KEYLADDER] = &__clk_regmap_gate_hw(&clk_en_keyladder),
+ [RTD1625_CRT_CLK_EN_IFCP_KLM] = &__clk_regmap_gate_hw(&clk_en_ifcp_klm),
+ [RTD1625_CRT_CLK_EN_IFCP] = &__clk_regmap_gate_hw(&clk_en_ifcp),
+ [RTD1625_CRT_CLK_EN_MDL_GENPW] = &__clk_regmap_gate_hw(&clk_en_mdl_genpw),
+ [RTD1625_CRT_CLK_EN_MDL_CHIP] = &__clk_regmap_gate_hw(&clk_en_mdl_chip),
+ [RTD1625_CRT_CLK_EN_MDL_IP] = &__clk_regmap_gate_hw(&clk_en_mdl_ip),
+ [RTD1625_CRT_CLK_EN_MDLM2M] = &__clk_regmap_gate_hw(&clk_en_mdlm2m),
+ [RTD1625_CRT_CLK_EN_MDL_XTAL] = &__clk_regmap_gate_hw(&clk_en_mdl_xtal),
+ [RTD1625_CRT_CLK_EN_TEST_MUX] = &__clk_regmap_gate_hw(&clk_en_test_mux),
+ [RTD1625_CRT_CLK_EN_DLA] = &__clk_regmap_gate_hw(&clk_en_dla),
+ [RTD1625_CRT_CLK_EN_TPCW] = &__clk_regmap_gate_hw(&clk_en_tpcw),
+ [RTD1625_CRT_CLK_EN_GPU_TS_SRC] = &__clk_regmap_gate_hw(&clk_en_gpu_ts_src),
+ [RTD1625_CRT_CLK_EN_VI] = &__clk_regmap_gate_hw(&clk_en_vi),
+ [RTD1625_CRT_CLK_EN_LVDS1] = &__clk_regmap_gate_hw(&clk_en_lvds1),
+ [RTD1625_CRT_CLK_EN_LVDS2] = &__clk_regmap_gate_hw(&clk_en_lvds2),
+ [RTD1625_CRT_CLK_EN_AUCPU] = &__clk_regmap_gate_hw(&clk_en_aucpu),
+ [RTD1625_CRT_CLK_EN_UR1] = &__clk_regmap_gate_hw(&clk_en_ur1),
+ [RTD1625_CRT_CLK_EN_UR2] = &__clk_regmap_gate_hw(&clk_en_ur2),
+ [RTD1625_CRT_CLK_EN_UR3] = &__clk_regmap_gate_hw(&clk_en_ur3),
+ [RTD1625_CRT_CLK_EN_UR4] = &__clk_regmap_gate_hw(&clk_en_ur4),
+ [RTD1625_CRT_CLK_EN_UR5] = &__clk_regmap_gate_hw(&clk_en_ur5),
+ [RTD1625_CRT_CLK_EN_UR6] = &__clk_regmap_gate_hw(&clk_en_ur6),
+ [RTD1625_CRT_CLK_EN_UR7] = &__clk_regmap_gate_hw(&clk_en_ur7),
+ [RTD1625_CRT_CLK_EN_UR8] = &__clk_regmap_gate_hw(&clk_en_ur8),
+ [RTD1625_CRT_CLK_EN_UR9] = &__clk_regmap_gate_hw(&clk_en_ur9),
+ [RTD1625_CRT_CLK_EN_UR_TOP] = &__clk_regmap_gate_hw(&clk_en_ur_top),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_7] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_7),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_6] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_6),
+ [RTD1625_CRT_CLK_EN_SPI0] = &__clk_regmap_gate_hw(&clk_en_spi0),
+ [RTD1625_CRT_CLK_EN_SPI1] = &__clk_regmap_gate_hw(&clk_en_spi1),
+ [RTD1625_CRT_CLK_EN_SPI2] = &__clk_regmap_gate_hw(&clk_en_spi2),
+ [RTD1625_CRT_CLK_EN_LSADC0] = &__clk_regmap_gate_hw(&clk_en_lsadc0),
+ [RTD1625_CRT_CLK_EN_LSADC1] = &__clk_regmap_gate_hw(&clk_en_lsadc1),
+ [RTD1625_CRT_CLK_EN_ISOMIS_DMA] = &__clk_regmap_gate_hw(&clk_en_isomis_dma),
+ [RTD1625_CRT_CLK_EN_DPTX] = &__clk_regmap_gate_hw(&clk_en_dptx),
+ [RTD1625_CRT_CLK_EN_NPU_MIPI_CSI] = &__clk_regmap_gate_hw(&clk_en_npu_mipi_csi),
+ [RTD1625_CRT_CLK_EN_EDPTX] = &__clk_regmap_gate_hw(&clk_en_edptx),
+ [RTD1625_CRT_CLK_GPU] = &__clk_regmap_mux_hw(&clk_gpu),
+ [RTD1625_CRT_CLK_VE1] = &__clk_regmap_mux_hw(&clk_ve1),
+ [RTD1625_CRT_CLK_VE2] = &__clk_regmap_mux_hw(&clk_ve2),
+ [RTD1625_CRT_CLK_VE4] = &__clk_regmap_mux_hw(&clk_ve4),
+ [RTD1625_CRT_PLL_VE1] = &__clk_pll_hw(&pll_ve1),
+ [RTD1625_CRT_PLL_DDSA] = &__clk_pll_hw(&pll_ddsa),
+ [RTD1625_CRT_PLL_BUS] = &__clk_pll_hw(&pll_bus),
+ [RTD1625_CRT_CLK_SYS] = &clk_sys.hw,
+ [RTD1625_CRT_PLL_DCSB] = &__clk_pll_hw(&pll_dcsb),
+ [RTD1625_CRT_CLK_SYSH] = &clk_sysh.hw,
+ [RTD1625_CRT_PLL_GPU] = &__clk_pll_hw(&pll_gpu),
+ [RTD1625_CRT_PLL_NPU] = &__clk_pll_hw(&pll_npu),
+ [RTD1625_CRT_PLL_VE2] = &__clk_pll_hw(&pll_ve2),
+ [RTD1625_CRT_PLL_HIFI] = &__clk_pll_hw(&pll_hifi),
+ [RTD1625_CRT_PLL_EMMC_REF] = &pll_emmc_ref.hw,
+ [RTD1625_CRT_PLL_EMMC] = &__clk_pll_mmc_hw(&pll_emmc),
+ [RTD1625_CRT_PLL_EMMC_VP0] = &pll_emmc.phase0_hw,
+ [RTD1625_CRT_PLL_EMMC_VP1] = &pll_emmc.phase1_hw,
+ [RTD1625_CRT_PLL_ACPU] = &__clk_pll_hw(&pll_acpu),
+ [RTD1625_CRT_CLK_NPU] = &clk_npu.hw,
+ [RTD1625_CRT_CLK_NPU_MIPI_CSI] = &clk_npu_mipi_csi.hw,
+
+ [RTD1625_CRT_CLK_MAX] = NULL,
+ },
+};
+
+static const struct rtk_clk_desc rtd1625_crt_desc = {
+ .clk_data = &rtd1625_crt_hw_data,
+ .clks = rtd1625_crt_regmap_clks,
+ .num_clks = ARRAY_SIZE(rtd1625_crt_regmap_clks),
+ .reset_descs = rtd1625_crt_reset_descs,
+ .num_reset_descs = RTD1625_CRT_RSTN_MAX,
+};
+
+static int rtd1625_crt_probe(struct platform_device *pdev)
+{
+ const struct rtk_clk_desc *desc;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ return rtk_clk_probe(pdev, desc);
+}
+
+static const struct of_device_id rtd1625_crt_match[] = {
+ {.compatible = "realtek,rtd1625-crt-clk", .data = &rtd1625_crt_desc,},
+ {/* sentinel */}
+};
+
+static struct platform_driver rtd1625_crt_driver = {
+ .probe = rtd1625_crt_probe,
+ .driver = {
+ .name = "rtk-rtd1625-crt-clk",
+ .of_match_table = rtd1625_crt_match,
+ },
+};
+
+static int __init rtd1625_crt_init(void)
+{
+ return platform_driver_register(&rtd1625_crt_driver);
+}
+subsys_initcall(rtd1625_crt_init);
+
+MODULE_DESCRIPTION("Reatek RTD1625 CRT Controller Driver");
+MODULE_AUTHOR("Cheng-Yu Lee <cylee12@realtek.com>");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH v5 06/10] clk: realtek: Add support for gate clock
From: Yu-Chun Lin @ 2026-03-24 2:53 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260324025332.3416977-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Introduce clk_regmap_gate_ops supporting enable, disable, is_enabled, and
disable_unused for standard regmap gate clocks.
Add clk_regmap_gate_ro_ops as a read-only variant exposing only is_enabled.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-regmap-gate.c | 66 +++++++++++++++++++++++++++
drivers/clk/realtek/clk-regmap-gate.h | 65 ++++++++++++++++++++++++++
3 files changed, 133 insertions(+)
create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index a89ad77993e9..74375f8127ac 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -2,5 +2,7 @@
obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
clk-rtk-y += common.o
+
clk-rtk-y += clk-pll.o
+clk-rtk-y += clk-regmap-gate.o
clk-rtk-y += freq_table.o
diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
new file mode 100644
index 000000000000..5174283c2c21
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include "clk-regmap-gate.h"
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(clkg->bit_idx);
+ val = BIT(clkg->bit_idx);
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ return regmap_update_bits(clkg->clkr.regmap, clkg->gate_ofs, mask, val);
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(clkg->bit_idx);
+ val = 0;
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ regmap_update_bits(clkg->clkr.regmap, clkg->gate_ofs, mask, val);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(clkg->clkr.regmap, clkg->gate_ofs, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & BIT(clkg->bit_idx));
+}
+
+const struct clk_ops rtk_clk_regmap_gate_ops = {
+ .enable = clk_regmap_gate_enable,
+ .disable = clk_regmap_gate_disable,
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_regmap_gate_ops);
+
+const struct clk_ops rtk_clk_regmap_gate_ro_ops = {
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(rtk_clk_regmap_gate_ro_ops);
diff --git a/drivers/clk/realtek/clk-regmap-gate.h b/drivers/clk/realtek/clk-regmap-gate.h
new file mode 100644
index 000000000000..b93357bd5a0d
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_REGMAP_GATE_H
+#define __CLK_REALTEK_CLK_REGMAP_GATE_H
+
+#include "common.h"
+
+struct clk_regmap_gate {
+ struct clk_regmap clkr;
+ int gate_ofs;
+ u8 bit_idx;
+ u32 write_en : 1;
+};
+
+#define __clk_regmap_gate_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+#define __CLK_REGMAP_GATE(_name, _parent, _ops, _flags, _ofs, _bit_idx, \
+ _write_en) \
+ struct clk_regmap_gate _name = { \
+ .clkr.hw.init = CLK_HW_INIT(#_name, _parent, _ops, _flags), \
+ .gate_ofs = _ofs, \
+ .bit_idx = _bit_idx, \
+ .write_en = _write_en, \
+ }
+
+#define CLK_REGMAP_GATE(_name, _parent, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE(_name, _parent, &rtk_clk_regmap_gate_ops, _flags, _ofs, \
+ _bit_idx, _write_en)
+
+#define CLK_REGMAP_GATE_RO(_name, _parent, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE(_name, _parent, &rtk_clk_regmap_gate_ro_ops, _flags, \
+ _ofs, _bit_idx, _write_en)
+
+#define __CLK_REGMAP_GATE_NO_PARENT(_name, _ops, _flags, _ofs, _bit_idx, \
+ _write_en) \
+ struct clk_regmap_gate _name = { \
+ .clkr.hw.init = CLK_HW_INIT_NO_PARENT(#_name, _ops, _flags), \
+ .gate_ofs = _ofs, \
+ .bit_idx = _bit_idx, \
+ .write_en = _write_en, \
+ }
+
+#define CLK_REGMAP_GATE_NO_PARENT(_name, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE_NO_PARENT(_name, &rtk_clk_regmap_gate_ops, _flags, _ofs, \
+ _bit_idx, _write_en)
+
+#define CLK_REGMAP_GATE_NO_PARENT_RO(_name, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE_NO_PARENT(_name, &rtk_clk_regmap_gate_ro_ops, _flags, \
+ _ofs, _bit_idx, _write_en)
+
+static inline struct clk_regmap_gate *to_clk_regmap_gate(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_regmap_gate, clkr);
+}
+
+extern const struct clk_ops rtk_clk_regmap_gate_ops;
+extern const struct clk_ops rtk_clk_regmap_gate_ro_ops;
+
+#endif /* __CLK_REALTEK_CLK_REGMAP_GATE_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v27 0/4] Add ASPEED AST2600 I2C controller driver
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
This series adds support for the AST2600 I2C controller “new register
set” implementation.
The AST2600 I2C controller introduces a revised register layout which
separates controller and target functionality into distinct register
blocks, and extends clock divider configuration, packet-based transfer
support, and DMA capabilities compared to the legacy mixed register
layout used on earlier ASPEED SoCs.
The current driver implementation for the AST2600 I2C peripheral is
through the hardware's "compatibility mode", which exposes a register
set that matches the previous generation hardware (AST2500 and earlier).
Instead, add a driver that works in new-register-set mode, to allow the
new features, and will provide support for future hardware that will
not implement compatibility mode.
In order to support the new mode, we need a couple of DT binding changes
to reflect the expanded hardware interfaces: references to a global
register set, and buffer mode selection. Since the binding still
represents the same (AST2600 SoC) physical hardware, we continue to use
the existing compatible string of "aspeed,ast2600-i2c-bus".
However: since we're changing semantics for an existing binding, we
allow backwards compatibility by selecting on presence/absence of the
newly-added properties, and fall back to the old driver (ie., in
compatibility mode) when we detect a DT using the old binding spec.
Specifically:
- ast2600-i2c-bus nodes that provide the `aspeed,global-regs` property
(which is mandatory in the new binding and absent in the legacy
binding) will be successfully probed by the new driver
- ast2600-i2c-bus nodes without `aspeed,global-regs` continue to use the
existing driver (in legacy register mode), ensuring that platforms
with the current DTBs remain functional
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v27:
- 1/4 use aspeed,enable-dma instead aspeed,transfer-mode.
- 2/4 remove aspeed,transfer-mode selection instad aspeed,transfer-mode
- 2/4 add sysfs for xfer mode.
- Link to v26: https://lore.kernel.org/r/20260309-upstream_i2c-v26-0-5fedcff8ffe8@aspeedtech.com
Changes in v26:
- 1/4: binding reworks based on review feedback
- Link to v25: https://lore.kernel.org/r/20260225-upstream_i2c-v25-0-9f4bdd954f3f@aspeedtech.com
Changes in v25:
- Use b4 to send series.
- Rebase on v7.0-rc1.
- Clarify cover letter and commit logs based on review feedback.
- Remove the i2c-aspeed-core multiplexer infrastructure and
implement driver selection via conditional -ENODEV handling
in individual probe() functions.
- 3/4: incorporate review feedback and refactor new driver
- Link to v24: https://lore.kernel.org/r/20251118014034.820988-1-ryan_chen@aspeedtech.com
Changes in v24:
- aspeed,ast2600-i2c.yaml
- fix make dt_binding_check blank warning.
- Link to v23: https://lore.kernel.org/all/20251117025040.3622984-1-ryan_chen@aspeedtech.com/
Changes in v23:
- update typo patch (1/4) commit message.
- aspeed,ast2600-i2c.yaml
- update reg and description.
- i2c-ast2600.c controller
- replace ast2600_select_i2c_clock to ast2600_i2c_ac_timing_config.
- i2c-ast2600.c target
- I2C_TARGET_MSG_BUF_SIZE 256 to 4096
- remove blank line.
- refine Master comment description to controller
- Link to v22: https://lore.kernel.org/all/20251112085649.1903631-1-ryan_chen@aspeedtech.com/
Changes in v22:
- update patch (1/4) commit message add dts example reason.
- aspeed,ast2600-i2c.yaml @patch (1/4)
- rename ast2600-i2c.yaml to aspeed,ast2600-i2c.yaml.
- update reg, clock-frequency description.
- aspeed,ast2600-i2c.yaml @patch (2/4)
- aspeed,transfer-mode, aspeed,transfer-mode add for ast2600.
- i2c-aspeed-core.c,h @patch (3/4)
- add i2c-aspeed-core allow both old and new device trees using the
same compatible string "aspeed,ast2600-i2c-bus".
- Link to v21: https://lore.kernel.org/all/20251027061240.3427875-1-ryan_chen@aspeedtech.com/
Changes in v21:
- update patch (1/4) commit message
- i2c-ast2600.c
- move rst to local variable in ast2600_i2c_probe().
- Link to v20: https://lore.kernel.org/all/20251021013548.2375190-1-ryan_chen@aspeedtech.com/
Changes in v20:
- ast2600-i2c.yaml
- fix warning at make dt_binding_check.
- Link to v19: https://lore.kernel.org/all/20251020013200.1858325-1-ryan_chen@aspeedtech.com/
Changes in v19:
- Split AST2600 binding into its own YAML file
- Removed `aspeed,ast2600-i2c-bus` from `aspeed,i2c.yaml`
- Added `aspeed,global-regs` and `aspeed,transfer-mode` to AST2600 binding
- Link to v18: https://lore.kernel.org/all/20250820051832.3605405-1-ryan_chen@aspeedtech.com/
Changes in v18:
- refine patch (1/3) commit message (reason for commit not list.)
- i2c-ast2600.c
- remove redundant reset_control_deassert in driver probe.
- remove reset_control_assert(i2c_bus->rst) in driver remove.
- Link to v17: https://lore.kernel.org/all/20250814084156.1650432-1-ryan_chen@aspeedtech.com/
Changes in v17:
- move i2c new mode register and feature into driver commit message.
- aspeed,i2c.yaml
- remove multi-master properties.
- use aspeed,transfer-mode properties for aspeed,enable-byte/enable-dma.
-i2c-ast2600.c
- rename dma_safe_buf to controller_dma_safe_buf.
- fix ast2600_i2c_recover_bus return overflow warnings.
- add ast2600_i2c_target_packet_buff_irq unhandle case.
- add parameter "cmd" in ast2600_i2c_setup_dma_rx,
ast2600_i2c_setup_buff_rx, ast2600_i2c_setup_byte_rx
- use reset_control_deassert replace
devm_reset_control_get_shared_deasserted.
- useaspeed,transfer-mode properties for transfer mode setting.
- change compatible = "aspeed,ast2600-i2cv2" to "aspeed,ast2600-i2c-bus".
- Link to v16: https://lore.kernel.org/all/20250224055936.1804279-1-ryan_chen@aspeedtech.com/
Changes in v16:
- aspeed,i2c.yaml: add aspeed,enable-byte properties for force byte mode.
- i2c-ast2600.c
- change include asm/unaligned.h to linux/unaligned.h.
- add reset timeout councter when slave active timeout.
- modify issue i2c_recovery_bus before slave re-enable.
- add aspeed,enable-byte properties.
- Link to v15: https://lore.kernel.org/all/20241007035235.2254138-1-ryan_chen@aspeedtech.com/
Changes in v15:
- i2c-ast2600.c
- add include unaligned.h
- rename all master -> controller, slave -> target.
- keep multi-master to align property.
- remove no used element in ast2600_i2c_bus.
- Link to v14: https://lore.kernel.org/all/20241002070213.1165263-1-ryan_chen@aspeedtech.com/
Changes in v14:
- aspeed,i2c.yaml
- v13 change people reviewed-by tag, v14 fixed to original people tag,
modify to Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
- struct ast2600_i2c_bus layout optimal.
- ast2600_select_i2c_clock refine.
- ast2600_i2c_recover_bus overridden fix.
- dma_mapping_error() returned error code shadowed modify.
- buffer register in a 4-byte aligned simplified
- remove smbus alert
- Link to v13: https://lore.kernel.org/all/20240819092850.1590758-1-ryan_chen@aspeedtech.com/
Changes in v13:
- separate i2c master and slave driver to be two patchs.
- modify include header list, add bits.h include. remove of*.h
- modify (((x) >> 24) & GENMASK(5, 0)) to (((x) & GENMASK(29, 24)) >> 24)
- modify ast2600_select_i2c_clock function implement.
- modify ast2600_i2c_recover_bus function u32 claim to
u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
- Link to v12: https://lore.kernel.org/all/20230714074522.23827-1-ryan_chen@aspeedtech.com/
Changes in v12:
- aspeed,i2c.yaml
- add Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
- i2c-ast2600.c
- update include by alphabetical order
- make just a one TAB and put the last two lines on the single one
- remove no used timing_table structre
- remove enum explicit assinment
- rewritten to avoid this and using loop in ast2600_select_i2c_clock
- use GENMASK for most 0xffff
- remove too many parentheses
- use str_read_write replace read write string
- remove redundant blank line after ast2600_i2c_bus_of_table
- fix wrong multi-line style of the comment
- use macro for i2c standard speeds
- remove useless noise dev_info
- Link to v11: https://lore.kernel.org/all/20230430041712.3247998-1-ryan_chen@aspeedtech.com/
Changes in v11:
- aspeed,i2c.yaml
- no change, the same with v10.
- i2c-ast2600.c
- modify alert_enable from int -> boolean.
- modify dbg string recovery -> recover.
- remove no need to init 0.
- remove new line after break.
- remove unneeded empty line.
- modify dma_alloc_coherent to dmam_alloc_coherent
- modify probe nomem return dev_err_probe
- modify i2c_add_adapter to devm_i2c_adapter
- modify checkpatch: Alignment should match open parenthesis
- modify checkpatch: braces {} should be used on all arms of this statement
- modify checkpatch: Unbalanced braces around else statement
- Link to v10: https://lore.kernel.org/all/20230415012848.1777768-1-ryan_chen@aspeedtech.com/
Changes in v10:
- aspeed,i2c.yaml
- move unevaluatedProperties after allOf.
- remove extra one blank line.
- i2c-ast2600.c
- no change, the same with v8.
- Link to v9: https://lore.kernel.org/all/20230405022825.333246-1-ryan_chen@aspeedtech.com/
Changes in v9:
- aspeed,i2c.yaml
- backoff to v7.
- no fix typo in maintainer's name and email. this would be another patch.
- no remove address-cells, size-cells, this would be another patch.
- use aspeed,enable-dma property instead of aspeed,xfer-mode selection.
- fix allOf and else false properties for aspeed,ast2600-i2cv2.
- i2c-ast2600.c
- no change, the same with v8
- Link to v8: https://lore.kernel.org/all/20230330073259.485606-1-ryan_chen@aspeedtech.com/
Changes in v8:
- aspeed,i2c.yaml
- modify commit message.
- Fix typo in maintainer's name and email.
- remove address-cells, size-cells.
- i2c-ast2600.c
- move "i2c timeout counter" comment description before property_read.
- remove redundant code "return ret" in probe end.
- Link to v7: https://lore.kernel.org/all/20230327092524.3916389-1-ryan_chen@aspeedtech.com/
Changes in v7:
- aspeed,i2c.yaml
- Update ASPEED I2C maintainers email.
- use aspeed,enable-dma property instead of aspeed,xfer-mode selection.
- fix allOf and else false properties for aspeed,ast2600-i2cv2.
- i2c-ast2600.c
- remove aspeed,xfer-mode instead of aspeed,enable-dma mode. buffer mode
is default.
- remove aspeed,timeout instead of i2c-scl-clk-low-timeout-us for
timeout setting.
- Link to v6: https://lore.kernel.org/all/20230226031321.3126756-1-ryan_chen@aspeedtech.com/
Changes in v6:
- remove aspeed,i2cv2.yaml, merge to aspeed,i2c.yaml -add support for
i2cv2 properites.
- i2c-ast2600.c
- fix ast2600_i2c_remove ordering.
- remove ast2600_i2c_probe goto labels, and add dev_err_probe -remove
redundant deb_dbg debug message.
- rename gr_regmap -> global_regs
- Link to v5: https://lore.kernel.org/all/20230220061745.1973981-1-ryan_chen@aspeedtech.com/
Changes in v5:
- remove ast2600-i2c-global.yaml, i2c-ast2600-global.c.
- i2c-ast2600.c
- remove legacy clock divide, all go for new clock divide.
- remove duplicated read isr.
- remove no used driver match
- fix probe return for each labels return.
- global use mfd driver, driver use phandle to regmap read/write.
- rename aspeed,i2c-ast2600.yaml to aspeed,i2cv2.yaml -remove bus-frequency.
- add required aspeed,gr
- add timeout, byte-mode, buff-mode properites.
- Link to v4: https://lore.kernel.org/all/20230201103359.1742140-1-ryan_chen@aspeedtech.com/
Changes in v4:
- fix i2c-ast2600.c driver buffer mode use single buffer conflit in
master slave mode both enable.
- fix kmemleak issue when use dma mode.
- fix typo aspeed,i2c-ast2600.yaml compatible is "aspeed,ast2600-i2c"
- fix typo aspeed,i2c-ast2600.ymal to aspeed,i2c-ast2600.yaml
- Link to v3: https://lore.kernel.org/all/20220516064900.30517-1-ryan_chen@aspeedtech.com/
Changes in v3:
- fix i2c global clock divide default value.
- remove i2c slave no used dev_dbg info.
- Link to v2: https://lore.kernel.org/all/20220413101735.27678-1-ryan_chen@aspeedtech.com/
Changes in v2:
- add i2c global ymal file commit.
- rename file name from new to ast2600.
aspeed-i2c-new-global.c -> i2c-ast2600-global.c
aspeed-i2c-new-global.h -> i2c-ast2600-global.h
i2c-new-aspeed.c -> i2c-ast2600.c
- rename all driver function name to ast2600.
- Link to v1: https://lore.kernel.org/all/20220323004009.943298-1-ryan_chen@aspeedtech.com/
---
Ryan Chen (4):
dt-bindings: i2c: Split AST2600 binding into a new YAML
dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and transfer-mode properties
i2c: ast2600: Add controller driver for AST2600 new register set
i2c: ast2600: Add target mode support
.../bindings/i2c/aspeed,ast2600-i2c.yaml | 79 +
.../devicetree/bindings/i2c/aspeed,i2c.yaml | 3 +-
drivers/i2c/busses/Makefile | 2 +-
drivers/i2c/busses/i2c-aspeed.c | 5 +
drivers/i2c/busses/i2c-ast2600.c | 1627 ++++++++++++++++++++
5 files changed, 1713 insertions(+), 3 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20260223-upstream_i2c-ebd07f89739c
Best regards,
--
Ryan Chen <ryan_chen@aspeedtech.com>
^ permalink raw reply
* [PATCH v27 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and transfer-mode properties
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com>
The AST2600 I2C controller supports three transfer modes (byte, buffer,
DMA). Add "aspeed,transfer-mode" so DT can select the preferred transfer
method per controller instance. Also add the "aspeed,global-regs"
phandle to reference the AST2600 global registers syscon/regmap used by
the controller.
These properties apply only to the AST2600 binding and are not part of
the legacy binding, which uses a mixed controller/target register layout
and does not have the split register blocks or these new configuration
registers. Legacy DTs remain unchanged.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v27:
- change aspeed,transfer-mode to aspeed,enable-dma.
---
.../devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
index de2c359037da..38da6fc6424f 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -37,6 +37,21 @@ properties:
resets:
maxItems: 1
+ aspeed,enable-dma:
+ type: boolean
+ description: |
+ I2C bus enable dma mode transfer.
+
+ ASPEED ast2600 platform equipped with 16 I2C controllers that share a
+ single DMA engine. DTS files can specify the data transfer mode to/from
+ the device, either DMA or programmed I/O.
+
+ aspeed,global-regs:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle reference to the i2c global syscon node, containing the
+ SoC-common i2c register set.
+
required:
- reg
- compatible
@@ -59,4 +74,6 @@ examples:
resets = <&syscon ASPEED_RESET_I2C>;
clock-frequency = <100000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+ aspeed,global-regs = <&i2c_global>;
+ aspeed,transfer-mode = "buffer";
};
--
2.34.1
^ permalink raw reply related
* [PATCH v27 1/4] dt-bindings: i2c: Split AST2600 binding into a new YAML
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com>
The AST2600 I2C controller introduces a completely new register layout
with separate controller and target register blocks, unlike the mixed
register layout used by AST2400/AST2500.
Move AST2600 I2C binding from aspeed,i2c.yaml to a dedicated
aspeed,ast2600-i2c.yaml schema.
Besides the split, this also adjusts for AST2600-specific requirements.
- require two reg regions (controller register block + buffer block)
- use clock-frequency for bus speed description
- interrupts are required on AST2600
- use correct DTS coding style in example
No compatible strings are changed.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v26:
- commit message: include details of changes from original binding
- fix example property ordering to follow DTS coding style
- use consistent "AST2600" naming
---
.../bindings/i2c/aspeed,ast2600-i2c.yaml | 62 ++++++++++++++++++++++
.../devicetree/bindings/i2c/aspeed,i2c.yaml | 3 +-
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
new file mode 100644
index 000000000000..de2c359037da
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/aspeed,ast2600-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED I2C on the AST2600 SoCs
+
+maintainers:
+ - Ryan Chen <ryan_chen@aspeedtech.com>
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2600-i2c-bus
+
+ reg:
+ items:
+ - description: controller registers
+ - description: controller buffer space
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency:
+ description: Desired operating frequency of the I2C bus in Hz.
+ minimum: 500
+ maximum: 4000000
+ default: 100000
+
+ resets:
+ maxItems: 1
+
+required:
+ - reg
+ - compatible
+ - clocks
+ - resets
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/aspeed-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ i2c@80 {
+ compatible = "aspeed,ast2600-i2c-bus";
+ reg = <0x80 0x80>, <0xc00 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+ clock-frequency = <100000>;
+ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
index 5b9bd2feda3b..d4e4f412feba 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/i2c/aspeed,i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: ASPEED I2C on the AST24XX, AST25XX, and AST26XX SoCs
+title: ASPEED I2C on the AST24XX, AST25XX SoCs
maintainers:
- Rayn Chen <rayn_chen@aspeedtech.com>
@@ -17,7 +17,6 @@ properties:
enum:
- aspeed,ast2400-i2c-bus
- aspeed,ast2500-i2c-bus
- - aspeed,ast2600-i2c-bus
reg:
minItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH v27 4/4] i2c: ast2600: Add target mode support
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com>
Add target mode support to the AST2600 I2C driver.
Target mode features implemented include:
- Add target interrupt handling
- Address match and response logic
- Separate Tx/Rx DMA address and length configuration
This complements the existing controller-mode support, enabling
dual-role capability.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v26:
- change int to bool target_operate
- rename target_operate to target_active
- use i2c_bus->target replace require IO
- use WRITE_ONCE replace target_operate write.
---
drivers/i2c/busses/i2c-ast2600.c | 565 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 564 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
index cf3006eaded9..0ae0cd71a78d 100644
--- a/drivers/i2c/busses/i2c-ast2600.c
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -274,6 +274,13 @@ struct ast2600_i2c_bus {
void __iomem *buf_base;
int (*setup_tx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
int (*setup_rx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* target structure */
+ bool target_active;
+ unsigned char *target_dma_buf;
+ dma_addr_t target_dma_addr;
+ struct i2c_client *target;
+#endif
};
static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
@@ -357,6 +364,440 @@ static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
return ret;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void ast2600_i2c_target_packet_dma_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ int target_rx_len = 0;
+ u32 cmd = 0;
+ u8 value;
+ int i;
+
+ sts &= ~(AST2600_I2CS_SLAVE_PENDING);
+ /* Handle i2c target timeout condition */
+ if (sts & AST2600_I2CS_INACTIVE_TO) {
+ /* Reset timeout counter */
+ u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &
+ AST2600_I2CC_AC_TIMING_MASK;
+
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ return;
+ }
+
+ sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
+
+ switch (sts) {
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_RX_DMA:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CS_DMA_LEN_STS));
+ for (i = 0; i < target_rx_len; i++) {
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
+ &i2c_bus->target_dma_buf[i]);
+ }
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE_NAK |
+ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_RX_DMA |
+ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_RX_DONE_NAK | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_STOP:
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CS_DMA_LEN_STS));
+ for (i = 0; i < target_rx_len; i++) {
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
+ &i2c_bus->target_dma_buf[i]);
+ }
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ if (sts & AST2600_I2CS_STOP)
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
+ break;
+
+ /* it is Mw data Mr coming -> it need send tx */
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_TX_DMA:
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_TX_DMA:
+ /* it should be repeat start read */
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CS_DMA_LEN_STS));
+ for (i = 0; i < target_rx_len; i++) {
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
+ &i2c_bus->target_dma_buf[i]);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
+ &i2c_bus->target_dma_buf[0]);
+ writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA:
+ /* First Start read */
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
+ &i2c_bus->target_dma_buf[0]);
+ writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
+ break;
+ case AST2600_I2CS_WAIT_TX_DMA:
+ /* it should be next start read */
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED,
+ &i2c_bus->target_dma_buf[0]);
+ writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
+ break;
+ case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
+ /* it just tx complete */
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ break;
+ case AST2600_I2CS_STOP:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
+ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+ break;
+ }
+
+ if (cmd)
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+}
+
+static void ast2600_i2c_target_packet_buff_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ int target_rx_len = 0;
+ u32 cmd = 0;
+ u8 value;
+ int i;
+
+ /* due to controller target is common buffer, need force the master stop not issue */
+ if (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15, 0)) {
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+ i2c_bus->cmd_err = -EBUSY;
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ complete(&i2c_bus->cmd_complete);
+ }
+
+ /* Handle i2c target timeout condition */
+ if (AST2600_I2CS_INACTIVE_TO & sts) {
+ /* Reset timeout counter */
+ u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &
+ AST2600_I2CC_AC_TIMING_MASK;
+
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ WRITE_ONCE(i2c_bus->target_active, false);
+ return;
+ }
+
+ sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
+
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ WRITE_ONCE(i2c_bus->target_active, true);
+
+ switch (sts) {
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ fallthrough;
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ cmd = TARGET_TRIGGER_CMD;
+ if (sts & AST2600_I2CS_RX_DONE) {
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ }
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_RX_BUFF_EN)
+ cmd = 0;
+ else
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ cmd |= AST2600_I2CS_RX_BUFF_EN;
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ cmd |= AST2600_I2CS_RX_BUFF_EN;
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ /* workaround for avoid next start with len != 0 */
+ writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ /* workaround for avoid next start with len != 0 */
+ writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |
+ AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_STOP:
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ writeb(value, i2c_bus->buf_base);
+ break;
+ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ writeb(value, i2c_bus->buf_base);
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_STOP |
+ AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_STOP |
+ AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_TX_DMA:
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ if (sts & AST2600_I2CS_RX_DONE) {
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ } else {
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+ writeb(value, i2c_bus->buf_base);
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+ break;
+ /* workaround : trigger the cmd twice to fix next state keep 1000000 */
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ break;
+ case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
+ case AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
+ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+ break;
+ }
+
+ if (cmd)
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+ if ((sts & AST2600_I2CS_STOP) && !(sts & AST2600_I2CS_SLAVE_PENDING))
+ WRITE_ONCE(i2c_bus->target_active, false);
+}
+
+static void ast2600_i2c_target_byte_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ u32 i2c_buff = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ u32 cmd = AST2600_I2CS_ACTIVE_ALL;
+ u8 byte_data;
+ u8 value;
+
+ /* Handle i2c target timeout condition */
+ if (sts & AST2600_I2CS_INACTIVE_TO) {
+ /* Reset timeout counter */
+ u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &
+ AST2600_I2CC_AC_TIMING_MASK;
+
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ writel(AST2600_I2CS_ACTIVE_ALL, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(sts, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ WRITE_ONCE(i2c_bus->target_active, false);
+ return;
+ }
+
+ switch (sts) {
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ /* first address match is address */
+ byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
+ break;
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
+ byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &byte_data);
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_TX_DMA:
+ cmd |= AST2600_I2CS_TX_CMD;
+ byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &byte_data);
+ writel(byte_data, i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ break;
+ case AST2600_I2CS_TX_ACK | AST2600_I2CS_WAIT_TX_DMA:
+ cmd |= AST2600_I2CS_TX_CMD;
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &byte_data);
+ writel(byte_data, i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ break;
+ case AST2600_I2CS_STOP:
+ case AST2600_I2CS_STOP | AST2600_I2CS_TX_NAK:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled pkt isr %x\n", sts);
+ break;
+ }
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(sts, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+}
+
+static int ast2600_i2c_target_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 ier = readl(i2c_bus->reg_base + AST2600_I2CS_IER);
+ u32 isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+ if (!(isr & ier))
+ return 0;
+
+ /*
+ * Target interrupt coming after controller packet done
+ * So need handle controller first.
+ */
+ if (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) & AST2600_I2CM_PKT_DONE)
+ return 0;
+
+ isr &= ~(AST2600_I2CS_ADDR_INDICATE_MASK);
+
+ if (AST2600_I2CS_ADDR1_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR1_NAK;
+
+ if (AST2600_I2CS_ADDR2_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR2_NAK;
+
+ if (AST2600_I2CS_ADDR3_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR3_NAK;
+
+ if (AST2600_I2CS_ADDR_MASK & isr)
+ isr &= ~AST2600_I2CS_ADDR_MASK;
+
+ if (AST2600_I2CS_PKT_DONE & isr) {
+ if (i2c_bus->mode == DMA_MODE)
+ ast2600_i2c_target_packet_dma_irq(i2c_bus, isr);
+ else
+ ast2600_i2c_target_packet_buff_irq(i2c_bus, isr);
+ } else {
+ ast2600_i2c_target_byte_irq(i2c_bus, isr);
+ }
+
+ return 1;
+}
+#endif
+
static int ast2600_i2c_setup_dma_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
{
struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
@@ -628,6 +1069,20 @@ static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u
}
break;
case AST2600_I2CM_RX_DONE:
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * Workaround for controller/target packet mode enable rx done stuck issue
+ * When controller go for first read (RX_DONE), target mode will also effect
+ * Then controller will send nack, not operate anymore.
+ */
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_PKT_MODE_EN) {
+ u32 target_cmd = readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(target_cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ }
+ fallthrough;
+#endif
case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
/* do next rx */
if (i2c_bus->mode == DMA_MODE) {
@@ -726,6 +1181,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
{
struct ast2600_i2c_bus *i2c_bus = dev_id;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (i2c_bus->target) {
+ if (ast2600_i2c_target_irq(i2c_bus))
+ return IRQ_HANDLED;
+ }
+#endif
return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
}
@@ -742,12 +1203,35 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
return ret;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (i2c_bus->mode == BUFF_MODE) {
+ if (i2c_bus->target_active)
+ return -EBUSY;
+ /**
+ * In BUFF_MODE, controller and target share the same buffer register,
+ * A target transaction can update buffer state asynchronously via IRQ,
+ * so block controller transfers while target is active, otherwise we can
+ * corrupt buffer.
+ */
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_IER);
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) || i2c_bus->target_active) {
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+ return -EBUSY;
+ }
+ }
+#endif
+
i2c_bus->cmd_err = 0;
i2c_bus->msgs = msgs;
i2c_bus->msgs_index = 0;
i2c_bus->msgs_count = num;
reinit_completion(&i2c_bus->cmd_complete);
ret = ast2600_i2c_do_start(i2c_bus);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* avoid race condication target is wait and controller wait 1st target operate */
+ if (i2c_bus->mode == BUFF_MODE)
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+#endif
if (ret)
goto controller_out;
timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
@@ -765,7 +1249,7 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
* a master timeout. In multi-master mode, attempt bus recovery
* if the bus is still busy.
*/
- if (i2c_bus->multi_master &&
+ if (i2c_bus->multi_master && !i2c_bus->target_active &&
(readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
AST2600_I2CC_BUS_BUSY_STS))
ast2600_i2c_recover_bus(i2c_bus);
@@ -813,8 +1297,80 @@ static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
/* Clear Interrupt */
writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* for memory buffer initial */
+ if (i2c_bus->dma_available) {
+ i2c_bus->target_dma_buf =
+ dmam_alloc_coherent(i2c_bus->dev, I2C_TARGET_MSG_BUF_SIZE,
+ &i2c_bus->target_dma_addr, GFP_KERNEL);
+ if (!i2c_bus->target_dma_buf)
+ return -ENOMEM;
+ }
+
+ writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+ if (i2c_bus->mode == BYTE_MODE)
+ writel(GENMASK(15, 0), i2c_bus->reg_base + AST2600_I2CS_IER);
+ else
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+#endif
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int ast2600_i2c_reg_target(struct i2c_client *client)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+ u32 cmd = TARGET_TRIGGER_CMD;
+
+ if (i2c_bus->target)
+ return -EINVAL;
+
+ dev_dbg(i2c_bus->dev, "target addr %x\n", client->addr);
+
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+ writel(AST2600_I2CC_SLAVE_EN | readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL),
+ i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ /* trigger rx buffer */
+ if (i2c_bus->mode == DMA_MODE) {
+ cmd |= AST2600_I2CS_RX_DMA_EN;
+ writel(i2c_bus->target_dma_addr, i2c_bus->reg_base + AST2600_I2CS_RX_DMA);
+ writel(i2c_bus->target_dma_addr, i2c_bus->reg_base + AST2600_I2CS_TX_DMA);
+ writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
+ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
+ } else if (i2c_bus->mode == BUFF_MODE) {
+ cmd = TARGET_TRIGGER_CMD;
+ } else {
+ cmd &= ~AST2600_I2CS_PKT_MODE_EN;
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ i2c_bus->target = client;
+ /* Set target addr. */
+ writel(client->addr | AST2600_I2CS_ADDR1_ENABLE,
+ i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ return 0;
+}
+
+static int ast2600_i2c_unreg_target(struct i2c_client *client)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+ u32 val;
+
+ /* Turn off target mode. */
+ val = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(val & ~AST2600_I2CC_SLAVE_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ val = readl(i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+ writel(val & ~AST2600_I2CS_ADDR1_MASK, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ i2c_bus->target = NULL;
+
return 0;
}
+#endif
static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
{
@@ -824,6 +1380,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
static const struct i2c_algorithm i2c_ast2600_algorithm = {
.xfer = ast2600_i2c_controller_xfer,
.functionality = ast2600_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_target = ast2600_i2c_reg_target,
+ .unreg_target = ast2600_i2c_unreg_target,
+#endif
};
static void ast2600_i2c_set_xfer_mode(struct ast2600_i2c_bus *i2c_bus,
@@ -949,6 +1509,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)
regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ WRITE_ONCE(i2c_bus->target_active, false);
+#endif
i2c_bus->dev = dev;
i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
i2c_bus->dma_available = device_property_read_bool(dev, "aspeed,enable-dma");
--
2.34.1
^ permalink raw reply related
* [PATCH v27 3/4] i2c: ast2600: Add controller driver for AST2600 new register set
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com>
The AST2600 introduces a new I2C controller register layout, selectable
at runtime via global control registers. Compared to the legacy layout
used on AST2400/AST2500, the new layout separates controller (master)
and target (slave) registers and adds support for packet-based transfers
The new register set extends the hardware capabilities with:
- Enhanced clock divider configuration for improved timing precision
- tCKHighMin timing control for SCL high pulse width
- Dual pool buffer mode (separate Tx/Rx buffers)
- Extended DMA support with larger buffer size and alignment handling
- Dedicated DMA buffers for controller and target directions
- Hardware-assisted bus recovery and timeout mechanisms
This patch adds an AST2600-specific I2C controller driver implementing
the new register layout, including support for packet-based transfers
and byte, buffer and DMA transfer modes.
The legacy and new register layouts represent the same AST2600 I2C
controller IP and therefore share the existing compatible string:
"aspeed,ast2600-i2c-bus"
To preserve DT ABI compatibility, driver selection is performed at probe
time based on DT contents. In particular, the new binding requires the
`aspeed,global-regs` phandle, which is absent from legacy DTBs:
- The new driver only probes successfully when `aspeed,global-regs` is
present.
- The existing i2c-aspeed driver returns -ENODEV for AST2600 nodes that
provide `aspeed,global-regs`, allowing the new driver to bind.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v27:
- remove aspeed,transfer-mode selection instead aspeed,dma-mode.
- add sysfs for xfer mode.
Changes in v25:
- Rename AST2600_I2CM_SMBUS_ALT to AST2600_I2CM_SMBUS_ALERT.
- Refactor transfer mode handling using setup_tx/setup_rx helpers.
- Rework DMA handling to use pre-allocated buffers and reduce
mapping overhead in interrupt context.
- Fix IRQ status checks to use consistent (sts & value) style.
- Move device_property_read_bool() to probe().
- Improve probe error handling.
- Handle timeout condition in target_byte_irq().
- Rename "package" to "packet".
- Remove target reset when master wait_for_completion_timeout().
---
drivers/i2c/busses/Makefile | 2 +-
drivers/i2c/busses/i2c-aspeed.c | 5 +
drivers/i2c/busses/i2c-ast2600.c | 1064 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1070 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 547123ab351f..ece201a67d41 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
obj-$(CONFIG_I2C_AMD_ASF) += i2c-amd-asf-plat.o
-obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
+obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o i2c-ast2600.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
i2c-at91-y := i2c-at91-core.o i2c-at91-master.o
i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL) += i2c-at91-slave.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index a26b74c71206..8286fd2cd130 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -22,6 +22,7 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -1002,6 +1003,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
struct clk *parent_clk;
int irq, ret;
+ if (device_is_compatible(&pdev->dev, "aspeed,ast2600-i2c-bus") &&
+ device_property_present(&pdev->dev, "aspeed,global-regs"))
+ return -ENODEV;
+
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
new file mode 100644
index 000000000000..cf3006eaded9
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASPEED AST2600 new register set I2C controller driver
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/unaligned.h>
+
+#define AST2600_I2CG_ISR 0x00
+#define AST2600_I2CG_SLAVE_ISR 0x04
+#define AST2600_I2CG_OWNER 0x08
+#define AST2600_I2CG_CTRL 0x0C
+#define AST2600_I2CG_CLK_DIV_CTRL 0x10
+
+#define AST2600_I2CG_SLAVE_PKT_NAK BIT(4)
+#define AST2600_I2CG_M_S_SEPARATE_INTR BIT(3)
+#define AST2600_I2CG_CTRL_NEW_REG BIT(2)
+#define AST2600_I2CG_CTRL_NEW_CLK_DIV BIT(1)
+#define AST2600_GLOBAL_INIT \
+ (AST2600_I2CG_CTRL_NEW_REG | AST2600_I2CG_CTRL_NEW_CLK_DIV)
+/*
+ * APB clk : 100Mhz
+ * div : scl : baseclk [APB/((div/2) + 1)] : tBuf [1/bclk * 16]
+ * I2CG10[31:24] base clk4 for i2c auto recovery timeout counter (0xC6)
+ * I2CG10[23:16] base clk3 for Standard-mode (100Khz) min tBuf 4.7us
+ * 0x3c : 100.8Khz : 3.225Mhz : 4.96us
+ * 0x3d : 99.2Khz : 3.174Mhz : 5.04us
+ * 0x3e : 97.65Khz : 3.125Mhz : 5.12us
+ * 0x40 : 97.75Khz : 3.03Mhz : 5.28us
+ * 0x41 : 99.5Khz : 2.98Mhz : 5.36us (default)
+ * I2CG10[15:8] base clk2 for Fast-mode (400Khz) min tBuf 1.3us
+ * 0x12 : 400Khz : 10Mhz : 1.6us
+ * I2CG10[7:0] base clk1 for Fast-mode Plus (1Mhz) min tBuf 0.5us
+ * 0x08 : 1Mhz : 20Mhz : 0.8us
+ */
+#define I2CCG_DIV_CTRL 0xC6411208
+
+/* 0x00 : I2CC Controller/Target Function Control Register */
+#define AST2600_I2CC_FUN_CTRL 0x00
+#define AST2600_I2CC_SLAVE_ADDR_RX_EN BIT(20)
+#define AST2600_I2CC_MASTER_RETRY_MASK GENMASK(19, 18)
+#define AST2600_I2CC_MASTER_RETRY(x) (((x) & GENMASK(1, 0)) << 18)
+#define AST2600_I2CC_BUS_AUTO_RELEASE BIT(17)
+#define AST2600_I2CC_M_SDA_LOCK_EN BIT(16)
+#define AST2600_I2CC_MULTI_MASTER_DIS BIT(15)
+#define AST2600_I2CC_M_SCL_DRIVE_EN BIT(14)
+#define AST2600_I2CC_MSB_STS BIT(9)
+#define AST2600_I2CC_SDA_DRIVE_1T_EN BIT(8)
+#define AST2600_I2CC_M_SDA_DRIVE_1T_EN BIT(7)
+#define AST2600_I2CC_M_HIGH_SPEED_EN BIT(6)
+/* reserver 5 : 2 */
+#define AST2600_I2CC_SLAVE_EN BIT(1)
+#define AST2600_I2CC_MASTER_EN BIT(0)
+
+/* 0x04 : I2CC Controller/Target Clock and AC Timing Control Register #1 */
+#define AST2600_I2CC_AC_TIMING 0x04
+#define AST2600_I2CC_TTIMEOUT(x) (((x) & GENMASK(4, 0)) << 24)
+#define AST2600_I2CC_TCKHIGHMIN(x) (((x) & GENMASK(3, 0)) << 20)
+#define AST2600_I2CC_TCKHIGH(x) (((x) & GENMASK(3, 0)) << 16)
+#define AST2600_I2CC_TCKLOW(x) (((x) & GENMASK(3, 0)) << 12)
+#define AST2600_I2CC_THDDAT(x) (((x) & GENMASK(1, 0)) << 10)
+#define AST2600_I2CC_TOUTBASECLK(x) (((x) & GENMASK(1, 0)) << 8)
+#define AST2600_I2CC_TBASECLK(x) ((x) & GENMASK(3, 0))
+#define AST2600_I2CC_AC_TIMING_MASK GENMASK(23, 0)
+
+/* 0x08 : I2CC Controller/Target Transmit/Receive Byte Buffer Register */
+#define AST2600_I2CC_STS_AND_BUFF 0x08
+#define AST2600_I2CC_TX_DIR_MASK GENMASK(31, 29)
+#define AST2600_I2CC_SDA_OE BIT(28)
+#define AST2600_I2CC_SDA_O BIT(27)
+#define AST2600_I2CC_SCL_OE BIT(26)
+#define AST2600_I2CC_SCL_O BIT(25)
+
+#define AST2600_I2CC_SCL_LINE_STS BIT(18)
+#define AST2600_I2CC_SDA_LINE_STS BIT(17)
+#define AST2600_I2CC_BUS_BUSY_STS BIT(16)
+
+#define AST2600_I2CC_GET_RX_BUFF(x) (((x) >> 8) & GENMASK(7, 0))
+
+/* 0x0C : I2CC Controller/Target Pool Buffer Control Register */
+#define AST2600_I2CC_BUFF_CTRL 0x0C
+#define AST2600_I2CC_GET_RX_BUF_LEN(x) (((x) & GENMASK(29, 24)) >> 24)
+#define AST2600_I2CC_SET_RX_BUF_LEN(x) (((((x) - 1) & GENMASK(4, 0)) << 16) | BIT(0))
+#define AST2600_I2CC_SET_TX_BUF_LEN(x) (((((x) - 1) & GENMASK(4, 0)) << 8) | BIT(0))
+#define AST2600_I2CC_GET_TX_BUF_LEN(x) ((((x) & GENMASK(12, 8)) >> 8) + 1)
+
+/* 0x10 : I2CM Controller Interrupt Control Register */
+#define AST2600_I2CM_IER 0x10
+/* 0x14 : I2CM Controller Interrupt Status Register : WC */
+#define AST2600_I2CM_ISR 0x14
+
+#define AST2600_I2CM_PKT_TIMEOUT BIT(18)
+#define AST2600_I2CM_PKT_ERROR BIT(17)
+#define AST2600_I2CM_PKT_DONE BIT(16)
+
+#define AST2600_I2CM_BUS_RECOVER_FAIL BIT(15)
+#define AST2600_I2CM_SDA_DL_TO BIT(14)
+#define AST2600_I2CM_BUS_RECOVER BIT(13)
+#define AST2600_I2CM_SMBUS_ALERT BIT(12)
+
+#define AST2600_I2CM_SCL_LOW_TO BIT(6)
+#define AST2600_I2CM_ABNORMAL BIT(5)
+#define AST2600_I2CM_NORMAL_STOP BIT(4)
+#define AST2600_I2CM_ARBIT_LOSS BIT(3)
+#define AST2600_I2CM_RX_DONE BIT(2)
+#define AST2600_I2CM_TX_NAK BIT(1)
+#define AST2600_I2CM_TX_ACK BIT(0)
+
+/* 0x18 : I2CM Controller Command/Status Register */
+#define AST2600_I2CM_CMD_STS 0x18
+#define AST2600_I2CM_PKT_ADDR(x) (((x) & GENMASK(6, 0)) << 24)
+#define AST2600_I2CM_PKT_EN BIT(16)
+#define AST2600_I2CM_SDA_OE_OUT_DIR BIT(15)
+#define AST2600_I2CM_SDA_O_OUT_DIR BIT(14)
+#define AST2600_I2CM_SCL_OE_OUT_DIR BIT(13)
+#define AST2600_I2CM_SCL_O_OUT_DIR BIT(12)
+#define AST2600_I2CM_RECOVER_CMD_EN BIT(11)
+
+#define AST2600_I2CM_RX_DMA_EN BIT(9)
+#define AST2600_I2CM_TX_DMA_EN BIT(8)
+/* Command Bit */
+#define AST2600_I2CM_RX_BUFF_EN BIT(7)
+#define AST2600_I2CM_TX_BUFF_EN BIT(6)
+#define AST2600_I2CM_STOP_CMD BIT(5)
+#define AST2600_I2CM_RX_CMD_LAST BIT(4)
+#define AST2600_I2CM_RX_CMD BIT(3)
+
+#define AST2600_I2CM_TX_CMD BIT(1)
+#define AST2600_I2CM_START_CMD BIT(0)
+
+/* 0x1C : I2CM Controller DMA Transfer Length Register */
+#define AST2600_I2CM_DMA_LEN 0x1C
+/* Tx Rx support length 1 ~ 4096 */
+#define AST2600_I2CM_SET_RX_DMA_LEN(x) ((((x) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CM_SET_TX_DMA_LEN(x) (((x) & GENMASK(11, 0)) | BIT(15))
+
+/* 0x20 : I2CS Target Interrupt Control Register */
+#define AST2600_I2CS_IER 0x20
+/* 0x24 : I2CS Target Interrupt Status Register */
+#define AST2600_I2CS_ISR 0x24
+
+#define AST2600_I2CS_ADDR_INDICATE_MASK GENMASK(31, 30)
+#define AST2600_I2CS_SLAVE_PENDING BIT(29)
+
+#define AST2600_I2CS_WAIT_TX_DMA BIT(25)
+#define AST2600_I2CS_WAIT_RX_DMA BIT(24)
+
+#define AST2600_I2CS_ADDR3_NAK BIT(22)
+#define AST2600_I2CS_ADDR2_NAK BIT(21)
+#define AST2600_I2CS_ADDR1_NAK BIT(20)
+
+#define AST2600_I2CS_ADDR_MASK GENMASK(19, 18)
+#define AST2600_I2CS_PKT_ERROR BIT(17)
+#define AST2600_I2CS_PKT_DONE BIT(16)
+#define AST2600_I2CS_INACTIVE_TO BIT(15)
+
+#define AST2600_I2CS_SLAVE_MATCH BIT(7)
+#define AST2600_I2CS_ABNOR_STOP BIT(5)
+#define AST2600_I2CS_STOP BIT(4)
+#define AST2600_I2CS_RX_DONE_NAK BIT(3)
+#define AST2600_I2CS_RX_DONE BIT(2)
+#define AST2600_I2CS_TX_NAK BIT(1)
+#define AST2600_I2CS_TX_ACK BIT(0)
+
+/* 0x28 : I2CS Target CMD/Status Register */
+#define AST2600_I2CS_CMD_STS 0x28
+#define AST2600_I2CS_ACTIVE_ALL GENMASK(18, 17)
+#define AST2600_I2CS_PKT_MODE_EN BIT(16)
+#define AST2600_I2CS_AUTO_NAK_NOADDR BIT(15)
+#define AST2600_I2CS_AUTO_NAK_EN BIT(14)
+
+#define AST2600_I2CS_ALT_EN BIT(10)
+#define AST2600_I2CS_RX_DMA_EN BIT(9)
+#define AST2600_I2CS_TX_DMA_EN BIT(8)
+#define AST2600_I2CS_RX_BUFF_EN BIT(7)
+#define AST2600_I2CS_TX_BUFF_EN BIT(6)
+#define AST2600_I2CS_RX_CMD_LAST BIT(4)
+
+#define AST2600_I2CS_TX_CMD BIT(2)
+
+#define AST2600_I2CS_DMA_LEN 0x2C
+#define AST2600_I2CS_SET_RX_DMA_LEN(x) (((((x) - 1) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CS_SET_TX_DMA_LEN(x) ((((x) - 1) & GENMASK(11, 0)) | BIT(15))
+
+/* I2CM Controller DMA Tx Buffer Register */
+#define AST2600_I2CM_TX_DMA 0x30
+/* I2CM Controller DMA Rx Buffer Register */
+#define AST2600_I2CM_RX_DMA 0x34
+/* I2CS Target DMA Tx Buffer Register */
+#define AST2600_I2CS_TX_DMA 0x38
+/* I2CS Target DMA Rx Buffer Register */
+#define AST2600_I2CS_RX_DMA 0x3C
+
+#define AST2600_I2CS_ADDR_CTRL 0x40
+
+#define AST2600_I2CS_ADDR3_MASK GENMASK(22, 16)
+#define AST2600_I2CS_ADDR2_MASK GENMASK(14, 8)
+#define AST2600_I2CS_ADDR1_MASK GENMASK(6, 0)
+
+#define AST2600_I2CM_DMA_LEN_STS 0x48
+#define AST2600_I2CS_DMA_LEN_STS 0x4C
+
+#define AST2600_I2C_GET_TX_DMA_LEN(x) ((x) & GENMASK(12, 0))
+#define AST2600_I2C_GET_RX_DMA_LEN(x) (((x) & GENMASK(28, 16)) >> 16)
+
+/* 0x40 : Target Device Address Register */
+#define AST2600_I2CS_ADDR3_ENABLE BIT(23)
+#define AST2600_I2CS_ADDR3(x) ((x) << 16)
+#define AST2600_I2CS_ADDR2_ENABLE BIT(15)
+#define AST2600_I2CS_ADDR2(x) ((x) << 8)
+#define AST2600_I2CS_ADDR1_ENABLE BIT(7)
+#define AST2600_I2CS_ADDR1(x) (x)
+
+#define I2C_TARGET_MSG_BUF_SIZE 4096
+
+#define AST2600_I2C_DMA_SIZE 4096
+
+#define CONTROLLER_TRIGGER_LAST_STOP (AST2600_I2CM_RX_CMD_LAST | AST2600_I2CM_STOP_CMD)
+#define TARGET_TRIGGER_CMD (AST2600_I2CS_ACTIVE_ALL | AST2600_I2CS_PKT_MODE_EN)
+
+#define AST_I2C_TIMEOUT_CLK 0x1
+
+enum xfer_mode {
+ BYTE_MODE,
+ BUFF_MODE,
+ DMA_MODE,
+};
+
+struct ast2600_i2c_bus {
+ struct i2c_adapter adap;
+ struct device *dev;
+ void __iomem *reg_base;
+ struct regmap *global_regs;
+ struct clk *clk;
+ struct i2c_timings timing_info;
+ struct completion cmd_complete;
+ struct i2c_msg *msgs;
+ u8 *controller_dma_buf;
+ dma_addr_t controller_dma_addr;
+ u32 apb_clk;
+ u32 timeout;
+ int irq;
+ int cmd_err;
+ int msgs_index;
+ int msgs_count;
+ int controller_xfer_cnt;
+ size_t buf_index;
+ size_t buf_size;
+ enum xfer_mode mode;
+ bool dma_available;
+ bool multi_master;
+ /* Buffer mode */
+ void __iomem *buf_base;
+ int (*setup_tx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
+ int (*setup_rx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
+};
+
+static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
+{
+ unsigned long base_clk[16];
+ int baseclk_idx = 0;
+ int divisor = 0;
+ u32 clk_div_reg;
+ u32 scl_low;
+ u32 scl_high;
+ u32 data;
+
+ regmap_read(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, &clk_div_reg);
+
+ for (int i = 0; i < ARRAY_SIZE(base_clk); i++) {
+ if (i == 0)
+ base_clk[i] = i2c_bus->apb_clk;
+ else if (i < 5)
+ base_clk[i] = (i2c_bus->apb_clk * 2) /
+ (((clk_div_reg >> ((i - 1) * 8)) & GENMASK(7, 0)) + 2);
+ else
+ base_clk[i] = base_clk[4] >> (i - 4);
+
+ if ((base_clk[i] / i2c_bus->timing_info.bus_freq_hz) <= 32) {
+ baseclk_idx = i;
+ divisor = DIV_ROUND_UP(base_clk[i], i2c_bus->timing_info.bus_freq_hz);
+ break;
+ }
+ }
+ baseclk_idx = min(baseclk_idx, 15);
+ divisor = min(divisor, 32);
+ scl_low = min(divisor * 9 / 16 - 1, 15);
+ scl_high = (divisor - scl_low - 2) & GENMASK(3, 0);
+ data = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx;
+ if (i2c_bus->timeout) {
+ data |= AST2600_I2CC_TOUTBASECLK(AST_I2C_TIMEOUT_CLK);
+ data |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ }
+
+ writel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+}
+
+static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ int ret = 0;
+ u32 ctrl;
+ int r;
+
+ dev_dbg(i2c_bus->dev, "%d-bus recovery bus [%x]\n", i2c_bus->adap.nr, state);
+
+ /* reset controller */
+ ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ reinit_completion(&i2c_bus->cmd_complete);
+ i2c_bus->cmd_err = 0;
+
+ /* Check SDA/SCL status in the status register. */
+ state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ if (!(state & AST2600_I2CC_SDA_LINE_STS) && (state & AST2600_I2CC_SCL_LINE_STS)) {
+ writel(AST2600_I2CM_RECOVER_CMD_EN, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+ r = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+ if (r == 0) {
+ dev_dbg(i2c_bus->dev, "recovery timed out\n");
+ return -ETIMEDOUT;
+ } else if (i2c_bus->cmd_err) {
+ dev_dbg(i2c_bus->dev, "recovery error\n");
+ ret = -EPROTO;
+ }
+ }
+
+ /* Recovery done */
+ state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ if (state & AST2600_I2CC_BUS_BUSY_STS) {
+ dev_dbg(i2c_bus->dev, "Can't recover bus [%x]\n", state);
+ ret = -EPROTO;
+ }
+
+ return ret;
+}
+
+static int ast2600_i2c_setup_dma_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (xfer_len > AST2600_I2C_DMA_SIZE)
+ xfer_len = AST2600_I2C_DMA_SIZE;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (xfer_len) {
+ memcpy(i2c_bus->controller_dma_buf, msg->buf, xfer_len);
+ cmd |= AST2600_I2CM_TX_DMA_EN | AST2600_I2CM_TX_CMD;
+ writel(AST2600_I2CM_SET_TX_DMA_LEN(xfer_len - 1),
+ i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+ u32 wbuf_dword;
+ int i;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (xfer_len > i2c_bus->buf_size)
+ xfer_len = i2c_bus->buf_size;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (xfer_len) {
+ cmd |= AST2600_I2CM_TX_BUFF_EN | AST2600_I2CM_TX_CMD;
+ /*
+ * The controller's buffer register supports dword writes only.
+ * Therefore, write dwords to the buffer register in a 4-byte aligned,
+ * and write the remaining unaligned data at the end.
+ */
+ for (i = 0; i < xfer_len; i += 4) {
+ int xfer_cnt = i2c_bus->controller_xfer_cnt + i;
+
+ switch (min(xfer_len - i, 4) % 4) {
+ case 1:
+ wbuf_dword = msg->buf[xfer_cnt];
+ break;
+ case 2:
+ wbuf_dword = get_unaligned_le16(&msg->buf[xfer_cnt]);
+ break;
+ case 3:
+ wbuf_dword = get_unaligned_le24(&msg->buf[xfer_cnt]);
+ break;
+ default:
+ wbuf_dword = get_unaligned_le32(&msg->buf[xfer_cnt]);
+ break;
+ }
+ writel(wbuf_dword, i2c_bus->buf_base + i);
+ }
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(xfer_len),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_byte_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len;
+
+ xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&
+ ((i2c_bus->controller_xfer_cnt + 1) == msg->len))
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (xfer_len) {
+ cmd |= AST2600_I2CM_TX_CMD;
+ writel(msg->buf[i2c_bus->controller_xfer_cnt],
+ i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_dma_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_DMA_EN | AST2600_I2CM_RX_CMD;
+
+ if (msg->flags & I2C_M_RECV_LEN)
+ xfer_len = 1;
+ else if (xfer_len > AST2600_I2C_DMA_SIZE)
+ xfer_len = AST2600_I2C_DMA_SIZE;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+
+ writel(AST2600_I2CM_SET_RX_DMA_LEN(xfer_len - 1), i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_BUFF_EN | AST2600_I2CM_RX_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ dev_dbg(i2c_bus->dev, "smbus read\n");
+ xfer_len = 1;
+ } else if (xfer_len > i2c_bus->buf_size) {
+ xfer_len = i2c_bus->buf_size;
+ } else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) {
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+ }
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(xfer_len), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_byte_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ dev_dbg(i2c_bus->dev, "smbus read\n");
+ } else if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&
+ ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) {
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_do_start(struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+ /* send start */
+ dev_dbg(i2c_bus->dev, "[%d] %s %d byte%s %s 0x%02x\n",
+ i2c_bus->msgs_index, str_read_write(msg->flags & I2C_M_RD),
+ msg->len, str_plural(msg->len),
+ msg->flags & I2C_M_RD ? "from" : "to", msg->addr);
+
+ if (!i2c_bus->setup_rx || !i2c_bus->setup_tx)
+ return -EINVAL;
+
+ i2c_bus->controller_xfer_cnt = 0;
+ i2c_bus->buf_index = 0;
+
+ if (msg->flags & I2C_M_RD)
+ return i2c_bus->setup_rx(AST2600_I2CM_START_CMD, i2c_bus);
+
+ return i2c_bus->setup_tx(AST2600_I2CM_START_CMD, i2c_bus);
+}
+
+static int ast2600_i2c_irq_err_to_errno(u32 irq_status)
+{
+ if (irq_status & AST2600_I2CM_ARBIT_LOSS)
+ return -EAGAIN;
+ if (irq_status & (AST2600_I2CM_SDA_DL_TO | AST2600_I2CM_SCL_LOW_TO))
+ return -ETIMEDOUT;
+ if (irq_status & (AST2600_I2CM_ABNORMAL))
+ return -EPROTO;
+
+ return 0;
+}
+
+static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len;
+ int i;
+
+ sts &= ~AST2600_I2CM_PKT_DONE;
+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ switch (sts) {
+ case AST2600_I2CM_PKT_ERROR:
+ i2c_bus->cmd_err = -EAGAIN;
+ complete(&i2c_bus->cmd_complete);
+ break;
+ case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK: /* a0 fix for issue */
+ fallthrough;
+ case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK | AST2600_I2CM_NORMAL_STOP:
+ i2c_bus->cmd_err = -ENXIO;
+ complete(&i2c_bus->cmd_complete);
+ break;
+ case AST2600_I2CM_NORMAL_STOP:
+ /* write 0 byte only have stop isr */
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index < i2c_bus->msgs_count) {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ } else {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ }
+ break;
+ case AST2600_I2CM_TX_ACK:
+ case AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP:
+ if (i2c_bus->mode == DMA_MODE)
+ xfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CM_DMA_LEN_STS));
+ else if (i2c_bus->mode == BUFF_MODE)
+ xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ else
+ xfer_len = 1;
+
+ i2c_bus->controller_xfer_cnt += xfer_len;
+
+ if (i2c_bus->controller_xfer_cnt == msg->len) {
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ } else {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ }
+ } else {
+ i2c_bus->setup_tx(0, i2c_bus);
+ }
+ break;
+ case AST2600_I2CM_RX_DONE:
+ case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
+ /* do next rx */
+ if (i2c_bus->mode == DMA_MODE) {
+ xfer_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CM_DMA_LEN_STS));
+ memcpy(&msg->buf[i2c_bus->controller_xfer_cnt],
+ i2c_bus->controller_dma_buf, xfer_len);
+ } else if (i2c_bus->mode == BUFF_MODE) {
+ xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < xfer_len; i++)
+ msg->buf[i2c_bus->controller_xfer_cnt + i] =
+ readb(i2c_bus->buf_base + 0x10 + i);
+ } else {
+ xfer_len = 1;
+ msg->buf[i2c_bus->controller_xfer_cnt] =
+ AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base +
+ AST2600_I2CC_STS_AND_BUFF));
+ }
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ u8 recv_len = AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base
+ + AST2600_I2CC_STS_AND_BUFF));
+ msg->len = min_t(unsigned int, recv_len, I2C_SMBUS_BLOCK_MAX);
+ msg->len += ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
+ msg->flags &= ~I2C_M_RECV_LEN;
+ if (!recv_len)
+ i2c_bus->controller_xfer_cnt = 0;
+ else
+ i2c_bus->controller_xfer_cnt = 1;
+ } else {
+ i2c_bus->controller_xfer_cnt += xfer_len;
+ }
+
+ if (i2c_bus->controller_xfer_cnt == msg->len) {
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ } else {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ }
+ } else {
+ i2c_bus->setup_rx(0, i2c_bus);
+ }
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled sts %x\n", sts);
+ break;
+ }
+}
+
+static int ast2600_i2c_controller_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 sts = readl(i2c_bus->reg_base + AST2600_I2CM_ISR);
+ u32 ctrl;
+
+ sts &= ~AST2600_I2CM_SMBUS_ALERT;
+
+ if (sts & AST2600_I2CM_BUS_RECOVER_FAIL) {
+ writel(AST2600_I2CM_BUS_RECOVER_FAIL, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ i2c_bus->cmd_err = -EPROTO;
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ if (sts & AST2600_I2CM_BUS_RECOVER) {
+ writel(AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ i2c_bus->cmd_err = 0;
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ i2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts);
+ if (i2c_bus->cmd_err) {
+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ if (sts & AST2600_I2CM_PKT_DONE) {
+ ast2600_i2c_controller_packet_irq(i2c_bus, sts);
+ return 1;
+ }
+
+ return 0;
+}
+
+static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_id;
+
+ return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
+}
+
+static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(adap);
+ unsigned long timeout;
+ int ret;
+
+ if (!i2c_bus->multi_master &&
+ (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & AST2600_I2CC_BUS_BUSY_STS)) {
+ ret = ast2600_i2c_recover_bus(i2c_bus);
+ if (ret)
+ return ret;
+ }
+
+ i2c_bus->cmd_err = 0;
+ i2c_bus->msgs = msgs;
+ i2c_bus->msgs_index = 0;
+ i2c_bus->msgs_count = num;
+ reinit_completion(&i2c_bus->cmd_complete);
+ ret = ast2600_i2c_do_start(i2c_bus);
+ if (ret)
+ goto controller_out;
+ timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+ if (timeout == 0) {
+ u32 ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ dev_dbg(i2c_bus->dev, "timeout isr[%x], sts[%x]\n",
+ readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ /*
+ * A slave holding SCL low can stall the transfer and trigger
+ * a master timeout. In multi-master mode, attempt bus recovery
+ * if the bus is still busy.
+ */
+ if (i2c_bus->multi_master &&
+ (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
+ AST2600_I2CC_BUS_BUSY_STS))
+ ast2600_i2c_recover_bus(i2c_bus);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = i2c_bus->cmd_err;
+ }
+
+ dev_dbg(i2c_bus->dev, "bus%d-m: %d end\n", i2c_bus->adap.nr, i2c_bus->cmd_err);
+
+controller_out:
+ return ret;
+}
+
+static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE | AST2600_I2CC_MASTER_EN;
+
+ /* I2C Reset */
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ if (!i2c_bus->multi_master)
+ fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS;
+
+ /* Enable Controller Mode */
+ writel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ /* disable target address */
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ /* Set AC Timing */
+ ast2600_i2c_ac_timing_config(i2c_bus);
+
+ if (i2c_bus->dma_available) {
+ i2c_bus->controller_dma_buf =
+ dmam_alloc_coherent(i2c_bus->dev, AST2600_I2C_DMA_SIZE,
+ &i2c_bus->controller_dma_addr, GFP_KERNEL);
+ if (!i2c_bus->controller_dma_buf)
+ return -ENOMEM;
+ writel(i2c_bus->controller_dma_addr,
+ i2c_bus->reg_base + AST2600_I2CM_TX_DMA);
+ writel(i2c_bus->controller_dma_addr,
+ i2c_bus->reg_base + AST2600_I2CM_RX_DMA);
+ }
+
+ /* Clear Interrupt */
+ writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
+
+ return 0;
+}
+
+static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm i2c_ast2600_algorithm = {
+ .xfer = ast2600_i2c_controller_xfer,
+ .functionality = ast2600_i2c_functionality,
+};
+
+static void ast2600_i2c_set_xfer_mode(struct ast2600_i2c_bus *i2c_bus,
+ enum xfer_mode mode)
+{
+ i2c_bus->mode = mode;
+
+ switch (mode) {
+ case DMA_MODE:
+ i2c_bus->setup_tx = ast2600_i2c_setup_dma_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_dma_rx;
+ break;
+ case BYTE_MODE:
+ i2c_bus->setup_tx = ast2600_i2c_setup_byte_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_byte_rx;
+ break;
+ case BUFF_MODE:
+ default:
+ i2c_bus->setup_tx = ast2600_i2c_setup_buff_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_buff_rx;
+ break;
+ }
+}
+
+static int ast2600_i2c_xfer_mode_parse(struct ast2600_i2c_bus *i2c_bus,
+ const char *buf, enum xfer_mode *mode)
+{
+ if (sysfs_streq(buf, "byte")) {
+ *mode = BYTE_MODE;
+ return 0;
+ }
+
+ if (sysfs_streq(buf, "buffer")) {
+ if (!i2c_bus->buf_base)
+ return -EINVAL;
+ *mode = BUFF_MODE;
+ return 0;
+ }
+
+ if (sysfs_streq(buf, "dma")) {
+ if (!i2c_bus->dma_available)
+ return -EINVAL;
+ *mode = DMA_MODE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const char *ast2600_i2c_xfer_mode_name(enum xfer_mode mode)
+{
+ switch (mode) {
+ case BYTE_MODE:
+ return "byte";
+ case DMA_MODE:
+ return "dma";
+ case BUFF_MODE:
+ default:
+ return "buffer";
+ }
+}
+
+static ssize_t xfer_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", ast2600_i2c_xfer_mode_name(i2c_bus->mode));
+}
+
+static ssize_t xfer_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
+ enum xfer_mode mode;
+ int ret;
+
+ ret = ast2600_i2c_xfer_mode_parse(i2c_bus, buf, &mode);
+ if (ret)
+ return ret;
+
+ i2c_lock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
+ ast2600_i2c_set_xfer_mode(i2c_bus, mode);
+ i2c_unlock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(xfer_mode);
+
+static int ast2600_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ast2600_i2c_bus *i2c_bus;
+ struct reset_control *rst;
+ struct resource *res;
+ u32 global_ctrl;
+ int ret;
+
+ if (!device_property_present(dev, "aspeed,global-regs"))
+ return -ENODEV;
+
+ i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);
+ if (!i2c_bus)
+ return -ENOMEM;
+
+ i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i2c_bus->reg_base))
+ return PTR_ERR(i2c_bus->reg_base);
+
+ rst = devm_reset_control_get_shared_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst), "Missing reset ctrl\n");
+
+ i2c_bus->global_regs =
+ syscon_regmap_lookup_by_phandle(dev_of_node(dev), "aspeed,global-regs");
+ if (IS_ERR(i2c_bus->global_regs))
+ return PTR_ERR(i2c_bus->global_regs);
+
+ regmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL, &global_ctrl);
+ if ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) {
+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CTRL, AST2600_GLOBAL_INIT);
+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
+ }
+
+ i2c_bus->dev = dev;
+ i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
+ i2c_bus->dma_available = device_property_read_bool(dev, "aspeed,enable-dma");
+ if (i2c_bus->dma_abailable)
+ i2c_bus->mode = DMA_MODE;
+ else
+ i2c_bus->mode = BUFF_MODE;
+
+ if (i2c_bus->mode == BUFF_MODE) {
+ i2c_bus->buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(i2c_bus->buf_base))
+ i2c_bus->mode = BYTE_MODE;
+ else
+ i2c_bus->buf_size = resource_size(res) / 2;
+ }
+
+ ast2600_i2c_set_xfer_mode(i2c_bus, i2c_bus->mode);
+
+ /*
+ * i2c timeout counter: use base clk4 1Mhz,
+ * per unit: 1/(1000/1024) = 1024us
+ */
+ ret = device_property_read_u32(dev, "i2c-scl-clk-low-timeout-us", &i2c_bus->timeout);
+ if (!ret)
+ i2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024);
+
+ init_completion(&i2c_bus->cmd_complete);
+
+ i2c_bus->irq = platform_get_irq(pdev, 0);
+ if (i2c_bus->irq < 0)
+ return i2c_bus->irq;
+
+ platform_set_drvdata(pdev, i2c_bus);
+
+ i2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);
+ if (IS_ERR(i2c_bus->clk))
+ return dev_err_probe(i2c_bus->dev, PTR_ERR(i2c_bus->clk), "Can't get clock\n");
+
+ i2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);
+
+ i2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info, true);
+
+ /* Initialize the I2C adapter */
+ i2c_bus->adap.owner = THIS_MODULE;
+ i2c_bus->adap.algo = &i2c_ast2600_algorithm;
+ i2c_bus->adap.retries = 0;
+ i2c_bus->adap.dev.parent = i2c_bus->dev;
+ device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));
+ i2c_bus->adap.algo_data = i2c_bus;
+ strscpy(i2c_bus->adap.name, pdev->name);
+ i2c_set_adapdata(&i2c_bus->adap, i2c_bus);
+
+ ret = ast2600_i2c_init(i2c_bus);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to initial i2c %d\n", ret);
+
+ ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0,
+ dev_name(dev), i2c_bus);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "Unable to request irq %d\n",
+ i2c_bus->irq);
+ goto err;
+ }
+
+ writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+ i2c_bus->reg_base + AST2600_I2CM_IER);
+
+ ret = devm_i2c_add_adapter(dev, &i2c_bus->adap);
+ if (ret)
+ goto err;
+
+ ret = sysfs_create_file(&dev->kobj, &dev_attr_xfer_mode.attr);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+ return ret;
+}
+
+static void ast2600_i2c_remove(struct platform_device *pdev)
+{
+ struct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);
+
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_xfer_mode.attr);
+
+ /* Disable everything. */
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+}
+
+static const struct of_device_id ast2600_i2c_of_match[] = {
+ { .compatible = "aspeed,ast2600-i2c-bus" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match);
+
+static struct platform_driver ast2600_i2c_driver = {
+ .probe = ast2600_i2c_probe,
+ .remove = ast2600_i2c_remove,
+ .driver = {
+ .name = "ast2600-i2c",
+ .of_match_table = ast2600_i2c_of_match,
+ },
+};
+module_platform_driver(ast2600_i2c_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ 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