Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] usb: cdns3: plat: Expose platform core driver as library
From: Peter Chen @ 2026-05-11  2:42 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, gregkh, pawell, rogerq
  Cc: devicetree, linux-kernel, linux-usb, cix-kernel-upstream,
	linux-arm-kernel, arnd, Peter Chen
In-Reply-To: <20260511024244.981941-1-peter.chen@cixtech.com>

Split the Cadence USB3 platform probe/remove and PM paths into
cdns3_core_probe(), cdns3_core_remove(), and exported runtime/system
sleep helpers so SoC glue (e.g. Sky1) can instantiate the core on the
same platform_device deterministically.

Add glue.h documenting struct cdns3_probe_data and the public entry points.

Signed-off-by: Peter Chen <peter.chen@cixtech.com>
---
 drivers/usb/cdns3/cdns3-plat.c | 138 ++++++++++++++++++++++-----------
 drivers/usb/cdns3/glue.h       |  51 ++++++++++++
 2 files changed, 144 insertions(+), 45 deletions(-)
 create mode 100644 drivers/usb/cdns3/glue.h

diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 3fe3109a3688..2219cbff1c59 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -21,6 +21,7 @@
 
 #include "core.h"
 #include "gadget-export.h"
+#include "glue.h"
 #include "host-export.h"
 #include "drd.h"
 
@@ -59,29 +60,21 @@ static int cdns3_plat_host_init(struct cdns *cdns)
 }
 
 /**
- * cdns3_plat_probe - probe for cdns3 core device
- * @pdev: Pointer to cdns3 core platform device
+ * cdns3_core_probe - Initialize the Cadence USB3 platform core
+ * @data: Controller context and platform device supplied by the glue layer
  *
  * Returns 0 on success otherwise negative errno
  */
-static int cdns3_plat_probe(struct platform_device *pdev)
+int cdns3_core_probe(const struct cdns3_probe_data *data)
 {
+	struct platform_device *pdev = data->pdev;
 	struct device *dev = &pdev->dev;
-	struct resource	*res;
-	struct cdns *cdns;
+	struct cdns *cdns = data->cdns;
+	struct resource *res;
 	void __iomem *regs;
 	int ret;
 
-	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
-	if (!cdns)
-		return -ENOMEM;
-
-	cdns->dev = dev;
-	cdns->pdata = dev_get_platdata(dev);
-	if (cdns->pdata && cdns->pdata->override_apb_timeout)
-		cdns->override_apb_timeout = cdns->pdata->override_apb_timeout;
-
-	platform_set_drvdata(pdev, cdns);
+	dev_set_drvdata(dev, cdns);
 
 	ret = platform_get_irq_byname(pdev, "host");
 	if (ret < 0)
@@ -195,14 +188,41 @@ static int cdns3_plat_probe(struct platform_device *pdev)
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(cdns3_core_probe);
 
 /**
- * cdns3_plat_remove() - unbind drd driver and clean up
- * @pdev: Pointer to Linux platform device
+ * cdns3_plat_probe - probe for cdns3 core device
+ * @pdev: Pointer to cdns3 core platform device
+ *
+ * Returns 0 on success otherwise negative errno
  */
-static void cdns3_plat_remove(struct platform_device *pdev)
+static int cdns3_plat_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cdns *cdns;
+	struct cdns3_probe_data probe_data;
+
+	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
+	if (!cdns)
+		return -ENOMEM;
+
+	cdns->dev = dev;
+	cdns->pdata = dev_get_platdata(dev);
+	if (cdns->pdata && cdns->pdata->override_apb_timeout)
+		cdns->override_apb_timeout = cdns->pdata->override_apb_timeout;
+
+	probe_data.cdns = cdns;
+	probe_data.pdev = pdev;
+
+	return cdns3_core_probe(&probe_data);
+}
+
+/**
+ * cdns3_core_remove - Tear down the Cadence USB3 platform core
+ * @cdns: Controller context previously initialized by cdns3_core_probe()
+ */
+void cdns3_core_remove(struct cdns *cdns)
 {
-	struct cdns *cdns = platform_get_drvdata(pdev);
 	struct device *dev = cdns->dev;
 
 	pm_runtime_get_sync(dev);
@@ -213,24 +233,30 @@ static void cdns3_plat_remove(struct platform_device *pdev)
 	phy_exit(cdns->usb2_phy);
 	phy_exit(cdns->usb3_phy);
 }
+EXPORT_SYMBOL_GPL(cdns3_core_remove);
+
+/**
+ * cdns3_plat_remove() - unbind drd driver and clean up
+ * @pdev: Pointer to Linux platform device
+ */
+static void cdns3_plat_remove(struct platform_device *pdev)
+{
+	cdns3_core_remove(platform_get_drvdata(pdev));
+}
 
 #ifdef CONFIG_PM
 
-static int cdns3_set_platform_suspend(struct device *dev,
-				      bool suspend, bool wakeup)
+static int cdns3_set_platform_suspend(struct cdns *cdns, bool suspend, bool wakeup)
 {
-	struct cdns *cdns = dev_get_drvdata(dev);
-	int ret = 0;
-
 	if (cdns->pdata && cdns->pdata->platform_suspend)
-		ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
+		return cdns->pdata->platform_suspend(cdns->dev, suspend, wakeup);
 
-	return ret;
+	return 0;
 }
 
-static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
+static int cdns3_controller_suspend(struct cdns *cdns, pm_message_t msg)
 {
-	struct cdns *cdns = dev_get_drvdata(dev);
+	struct device *dev = cdns->dev;
 	bool wakeup;
 	unsigned long flags;
 
@@ -242,7 +268,7 @@ static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
 	else
 		wakeup = device_may_wakeup(dev);
 
-	cdns3_set_platform_suspend(cdns->dev, true, wakeup);
+	cdns3_set_platform_suspend(cdns, true, wakeup);
 	set_phy_power_off(cdns);
 	spin_lock_irqsave(&cdns->lock, flags);
 	cdns->in_lpm = true;
@@ -252,9 +278,8 @@ static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
 	return 0;
 }
 
-static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
+static int cdns3_controller_resume(struct cdns *cdns, pm_message_t msg)
 {
-	struct cdns *cdns = dev_get_drvdata(dev);
 	int ret;
 	unsigned long flags;
 
@@ -277,7 +302,7 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
 	if (ret)
 		return ret;
 
-	cdns3_set_platform_suspend(cdns->dev, false, false);
+	cdns3_set_platform_suspend(cdns, false, false);
 
 	spin_lock_irqsave(&cdns->lock, flags);
 	cdns_resume(cdns);
@@ -293,26 +318,37 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
 	return ret;
 }
 
-static int cdns3_plat_runtime_suspend(struct device *dev)
+int cdns3_runtime_suspend(struct cdns *cdns)
 {
-	return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
+	return cdns3_controller_suspend(cdns, PMSG_AUTO_SUSPEND);
 }
+EXPORT_SYMBOL_GPL(cdns3_runtime_suspend);
 
-static int cdns3_plat_runtime_resume(struct device *dev)
+int cdns3_runtime_resume(struct cdns *cdns)
 {
-	return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
+	return cdns3_controller_resume(cdns, PMSG_AUTO_RESUME);
 }
+EXPORT_SYMBOL_GPL(cdns3_runtime_resume);
 
-#ifdef CONFIG_PM_SLEEP
+static int cdns3_dev_runtime_suspend(struct device *dev)
+{
+	return cdns3_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int cdns3_dev_runtime_resume(struct device *dev)
+{
+	return cdns3_runtime_resume(dev_get_drvdata(dev));
+}
 
-static int cdns3_plat_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+int cdns3_pm_suspend(struct cdns *cdns)
 {
-	struct cdns *cdns = dev_get_drvdata(dev);
+	struct device *dev = cdns->dev;
 	int ret;
 
 	cdns_suspend(cdns);
 
-	ret = cdns3_controller_suspend(dev, PMSG_SUSPEND);
+	ret = cdns3_controller_suspend(cdns, PMSG_SUSPEND);
 	if (ret)
 		return ret;
 
@@ -321,18 +357,30 @@ static int cdns3_plat_suspend(struct device *dev)
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(cdns3_pm_suspend);
+
+int cdns3_pm_resume(struct cdns *cdns)
+{
+	return cdns3_controller_resume(cdns, PMSG_RESUME);
+}
+EXPORT_SYMBOL_GPL(cdns3_pm_resume);
+
+static int cdns3_dev_pm_suspend(struct device *dev)
+{
+	return cdns3_pm_suspend(dev_get_drvdata(dev));
+}
 
-static int cdns3_plat_resume(struct device *dev)
+static int cdns3_dev_pm_resume(struct device *dev)
 {
-	return cdns3_controller_resume(dev, PMSG_RESUME);
+	return cdns3_pm_resume(dev_get_drvdata(dev));
 }
 #endif /* CONFIG_PM_SLEEP */
 #endif /* CONFIG_PM */
 
 static const struct dev_pm_ops cdns3_pm_ops = {
-	SET_SYSTEM_SLEEP_PM_OPS(cdns3_plat_suspend, cdns3_plat_resume)
-	SET_RUNTIME_PM_OPS(cdns3_plat_runtime_suspend,
-			   cdns3_plat_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(cdns3_dev_pm_suspend, cdns3_dev_pm_resume)
+	SET_RUNTIME_PM_OPS(cdns3_dev_runtime_suspend,
+			   cdns3_dev_runtime_resume, NULL)
 };
 
 #ifdef CONFIG_OF
diff --git a/drivers/usb/cdns3/glue.h b/drivers/usb/cdns3/glue.h
new file mode 100644
index 000000000000..67cd1073b555
--- /dev/null
+++ b/drivers/usb/cdns3/glue.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * glue.h - Cadence USB3 DRD glue header
+ */
+
+#ifndef __DRIVERS_USB_CDNS3_GLUE_H
+#define __DRIVERS_USB_CDNS3_GLUE_H
+
+#include <linux/types.h>
+
+#include "core.h"
+
+struct platform_device;
+
+/**
+ * struct cdns3_probe_data - Parameters passed to cdns3_core_probe()
+ * @cdns: Cadence DRD controller context (allocated by the glue driver)
+ * @pdev: Platform device for resources and IRQs
+ */
+struct cdns3_probe_data {
+	struct cdns *cdns;
+	struct platform_device *pdev;
+};
+
+/**
+ * cdns3_core_probe - Initialize the Cadence USB3 platform core
+ * @data: Controller context and platform device supplied by the glue layer
+ *
+ * Performs resource mapping, PHY setup, cdns_init(), role setup, and runtime PM
+ * configuration for the standard platform binding of the Cadence USB3/USBSSP DRD IP.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int cdns3_core_probe(const struct cdns3_probe_data *data);
+
+/**
+ * cdns3_core_remove - Tear down the Cadence USB3 platform core
+ * @cdns: Controller context previously initialized by cdns3_core_probe()
+ */
+void cdns3_core_remove(struct cdns *cdns);
+
+/*
+ * The following callbacks are for glue drivers to invoke from their own
+ * &dev_pm_ops, so platform-specific work can wrap the shared controller logic.
+ */
+int cdns3_runtime_suspend(struct cdns *cdns);
+int cdns3_runtime_resume(struct cdns *cdns);
+int cdns3_pm_suspend(struct cdns *cdns);
+int cdns3_pm_resume(struct cdns *cdns);
+
+#endif /* __DRIVERS_USB_CDNS3_GLUE_H */
-- 
2.50.1



^ permalink raw reply related

* [PATCH 4/4] arm64: dts: cix: add Sky1 USB4 and USB5 controllers
From: Peter Chen @ 2026-05-11  2:42 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, gregkh, pawell, rogerq
  Cc: devicetree, linux-kernel, linux-usb, cix-kernel-upstream,
	linux-arm-kernel, arnd, Peter Chen
In-Reply-To: <20260511024244.981941-1-peter.chen@cixtech.com>

Add the Sky1 USB4 and USB5 Cadence USB3 controller nodes with their
registers, interrupts, clocks, resets and S5 syscon control. Enable both
ports on the Orion O6 board in host mode with the required VBUS pinctrl.

Signed-off-by: Peter Chen <peter.chen@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 30 ++++++++++
 arch/arm64/boot/dts/cix/sky1.dtsi         | 68 +++++++++++++++++++++++
 2 files changed, 98 insertions(+)

diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
index e39c87774c12..d1e2afceea15 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -80,6 +80,22 @@ pins {
 
 		};
 	};
+
+	pinctrl_usb4: usb4-power-on-cfg {
+		pins {
+			pinmux = <CIX_PAD_GPIO041_FUNC_USB_DRIVE_VBUS4>;
+			bias-pull-down;
+			drive-strength = <8>;
+		};
+	};
+
+	pinctrl_usb5: usb5-power-on-cfg {
+		pins {
+			pinmux = <CIX_PAD_GPIO042_FUNC_USB_DRIVE_VBUS5>;
+			bias-pull-down;
+			drive-strength = <8>;
+		};
+	};
 };
 
 &pcie_x8_rc {
@@ -117,3 +133,17 @@ &s5_gpio2 {
 &uart2 {
 	status = "okay";
 };
+
+&usb4 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usb4>;
+	dr_mode = "host";
+	status = "okay";
+};
+
+&usb5 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usb5>;
+	dr_mode = "host";
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index bb5cfb1f2113..9f7d9ad6586c 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -6,6 +6,8 @@
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
 #include "sky1-power.h"
 
 / {
@@ -504,6 +506,72 @@ mbox_ap2sfh: mailbox@80a0000 {
 			cix,mbox-dir = "tx";
 		};
 
+		usb4: usb@91d0000 {
+			compatible = "cix,sky1-usb3", "cix,cdns-usb3";
+			reg = <0x00 0x91d0000 0x00 0x4000>,
+					<0x00 0x91d4000 0x00 0x4000>,
+					<0x00 0x91d8000 0x00 0x8000>,
+					<0x00 0x91c0314 0x00 0x4>;
+			reg-names = "otg", "dev", "xhci", "glue";
+
+			interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>,	/* host irq */
+					<GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>,	/* peripheral irq */
+					<GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH 0>,	/* otgirq */
+					<GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>;	/* wakeup irq */
+			interrupt-names = "host",
+					"peripheral",
+					"otg",
+					"wakeup";
+
+			resets = <&s5_syscon SKY1_USBC_SS2_PRST_N>,
+				<&s5_syscon SKY1_USBC_SS2_RST_N>;
+			reset-names = "prst", "rst";
+
+			clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
+				<&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
+				<&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
+				<&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
+			clock-names = "sof", "aclk", "lpm", "pclk";
+
+			cix,syscon-usb = <&s5_syscon>;
+			dma-coherent;
+			maximum-speed = "super-speed-plus";
+			dr_mode = "otg";
+		};
+
+		usb5: usb@91e0000 {
+			compatible = "cix,sky1-usb3", "cix,cdns-usb3";
+			reg = <0x00 0x91e0000 0x00 0x4000>,
+					<0x00 0x91e4000 0x00 0x4000>,
+					<0x00 0x91e8000 0x00 0x8000>,
+					<0x00 0x91c0324 0x00 0x4>;
+			reg-names = "otg", "dev", "xhci", "glue";
+
+			interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>,	/* host irq */
+					<GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>,	/* peripheral irq */
+					<GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH 0>,	/* otgirq */
+					<GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>;	/* wakeup irq */
+			interrupt-names = "host",
+					"peripheral",
+					"otg",
+					"wakeup";
+
+			resets = <&s5_syscon SKY1_USBC_SS3_PRST_N>,
+				<&s5_syscon SKY1_USBC_SS3_RST_N>;
+			reset-names = "prst", "rst";
+
+			clocks = <&scmi_clk CLK_TREE_USB3A_H1_CLK_SOF>,
+				<&scmi_clk CLK_TREE_USB3A_1_AXI_GATE>,
+				<&scmi_clk CLK_TREE_USB3A_H1_CLK_LPM>,
+				<&scmi_clk CLK_TREE_USB3A_1_APB_GATE>;
+			clock-names = "sof", "aclk", "lpm", "pclk";
+
+			cix,syscon-usb = <&s5_syscon>;
+			dma-coherent;
+			maximum-speed = "super-speed-plus";
+			dr_mode = "otg";
+		};
+
 		pcie_x8_rc: pcie@a010000 {
 			compatible = "cix,sky1-pcie-host";
 			reg = <0x00 0x0a010000 0x00 0x10000>,
-- 
2.50.1



^ permalink raw reply related

* [PATCH v3 2/4] remoteproc: imx_rproc: add support for Cortex-A Core
From: Jiafei Pan @ 2026-05-11  2:39 UTC (permalink / raw)
  To: andersson, mathieu.poirier, peng.fan, Frank.Li, s.hauer, kernel,
	festevam, imx, linux-arm-kernel, linux-kernel
  Cc: Zhiqiang.Hou, mingkai.hu, linux-remoteproc, devicetree,
	Jiafei Pan
In-Reply-To: <20260511023928.39640-1-Jiafei.Pan@nxp.com>

Add Cortex-A Core remoteproc support, it use PSCI and SIP SMC call
to manage Cortex-A Core to be on or off.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>
---

Changes in v3:
- Updated prefix of patch subject

---
 drivers/remoteproc/imx_rproc.c | 134 +++++++++++++++++++++++++++++++++
 drivers/remoteproc/imx_rproc.h |   2 +
 2 files changed, 136 insertions(+)

diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 0dd80e688b0e..8a3de27c96b7 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -6,6 +6,7 @@
 #include <dt-bindings/firmware/imx/rsrc.h>
 #include <linux/arm-smccc.h>
 #include <linux/clk.h>
+#include <linux/cpu.h>
 #include <linux/err.h>
 #include <linux/firmware/imx/sci.h>
 #include <linux/firmware/imx/sm.h>
@@ -20,12 +21,17 @@
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
+#include <linux/psci.h>
 #include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/remoteproc.h>
 #include <linux/scmi_imx_protocol.h>
 #include <linux/workqueue.h>
 
+#include <uapi/linux/psci.h>
+
+#include <asm/smp_plat.h>
+
 #include "imx_rproc.h"
 #include "remoteproc_internal.h"
 
@@ -72,6 +78,8 @@
 #define IMX_SIP_RPROC_STARTED		0x01
 #define IMX_SIP_RPROC_STOP		0x02
 
+#define IMX_SIP_CPU_OFF			0xC2000012
+
 #define IMX_SC_IRQ_GROUP_REBOOTED	5
 
 /**
@@ -131,6 +139,9 @@ struct imx_rproc {
 	 * BIT 0: IMX_RPROC_FLAGS_SM_LMM_CTRL(RPROC LM is under Linux control )
 	 */
 	u32				flags;
+	/* used by Cortex-A Core remoteproc to manage all CPU Cores */
+	cpumask_t			cpus;
+	cpumask_t			offlined_cpus;
 };
 
 static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = {
@@ -381,6 +392,45 @@ static int imx_rproc_sm_lmm_start(struct rproc *rproc)
 	return 0;
 }
 
+static int imx_rproc_psci_start(struct rproc *rproc)
+{
+	struct imx_rproc *priv = rproc->priv;
+	struct device *dev = priv->dev;
+	unsigned int cpu;
+	int ret;
+
+	if (cpumask_empty(&priv->cpus)) {
+		dev_err(dev, "No CPU Core assigned!\n");
+		return -ENODEV;
+	}
+
+	for_each_cpu(cpu, &priv->cpus) {
+		if (cpu_online(cpu)) {
+			ret = remove_cpu(cpu);
+			if (ret)
+				goto err;
+			cpumask_set_cpu(cpu, &priv->offlined_cpus);
+		}
+	}
+
+	cpu = cpumask_first(&priv->cpus);
+	ret = psci_ops.cpu_on(cpu_logical_map(cpu), rproc->bootaddr);
+	if (ret) {
+		dev_err(dev, "Boot failed on CPU Core %d\n", cpu);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	for_each_cpu(cpu, &priv->cpus) {
+		if (!cpu_online(cpu) && add_cpu(cpu) == 0)
+			cpumask_clear_cpu(cpu, &priv->offlined_cpus);
+	}
+
+	return ret;
+}
+
 static int imx_rproc_start(struct rproc *rproc)
 {
 	struct imx_rproc *priv = rproc->priv;
@@ -456,6 +506,50 @@ static int imx_rproc_sm_lmm_stop(struct rproc *rproc)
 	return scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0);
 }
 
+static int imx_rproc_psci_stop(struct rproc *rproc)
+{
+	struct imx_rproc *priv = rproc->priv;
+	struct device *dev = priv->dev;
+	struct arm_smccc_res res;
+	unsigned int cpu;
+	unsigned long start, end;
+	int err;
+
+	for_each_cpu(cpu, &priv->cpus) {
+		/* Check CPU status */
+		err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+		if (err == PSCI_0_2_AFFINITY_LEVEL_OFF)
+			continue;
+
+		/* Bring CPU to be off */
+		arm_smccc_smc(IMX_SIP_CPU_OFF, cpu, 0,
+			0, 0, 0, 0, 0, &res);
+		start = jiffies;
+		end = start + msecs_to_jiffies(100);
+		do {
+			err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+			if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+				pr_info("CPU%d is killed (polled %d ms)\n", cpu,
+					jiffies_to_msecs(jiffies - start));
+				break;
+			}
+
+			usleep_range(100, 1000);
+		} while (time_before(jiffies, end));
+	}
+
+	/* Return back freed CPU Core to Linux kernel */
+	for_each_cpu(cpu, &priv->cpus) {
+		if (cpumask_test_cpu(cpu, &priv->offlined_cpus)) {
+			if (add_cpu(cpu) != 0)
+				dev_err(dev, "Failed to bring CPU %d back to be online", cpu);
+			cpumask_clear_cpu(cpu, &priv->offlined_cpus);
+		}
+	}
+
+	return 0;
+}
+
 static int imx_rproc_stop(struct rproc *rproc)
 {
 	struct imx_rproc *priv = rproc->priv;
@@ -480,6 +574,12 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
 	int i;
 
+	/* No need to translate for Cortex-A Core */
+	if (dcfg->flags & IMX_RPROC_NO_ADDR_TRANS) {
+		*sys = da;
+		return 0;
+	}
+
 	/* parse address translation table */
 	for (i = 0; i < dcfg->att_size; i++) {
 		const struct imx_rproc_att *att = &dcfg->att[i];
@@ -1184,6 +1284,13 @@ static int imx_rproc_sm_detect_mode(struct rproc *rproc)
 	return imx_rproc_sm_lmm_check(rproc, started);
 }
 
+static int imx_rproc_psci_detect_mode(struct rproc *rproc)
+{
+	rproc->state = RPROC_OFFLINE;
+
+	return 0;
+}
+
 static int imx_rproc_detect_mode(struct imx_rproc *priv)
 {
 	/*
@@ -1228,6 +1335,8 @@ static int imx_rproc_probe(struct platform_device *pdev)
 	struct imx_rproc *priv;
 	struct rproc *rproc;
 	const struct imx_rproc_dcfg *dcfg;
+	unsigned int cpus;
+	unsigned long cpus_bits;
 	int ret;
 
 	/* set some other name then imx */
@@ -1274,6 +1383,17 @@ static int imx_rproc_probe(struct platform_device *pdev)
 	if (ret)
 		return dev_err_probe(dev, ret, "failed on imx_rproc_addr_init\n");
 
+	ret = of_property_read_u32(dev->of_node, "fsl,cpus-bits", &cpus);
+	if (ret) {
+		cpumask_clear(&priv->cpus);
+	} else {
+		cpus_bits = cpus;
+		bitmap_copy(cpumask_bits(&priv->cpus), &cpus_bits,
+				min((unsigned int)nr_cpumask_bits,
+				    (unsigned int)sizeof(unsigned long)));
+		rproc->auto_boot = false;
+	}
+
 	ret = imx_rproc_detect_mode(priv);
 	if (ret)
 		return dev_err_probe(dev, ret, "failed on detect mode\n");
@@ -1372,6 +1492,12 @@ static const struct imx_rproc_plat_ops imx_rproc_ops_sm_cpu = {
 	.stop		= imx_rproc_sm_cpu_stop,
 };
 
+static const struct imx_rproc_plat_ops imx_rproc_ops_psci = {
+	.start		= imx_rproc_psci_start,
+	.stop		= imx_rproc_psci_stop,
+	.detect_mode	= imx_rproc_psci_detect_mode,
+};
+
 static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = {
 	.src_reg	= IMX7D_SRC_SCR,
 	.src_mask	= IMX7D_M4_RST_MASK,
@@ -1464,6 +1590,13 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = {
 	.lmid		= 1, /* Use 1 as Logical Machine ID where M7 resides */
 };
 
+static const struct imx_rproc_dcfg imx_rproc_cfg_psci = {
+	.att		= NULL,
+	.att_size	= 0,
+	.ops		= &imx_rproc_ops_psci,
+	.flags		= IMX_RPROC_NO_ADDR_TRANS,
+};
+
 static const struct of_device_id imx_rproc_of_match[] = {
 	{ .compatible = "fsl,imx7ulp-cm4", .data = &imx_rproc_cfg_imx7ulp },
 	{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
@@ -1479,6 +1612,7 @@ static const struct of_device_id imx_rproc_of_match[] = {
 	{ .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp },
 	{ .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 },
 	{ .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 },
+	{ .compatible = "fsl,imx-rproc-psci", .data = &imx_rproc_cfg_psci },
 	{},
 };
 MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h
index d37e6f90548c..be6709971042 100644
--- a/drivers/remoteproc/imx_rproc.h
+++ b/drivers/remoteproc/imx_rproc.h
@@ -18,6 +18,8 @@ struct imx_rproc_att {
 /* dcfg flags */
 #define IMX_RPROC_NEED_SYSTEM_OFF	BIT(0)
 #define IMX_RPROC_NEED_CLKS		BIT(1)
+/* No need address translation */
+#define IMX_RPROC_NO_ADDR_TRANS		BIT(2)
 
 struct imx_rproc_plat_ops {
 	int (*start)(struct rproc *rproc);
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 3/4] remoteproc: imx_rproc: add autoboot support for A-core
From: Jiafei Pan @ 2026-05-11  2:39 UTC (permalink / raw)
  To: andersson, mathieu.poirier, peng.fan, Frank.Li, s.hauer, kernel,
	festevam, imx, linux-arm-kernel, linux-kernel
  Cc: Zhiqiang.Hou, mingkai.hu, linux-remoteproc, devicetree,
	Jiafei Pan
In-Reply-To: <20260511023928.39640-1-Jiafei.Pan@nxp.com>

From: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>

Add autoboot support for Cortex-A Core remoteproc.

Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>
---

Changes in v3:
- Added my signed-of-by.

---
 drivers/remoteproc/imx_rproc.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 8a3de27c96b7..e8d239bef5c9 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -1286,7 +1286,24 @@ static int imx_rproc_sm_detect_mode(struct rproc *rproc)
 
 static int imx_rproc_psci_detect_mode(struct rproc *rproc)
 {
-	rproc->state = RPROC_OFFLINE;
+	struct imx_rproc *priv = rproc->priv;
+	unsigned int cpu;
+	int cpu_aff;
+
+	rproc->state = RPROC_DETACHED;
+	for_each_cpu(cpu, &priv->cpus) {
+		cpu_aff = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+		if (cpu_aff == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+			rproc->state = RPROC_OFFLINE;
+			break;
+		}
+
+		/* in psci on state but running Linux */
+		if (cpu_online(cpu)) {
+			rproc->state = RPROC_OFFLINE;
+			break;
+		}
+	}
 
 	return 0;
 }
@@ -1391,7 +1408,6 @@ static int imx_rproc_probe(struct platform_device *pdev)
 		bitmap_copy(cpumask_bits(&priv->cpus), &cpus_bits,
 				min((unsigned int)nr_cpumask_bits,
 				    (unsigned int)sizeof(unsigned long)));
-		rproc->auto_boot = false;
 	}
 
 	ret = imx_rproc_detect_mode(priv);
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 4/4] arm64: dts: imx93: Cortex-A Core remoteproc device node
From: Jiafei Pan @ 2026-05-11  2:39 UTC (permalink / raw)
  To: andersson, mathieu.poirier, peng.fan, Frank.Li, s.hauer, kernel,
	festevam, imx, linux-arm-kernel, linux-kernel
  Cc: Zhiqiang.Hou, mingkai.hu, linux-remoteproc, devicetree,
	Jiafei Pan
In-Reply-To: <20260511023928.39640-1-Jiafei.Pan@nxp.com>

Create device tree for i.MX93 14x14 evk and 11x11 evk, add Cortex-A Core
remoteproc device nodes in these device tree.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>
Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
---
 arch/arm64/boot/dts/freescale/Makefile        |  2 +
 .../imx93-11x11-evk-multicore-rtos.dts        | 39 +++++++++++++++++++
 .../imx93-14x14-evk-multicore-rtos.dts        | 39 +++++++++++++++++++
 .../boot/dts/freescale/imx93-rproc-ca55.dtsi  | 14 +++++++
 4 files changed, 94 insertions(+)
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi

diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
index 711e36cc2c99..f067e5c44dae 100644
--- a/arch/arm64/boot/dts/freescale/Makefile
+++ b/arch/arm64/boot/dts/freescale/Makefile
@@ -460,12 +460,14 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-i3c.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtb
 
 dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb
+dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk-multicore-rtos.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-frdm.dtb
 
 imx93-11x11-frdm-pixpaper-dtbs += imx93-11x11-frdm.dtb imx93-11x11-frdm-pixpaper.dtbo
 dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-frdm-pixpaper.dtb
 
 dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb
+dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk-multicore-rtos.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-kontron-bl-osm-s.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin.dtb
diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts
new file mode 100644
index 000000000000..9fb2b94b83b4
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright 2023-2026 NXP
+ */
+
+/dts-v1/;
+#include "imx93-11x11-evk.dts"
+#include "imx93-rproc-ca55.dtsi"
+
+/ {
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/*
+		 * Reserve up to 16MB for one possible RTOS instances running on
+		 * one Cortex-A Cores when booting Linux on at least one Cortex-A Core.
+		 */
+		rtos_ca55_reserved: rtos-ca55@d0000000 {
+			no-map;
+			reg = <0 0xd0000000 0x0 0x1000000>;
+		};
+
+		/* Reserve 16MB for FreeRTOS on M33 */
+		m33_reserved: m33@a5000000 {
+			no-map;
+			reg = <0 0xa5000000 0 0x1000000>;
+		};
+	};
+};
+
+&lpuart2 {
+	status = "disabled";
+};
+
+&clk {
+	init-on-array = <IMX93_CLK_LPUART2_GATE>;
+};
diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts
new file mode 100644
index 000000000000..b2481bf19b4a
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright 2024-2026 NXP
+ */
+
+/dts-v1/;
+#include "imx93-14x14-evk.dts"
+#include "imx93-rproc-ca55.dtsi"
+
+/ {
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/*
+		 * Reserve up to 16MB for one possible RTOS instances running on
+		 * one Cortex-A Cores when booting Linux on at least one Cortex-A Core.
+		 */
+		rtos_ca55_reserved: rtos-ca55@d0000000 {
+			no-map;
+			reg = <0 0xd0000000 0x0 0x1000000>;
+		};
+
+		/* Reserve 16MB for FreeRTOS on M33 */
+		m33_reserved: m33@a5000000 {
+			no-map;
+			reg = <0 0xa5000000 0 0x1000000>;
+		};
+	};
+};
+
+&lpuart2 {
+	status = "disabled";
+};
+
+&clk {
+	init-on-array = <IMX93_CLK_LPUART2_GATE>;
+};
diff --git a/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi
new file mode 100644
index 000000000000..549a9e762d6a
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright 2024-2026 NXP
+ */
+
+/ {
+	ca55_1: remoteproc-ca55-1 {
+		compatible = "fsl,imx-rproc-psci";
+		/* bitmask:0b10, assign A55 Core 1 */
+		fsl,cpus-mask = <0x2>;
+		memory-region = <&rtos_ca55_reserved>;
+	};
+};
+
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 1/4] dt-bindings: remoteproc: add imx-rproc-psci
From: Jiafei Pan @ 2026-05-11  2:39 UTC (permalink / raw)
  To: andersson, mathieu.poirier, peng.fan, Frank.Li, s.hauer, kernel,
	festevam, imx, linux-arm-kernel, linux-kernel
  Cc: Zhiqiang.Hou, mingkai.hu, linux-remoteproc, devicetree,
	Jiafei Pan
In-Reply-To: <20260511023928.39640-1-Jiafei.Pan@nxp.com>

Add compatible string "fsl,imx-rproc-psci" for i.MX Cortex-A Core's
remoteproc support.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>

---
Changes in v3:
- Fixed dt_binding_check warnings

---
 .../remoteproc/fsl,imx-rproc-psci.yaml        | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc-psci.yaml

diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc-psci.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc-psci.yaml
new file mode 100644
index 000000000000..28d00dbf8bc7
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc-psci.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/fsl,imx-rproc-psci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX Cortex-A Core Remote Processor via PSCI
+
+maintainers:
+  - Jiafei Pan <Jiafei.Pan@nxp.com>
+
+description:
+  This binding provides support for managing Cortex-A cores as remote
+  processors on i.MX platforms using the PSCI (Power State Coordination
+  Interface) for CPU power management operations. This allows single
+  Cortex-A core or multiple Cortex-A cores to be controlled by Linux as
+  a remote processor, enabling them to run RTOS or bare-metal applications.
+
+properties:
+  compatible:
+    const: fsl,imx-rproc-psci
+
+  fsl,cpus-mask:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Bitmask indicating which CPU cores are assigned to this remote
+      processor instance. Each bit represents a CPU core, where bit N
+      corresponds to CPU N. For example, 0x2 (0b10) assigns CPU core 1,
+      while 0x6 (0b110) assigns CPU cores 1 and 2.
+
+  memory-region:
+    maxItems: 1
+    description:
+      Phandle to a reserved memory region to be used for the remote
+      processor's code and data.
+
+required:
+  - compatible
+  - fsl,cpus-mask
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+        remoteproc-ca55-1 {
+            compatible = "fsl,imx-rproc-psci";
+            /* bitmask: 0b10, assign A55 Core 1 */
+            fsl,cpus-mask = <0x2>;
+            memory-region = <&rtos_ca55_reserved>;
+        };
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 0/4] remoteproc: add Cortex-A Core remoteproc support on i.MX platforms
From: Jiafei Pan @ 2026-05-11  2:39 UTC (permalink / raw)
  To: andersson, mathieu.poirier, peng.fan, Frank.Li, s.hauer, kernel,
	festevam, imx, linux-arm-kernel, linux-kernel
  Cc: Zhiqiang.Hou, mingkai.hu, linux-remoteproc, devicetree,
	Jiafei Pan

This patch series is to add remoteproc support on Cortex-A Core of i.MX platforms:
1. Adding dts binding for Cortex-A Core remoteproc
2. Enable Cortex-A Core remoteproc support in remoteproc driver
3. Adding dts example on imx93 platforms.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>

---
Changes in v3:
- Fixed dt_binding_check warnings
- Updated prefix of patch subject

Changes in v2:
- Update arch/arm64/boot/dts/freescale/Makefile to add new dts

---
Hou Zhiqiang (1):
  remoteproc: imx_rproc: add autoboot support for A-core

Jiafei Pan (3):
  dt-bindings: remoteproc: add imx-rproc-psci
  remoteproc: imx_rproc: add support for Cortex-A Core
  arm64: dts: imx93: Cortex-A Core remoteproc device node

 .../remoteproc/fsl,imx-rproc-psci.yaml        |  51 ++++++
 arch/arm64/boot/dts/freescale/Makefile        |   2 +
 .../imx93-11x11-evk-multicore-rtos.dts        |  39 +++++
 .../imx93-14x14-evk-multicore-rtos.dts        |  39 +++++
 .../boot/dts/freescale/imx93-rproc-ca55.dtsi  |  14 ++
 drivers/remoteproc/imx_rproc.c                | 150 ++++++++++++++++++
 drivers/remoteproc/imx_rproc.h                |   2 +
 7 files changed, 297 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc-psci.yaml
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi

-- 
2.43.0



^ permalink raw reply

* [GIT PULL] CIX arm64 defconfig changes for v7.2-rc1
From: Peter Chen @ 2026-05-11  2:27 UTC (permalink / raw)
  To: soc, arm, soc; +Cc: fugang.duan, linux-arm-kernel, cix-kernel-upstream

Hi Arnd,

Please note: it may get merge conflict with soc/defconfig branch
since I got below merge conflict message from Thierry Reding at
May 8th.

	Today's linux-next merge of the cix tree got a conflict in:

	  arch/arm64/configs/defconfig

	between commit:

	  f54f7979ff88 ("arm64: defconfig: Move entries to match savedefconfig")

	from the arm-soc trees and commit:

	  f4f1e3bdb5f9 ("arm64: defconfig: Enable CIX Sky1 pinctrl, PCIe host, and Cadence GPIO")

	from the cix tree.


The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:

  Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git/ tags/cix-defconfig-v7.2-rc1

for you to fetch changes up to 246e37739f24b00a5559535dc814a315e7774639:

  arm64: defconfig: Enable CIX Sky1 pinctrl, PCIe host, and Cadence GPIO (2026-05-09 18:15:49 +0800)

----------------------------------------------------------------
- Enable CIX Sky1 pinctrl, PCIe host, and Cadence GPIO for CIX platform

----------------------------------------------------------------
Peter Chen (1):
      arm64: defconfig: Enable CIX Sky1 pinctrl, PCIe host, and Cadence GPIO

 arch/arm64/configs/defconfig | 3 +++
 1 file changed, 3 insertions(+)

-- 

Best regards,
Peter


^ permalink raw reply

* Re: [PATCH v2 4/7] Add dtsi to describe the xe180100 CAMSS block
From: Wenmeng Liu @ 2026-05-11  2:24 UTC (permalink / raw)
  To: Bryan O'Donoghue, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Vladimir Zapolskiy,
	Bryan O'Donoghue, Robert Foss, Todor Tomov,
	Mauro Carvalho Chehab
  Cc: linux-arm-msm, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-media, Konrad Dybcio
In-Reply-To: <328860b4-1d4b-4a90-9d10-cb70207aaa5c@linaro.org>



On 5/8/2026 10:23 PM, Bryan O'Donoghue wrote:
> On 08/05/2026 12:40, Wenmeng Liu wrote:
>> From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>>
>> 4 x CSIPHY
>> 2 x CSID
>> 2 x CSID Lite
>> 2 x IFE
>> 2 x IFE Lite
>>
>> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>> Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
> I'll repost my working version with the PHY stuff so I'd kind of 
> appreciate you not posting an old version here, just wait for it to land.
> 
> ---
> bod

If we are able to designate the old version as a candidate, and should 
the new version continue to encounter substantial resistance, we can 
provide a functional version.

Thanks,
Wenmeng


^ permalink raw reply

* [PATCH] ARM: move the only use of machine_desc.reboot_mode into mach-omap2
From: Ethan Nelson-Moore @ 2026-05-11  2:18 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Russell King, Ethan Nelson-Moore, Aaro Koskinen, Andreas Kemnade,
	Kevin Hilman, Roger Quadros, Tony Lindgren, Andrew Morton,
	Jiri Bohac

struct machine_desc defines a reboot_mode field which is only set by
mach-omap2. Simplify the code and make it more generic by moving the
code to set reboot_mode from arch/arm/kernel/setup.c into mach-omap2.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 arch/arm/include/asm/mach/arch.h    | 1 -
 arch/arm/kernel/setup.c             | 3 ---
 arch/arm/mach-omap2/board-generic.c | 6 ------
 arch/arm/mach-omap2/io.c            | 7 +++++++
 4 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 2b18a258204d..d4688cd1f080 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -40,7 +40,6 @@ struct machine_desc {
 	unsigned char		reserve_lp0 :1;	/* never has lp0	*/
 	unsigned char		reserve_lp1 :1;	/* never has lp1	*/
 	unsigned char		reserve_lp2 :1;	/* never has lp2	*/
-	enum reboot_mode	reboot_mode;	/* default restart mode	*/
 	unsigned		l2c_aux_val;	/* L2 cache aux value	*/
 	unsigned		l2c_aux_mask;	/* L2 cache aux mask	*/
 	void			(*l2c_write_sec)(unsigned long, unsigned);
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 0bfd66c7ada0..6430646a46bf 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -1123,9 +1123,6 @@ void __init setup_arch(char **cmdline_p)
 	machine_name = mdesc->name;
 	dump_stack_set_arch_desc("%s", mdesc->name);
 
-	if (mdesc->reboot_mode != REBOOT_HARD)
-		reboot_mode = mdesc->reboot_mode;
-
 	setup_initial_init_mm(_text, _etext, _edata, _end);
 
 	/* populate cmd_line too for later use, preserving boot_command_line */
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 68e0baad2bbf..fde6ccb3df6e 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -246,12 +246,6 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
 	.init_time	= omap_init_time_of,
 	.dt_compat	= am33xx_boards_compat,
 	.restart	= am33xx_restart,
-	/*
-	 * Historically am33xx supported only REBOOT_WARM even though default
-	 * reboot_mode was REBOOT_COLD. Reflect legacy de-facto behaviour in
-	 * SYSFS.
-	 */
-	.reboot_mode	= REBOOT_WARM,
 MACHINE_END
 #endif
 
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 14ec3f78000b..26a12d083dce 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -15,6 +15,7 @@
  */
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/clk.h>
@@ -546,6 +547,12 @@ void __init ti816x_init_early(void)
 #ifdef CONFIG_SOC_AM33XX
 void __init am33xx_init_early(void)
 {
+	/*
+	 * Historically am33xx supported only REBOOT_WARM even though default
+	 * reboot_mode was REBOOT_COLD. Reflect legacy de-facto behaviour in
+	 * SYSFS.
+	 */
+	reboot_mode = REBOOT_WARM;
 	omap2_set_globals_tap(AM335X_CLASS,
 			      AM33XX_L4_WK_IO_ADDRESS(AM33XX_TAP_BASE));
 	omap2_control_base_init();
-- 
2.43.0



^ permalink raw reply related

* [RFC] media: sun6i-csi: V3S parallel HREF/VREF polarity mismatch with GC0308
From: Yixiang Xu @ 2026-05-11  1:44 UTC (permalink / raw)
  To: linux-media; +Cc: Yong Deng, Paul Kocialkowski, linux-arm-kernel, linux-sunxi

Hi,

I am debugging a parallel DVP GC0308 camera on an Allwinner V3S board
with Linux 6.8.12 and the mainline sun6i-csi driver.

The sensor probes correctly and the media graph is created, but the
captured frames are invalid unless the HREF/VREF polarity bits in
SUN6I_CSI_IF_CFG_REG are forced to 1.

Hardware / software setup:

SoC: Allwinner V3S
Kernel: Linux 6.8.12
Driver: drivers/media/platform/sunxi/sun6i-csi/
Sensor: GalaxyCore GC0308, parallel DVP, UYVY8_2X8
MCLK: 24 MHz
Bus: 8-bit parallel

Relevant device-tree endpoint properties:

bus-width = <8>;
data-shift = <0>;
hsync-active = <1>;
vsync-active = <1>;
data-active = <1>;
pclk-sample = <1>;

The GC0308 sensor side works:

I2C address 0x21 is occupied by the kernel driver

the sensor node appears as "gc0308 0-0021"

media-ctl shows:

gc0308 0-0021 -> sun6i-csi-bridge -> sun6i-csi-capture

the sensor source pad is set to:

UYVY8_2X8/640x480

With the unmodified sun6i-csi polarity mapping, the driver computes:

IF_CFG=0x00010000

That means:

bit16 PCLK = 1
bit17 HREF = 0
bit18 VREF = 0

The resulting captured buffers have the expected size, but the contents
are not valid image data. Typical UYVY/422P dumps look like repeated
single-byte patterns plus zero-filled tails, for example:

e8 e8 e8 e8 ...
d1 d1 d1 d1 ...
84 84 84 84 ...

As an A/B test, I forced the parallel CSI IF_CFG polarity bits to match
the old Allwinner BSP behaviour:

value |= SUN6I_CSI_IF_CFG_CLK_POL_RISING |
             SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE |
             SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE;

This changes the log to:

IF_CFG=0x00070000

After this change, the captured UYVY data starts looking like real
U/Y/V/Y data, for example:

80 16 80 16 80 15 7f 15 ...

and the frame displays correctly on the LCD.

I also compared against an old working Allwinner BSP for the same board
and sensor. The old GC0308 BSP driver uses:

VREF_POL = V4L2_MBUS_VSYNC_ACTIVE_HIGH
HREF_POL = V4L2_MBUS_HSYNC_ACTIVE_HIGH
CLK_POL = V4L2_MBUS_PCLK_SAMPLE_RISING

The old BSP path is known to display the GC0308 image correctly.
It effectively programs the CSI timing/polarity bits as 1 for
PCLK/HREF/VREF on this board.

This looks related to the existing comment in sun6i_csi_reg.h:

/* Note that Allwinner manuals and code invert positive/negative definitions. */

and also to the previous discussion:

[PATCH] media: sun6i-csi: Fix incorrect HSYNC/VSYNC/PCLK polarity configuration
https://www.spinics.net/lists/arm-kernel/msg771128.html

Parallel Camera Interface Timings, Signal Definitions and Polarity?
https://www.spinics.net/lists/arm-kernel/msg771862.html

Question:

What would be the preferred way to fix or represent this?

Change the sun6i-csi parallel HREF/VREF mapping so that
active-high hsync/vsync sets IF_CFG bit17/18 to 1?

Add a V3S-specific quirk for the parallel HREF/VREF mapping?

Use a device-tree workaround by writing:

hsync-active = <0>;
vsync-active = <0>;
pclk-sample = <1>;

so that the current driver computes IF_CFG=0x00070000,
even though this contradicts the GC0308 BSP/sensor active-high
description?

Is there a better way to distinguish HREF/VREF semantics from
HSYNC/VSYNC polarity in the binding or driver?

I am not proposing the hardcoded A/B-test patch as an upstream fix.
It was only used to isolate the issue. I can provide dmesg logs,
media-ctl output, raw frame dumps, and test additional variants if that
helps.

Thanks,
Andrew


^ permalink raw reply

* [RFC PATCH] ARM: move reserve_lp[012] handling into affected machines
From: Ethan Nelson-Moore @ 2026-05-11  1:14 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: Russell King, Ethan Nelson-Moore, Andrew Morton, Jiri Bohac

arch/arm/kernel/setup.c contains code to reserve lp0/1/2 I/O ports for
machines that can't possibly have these ports. This code is only used
by netwinder and footbridge, and is small enough that it can just be
moved into these machines. Do so to make the setup code more generic
and the machine code more self-contained.

This patch is an RFC because I'm not sure if using .init_early is
actually necessary. I did it to match the place the original code was
called as closely as possible. Can anyone weigh in on this?

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 arch/arm/include/asm/mach/arch.h        |  3 --
 arch/arm/kernel/setup.c                 | 37 -------------------
 arch/arm/mach-footbridge/netwinder-hw.c | 48 ++++++++++++++++++-------
 arch/arm/mach-rpc/riscpc.c              | 30 ++++++++++++++--
 4 files changed, 63 insertions(+), 55 deletions(-)

diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 2b18a258204d..fc5abdd2fe88 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -37,9 +37,6 @@ struct machine_desc {
 	unsigned int		video_start;	/* start of video RAM	*/
 	unsigned int		video_end;	/* end of video RAM	*/
 
-	unsigned char		reserve_lp0 :1;	/* never has lp0	*/
-	unsigned char		reserve_lp1 :1;	/* never has lp1	*/
-	unsigned char		reserve_lp2 :1;	/* never has lp2	*/
 	enum reboot_mode	reboot_mode;	/* default restart mode	*/
 	unsigned		l2c_aux_val;	/* L2 cache aux value	*/
 	unsigned		l2c_aux_mask;	/* L2 cache aux mask	*/
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 0bfd66c7ada0..5416ef7b4f31 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -8,7 +8,6 @@
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/stddef.h>
-#include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/utsname.h>
 #include <linux/initrd.h>
@@ -185,31 +184,6 @@ static struct resource mem_res[] = {
 #define kernel_code mem_res[1]
 #define kernel_data mem_res[2]
 
-static struct resource io_res[] = {
-	{
-		.name = "reserved",
-		.start = 0x3bc,
-		.end = 0x3be,
-		.flags = IORESOURCE_IO | IORESOURCE_BUSY
-	},
-	{
-		.name = "reserved",
-		.start = 0x378,
-		.end = 0x37f,
-		.flags = IORESOURCE_IO | IORESOURCE_BUSY
-	},
-	{
-		.name = "reserved",
-		.start = 0x278,
-		.end = 0x27f,
-		.flags = IORESOURCE_IO | IORESOURCE_BUSY
-	}
-};
-
-#define lp0 io_res[0]
-#define lp1 io_res[1]
-#define lp2 io_res[2]
-
 static const char *proc_arch[] = {
 	"undefined/unknown",
 	"3",
@@ -909,17 +883,6 @@ static void __init request_standard_resources(const struct machine_desc *mdesc)
 		video_ram.end   = mdesc->video_end;
 		request_resource(&iomem_resource, &video_ram);
 	}
-
-	/*
-	 * Some machines don't have the possibility of ever
-	 * possessing lp0, lp1 or lp2
-	 */
-	if (mdesc->reserve_lp0)
-		request_resource(&ioport_resource, &lp0);
-	if (mdesc->reserve_lp1)
-		request_resource(&ioport_resource, &lp1);
-	if (mdesc->reserve_lp2)
-		request_resource(&ioport_resource, &lp2);
 }
 
 #if defined(CONFIG_VGA_CONSOLE)
diff --git a/arch/arm/mach-footbridge/netwinder-hw.c b/arch/arm/mach-footbridge/netwinder-hw.c
index ab17ba916d47..6a4f15526189 100644
--- a/arch/arm/mach-footbridge/netwinder-hw.c
+++ b/arch/arm/mach-footbridge/netwinder-hw.c
@@ -616,23 +616,47 @@ static int __init nw_hw_init(void)
 
 __initcall(nw_hw_init);
 
+/* The NetWinder never has lp0 or lp2 */
+static struct resource reserved_resources[] = {
+	{
+		/* lp0 */
+		.name = "reserved",
+		.start = 0x3bc,
+		.end = 0x3be,
+		.flags = IORESOURCE_IO | IORESOURCE_BUSY
+	},
+	{
+		/* lp2 */
+		.name = "reserved",
+		.start = 0x278,
+		.end = 0x27f,
+		.flags = IORESOURCE_IO | IORESOURCE_BUSY
+	},
+};
+
 /*
  * Older NeTTroms either do not provide a parameters
  * page, or they don't supply correct information in
  * the parameter page.
  */
-static void __init
-fixup_netwinder(struct tag *tags, char **cmdline)
+static void __init netwinder_init_early(void)
 {
-#ifdef CONFIG_ISAPNP
-	extern int isapnp_disable;
+	int i;
 
-	/*
-	 * We must not use the kernels ISAPnP code
-	 * on the NetWinder - it will reset the settings
-	 * for the WaveArtist chip and render it inoperable.
-	 */
-	isapnp_disable = 1;
+	for (i = 0; i < ARRAY_SIZE(reserved_resources); i++)
+		request_resource(&ioport_resource, &reserved_resources[i]);
+
+#ifdef CONFIG_ISAPNP
+	{
+		extern int isapnp_disable;
+
+		/*
+		 * We must not use the kernels ISAPnP code
+		 * on the NetWinder - it will reset the settings
+		 * for the WaveArtist chip and render it inoperable.
+		 */
+		isapnp_disable = 1;
+	}
 #endif
 }
 
@@ -763,9 +787,7 @@ MACHINE_START(NETWINDER, "Rebel-NetWinder")
 	.atag_offset	= 0x100,
 	.video_start	= 0x000a0000,
 	.video_end	= 0x000bffff,
-	.reserve_lp0	= 1,
-	.reserve_lp2	= 1,
-	.fixup		= fixup_netwinder,
+	.init_early	= netwinder_init_early,
 	.map_io		= footbridge_map_io,
 	.nr_irqs	= FOOTBRIDGE_NR_IRQS,
 	.init_irq	= footbridge_init_irq,
diff --git a/arch/arm/mach-rpc/riscpc.c b/arch/arm/mach-rpc/riscpc.c
index 14d78b7f9493..6d764a660474 100644
--- a/arch/arm/mach-rpc/riscpc.c
+++ b/arch/arm/mach-rpc/riscpc.c
@@ -16,6 +16,7 @@
 #include <linux/serial_8250.h>
 #include <linux/ata_platform.h>
 #include <linux/io.h>
+#include <linux/ioport.h>
 #include <linux/i2c.h>
 #include <linux/reboot.h>
 
@@ -185,6 +186,32 @@ static struct i2c_board_info i2c_rtc = {
 	I2C_BOARD_INFO("pcf8583", 0x50)
 };
 
+/* The Risc PC never has lp0 or lp1 */
+static struct resource reserved_resources[] = {
+	{
+		/* lp0 */
+		.name = "reserved",
+		.start = 0x3bc,
+		.end = 0x3be,
+		.flags = IORESOURCE_IO | IORESOURCE_BUSY
+	},
+	{
+		/* lp1 */
+		.name = "reserved",
+		.start = 0x378,
+		.end = 0x37f,
+		.flags = IORESOURCE_IO | IORESOURCE_BUSY
+	},
+};
+
+static void __init rpc_init_early(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(reserved_resources); i++)
+		request_resource(&ioport_resource, &reserved_resources[i]);
+}
+
 static int __init rpc_init(void)
 {
 	i2c_register_board_info(0, &i2c_rtc, 1);
@@ -208,8 +235,7 @@ void ioc_timer_init(void);
 MACHINE_START(RISCPC, "Acorn-RiscPC")
 	/* Maintainer: Russell King */
 	.atag_offset	= 0x100,
-	.reserve_lp0	= 1,
-	.reserve_lp1	= 1,
+	.init_early	= rpc_init_early,
 	.map_io		= rpc_map_io,
 	.nr_irqs	= RPC_NR_IRQS,
 	.init_irq	= rpc_init_irq,
-- 
2.43.0



^ permalink raw reply related

* [PATCH] ARM: riscpc: simplify vram_size calculation
From: Ethan Nelson-Moore @ 2026-05-10 23:38 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel; +Cc: Russell King, Ethan Nelson-Moore

The code to calculate vram_size in parse_tag_acorn() is unnecessarily
complex and inflexible. Replace it with a simple multiplication.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 arch/arm/mach-rpc/riscpc.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/arch/arm/mach-rpc/riscpc.c b/arch/arm/mach-rpc/riscpc.c
index bdad13226c6d..14d78b7f9493 100644
--- a/arch/arm/mach-rpc/riscpc.c
+++ b/arch/arm/mach-rpc/riscpc.c
@@ -43,17 +43,8 @@ static int __init parse_tag_acorn(const struct tag *tag)
 {
 	memc_ctrl_reg = tag->u.acorn.memc_control_reg;
 	number_mfm_drives = tag->u.acorn.adfsdrives;
+	vram_size = tag->u.acorn.vram_pages * PAGE_SIZE;
 
-	switch (tag->u.acorn.vram_pages) {
-	case 512:
-		vram_size += PAGE_SIZE * 256;
-		fallthrough;	/* ??? */
-	case 256:
-		vram_size += PAGE_SIZE * 256;
-		break;
-	default:
-		break;
-	}
 #if 0
 	if (vram_size) {
 		desc->video_start = 0x02000000;
-- 
2.43.0



^ permalink raw reply related

* [PATCH] ARM: remove unnecessary __LINUX_ARM_ARCH__ check in <asm/cp15.h>
From: Ethan Nelson-Moore @ 2026-05-10 22:50 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel; +Cc: Russell King, Ethan Nelson-Moore

__LINUX_ARM_ARCH__ is only defined to 3 on the Risc PC. This is because
its currently supported configuration uses a StrongARM ARMv4 CPU, but
its bus does not support half-word accesses, so LDRH/STRH instructions
are disabled for it. Otherwise, it is a standard ARMv4 platform.
Support for actual ARMv3 CPUs was dropped in commit 357c9c1f07d4 ("ARM:
Remove support for ARMv3 ARM610 and ARM710 CPUs"), and therefore the
check for __LINUX_ARM_ARCH__ in <asm/cp15.h> is unnecessary. Remove it.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 arch/arm/include/asm/cp15.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h
index a54230e65647..0d9f40b346e9 100644
--- a/arch/arm/include/asm/cp15.h
+++ b/arch/arm/include/asm/cp15.h
@@ -42,11 +42,7 @@
 
 #ifndef __ASSEMBLY__
 
-#if __LINUX_ARM_ARCH__ >= 4
 #define vectors_high()	(get_cr() & CR_V)
-#else
-#define vectors_high()	(0)
-#endif
 
 #ifdef CONFIG_CPU_CP15
 
-- 
2.43.0



^ permalink raw reply related

* [PATCH RFC v2 1/4] dt-bindings: clk: zte: Add zx297520v3 clock and reset bindings.
From: Stefan Dösinger @ 2026-05-10 21:49 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel,
	Stefan Dösinger
In-Reply-To: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com>

These SoCs have 3 clock and reset controllers. The "top" controller -
all names follow ZTE's naming - controls core devices like the AHB bus,
most timers and the Cortex M0 that brings up the board. The register
layout is fairly chaotic. Some patterns can be found, but nothing that
holds true for all devices it controls.

Generally every device has two clocks (one work clock, and one that
connects it to the bus, I call it PCLK), two reset bits (I don't know
what the difference is - sometimes asserting one is enough to reset the
device, sometimes both need to be asserted) and one mux. Some devices,
like the GPIO controller, only have reset bits and no clocks.

The top clock controller is fed by a 26mhz external oscillator and has 4
PLLs to generate other clock rates. ZTE's kernel does not manipulate the
PLLs at all and relies on BROM and the boot loader to set them up. The
bitfields in the control registers are somewhat documented in a
Lauterback TRACE32 debug file in the kernel sources though. At the
moment, my driver extracts clock rates from the PLLs, but cannot change
them. A proper PLL clk is on my TODO list before I remove the [RFC] tag
from the submission. It will be necessary for the LTE hardware with
replacement boot loaders because BROM does not set up the LTE-related
PLL.

The "matrix" controller controls the main Cortex A53 CPU, the LTE ZSP,
SDIO and a few others. It is even more chaotic than the "top"
controller.

The "LSP" controller - I suspect it stands for "low speed peripherals" -
is very regular. One 32 bit register for 2 clock gates, two resets, one
mux (1-3 bit) and in some cases a 4 bit divider.

Not all clocks will have an explicit user in the end. I am defining a
lot of them simply to shut them off. The boot loader sets up a few of
the proprietary timers, which will send regular IRQs (although the
kernel of course doesn't need to listen to them). I don't plan to add a
driver for the proprietary timer as I see no use for them - the ARM arch
timer works just fine. I will add a driver for the very similar
proprietary watchdog though.

The top and matrix list is not exhaustive. There are other bits
that are enabled, but I couldn't deduce what they are controlling by
trial and error. Some of them seem to do nothing. Others cause an
instant hang of the board when disabled. I isolated a few (SRAM PCLK,
arm arch timer clock) where I don't see a reason to manipulate them. It
is quite likely that a handful more clocks will be added in the future,
but not a large number.

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
 .../bindings/clock/zte,zx297520v3-clk.yaml         | 173 ++++++++++++++++++++
 include/dt-bindings/clock/zte,zx297520v3-clk.h     | 179 +++++++++++++++++++++
 2 files changed, 352 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yaml b/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yaml
new file mode 100644
index 000000000000..3b7084a18a97
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yaml
@@ -0,0 +1,173 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/zte,zx297520v3-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ZTE zx297520v3 SoC clock and reset controller
+
+maintainers:
+  - Stefan Dösinger <stefandoesinger@gmail.com>
+
+description: |
+  The zx297520v3's clock controller consists of 3 controllers, which generate
+  clocks for internal SoC devices. In addition to clocks it also has reset
+  controls for most, but not all, devices.
+
+  While there is a certain hierarchy among the controllers ("top" controlls core
+  parts like the boot-up Cortex M0, "matrix" controls the main CPU and LTE DSP,
+  "lsp" controls peripherals"), in practise all 3 are required to reasonably
+  operate the SoC.
+
+  The top controller has two inputs: a 26 MHz and a 32 KHz external oscillator.
+  They need to be provided as input clocks. The matrix controller controlls 10
+  clock lines that get fed into the LSP controller. The LSP device node needs
+  to list these input clocks.
+
+  The matrix controller consumes clocks generated by PLLs in the top
+  controller, but there are no controls in the top controller to sever this
+  link. The interface between these controllers is not expressed in the device
+  tree, but the matrix controller cannot work without the clock handles
+  registered by the top controller.
+
+  All available clocks are defined as preprocessor macros in
+  'dt-bindings/clock/zte,zx297520v3-clk.h' header.
+
+properties:
+  compatible:
+    enum:
+      - zte,zx297520v3-topclk
+      - zte,zx297520v3-matrixclk
+      - zte,zx297520v3-lspclk
+
+  clocks:
+    minItems: 2
+    maxItems: 10
+
+  clock-names:
+    minItems: 2
+    maxItems: 10
+
+  "#clock-cells":
+    const: 1
+
+  "#reset-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: zte,zx297520v3-topclk
+    then:
+      properties:
+        clocks:
+          items:
+            - description: External reference clock (26 MHz)
+            - description: External reference clock (32 KHz)
+        clock-names:
+          items:
+            - const: osc26m
+            - const: osc32k
+      required:
+        - clocks
+        - clock-names
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: zte,zx297520v3-lspclk
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Main PLL divided by 5 output from matrixclk (124.8 MHz)
+            - description: Main PLL divided by 4 output from matrixclk (156 MHz)
+            - description: Main PLL divided by 6 output from matrixclk (104 MHz)
+            - description: Main PLL divided by 8 output from matrixclk (78 MHz)
+            - description: Main PLL divided by 12 output from matrixclk (52 MHz)
+            - description: Main oscillator output from matrixclk (26 MHz)
+            - description: Timer oscillator output from matrixclk (32 KHz)
+            - description: LSP pclk output from matrixclk (26 MHz)
+            - description: TDM wclk mux output from matrixclk
+            - description: DPLL divided by 4 output from matrixclk (122.88 MHz)
+        clock-names:
+          items:
+            - const: mpll_d5
+            - const: mpll_d4
+            - const: mpll_d6
+            - const: mpll_d8
+            - const: mpll_d12
+            - const: osc26m
+            - const: osc32k
+            - const: pclk
+            - const: tdm_wclk
+            - const: dpll_d4
+      required:
+        - clocks
+        - clock-names
+
+additionalProperties: false
+
+required:
+  - compatible
+  - '#clock-cells'
+  - reg
+  - '#reset-cells'
+
+examples:
+  - |
+    #include <dt-bindings/clock/zte,zx297520v3-clk.h>
+
+    osc26m: osc26m {
+      compatible = "fixed-clock";
+      clock-output-names = "osc26m";
+      #clock-cells = <0>;
+    };
+
+    osc32k: osc32k {
+      compatible = "fixed-clock";
+      clock-output-names = "osc32k";
+      #clock-cells = <0>;
+    };
+
+    topclk: topclk@13b000 {
+        compatible = "zte,zx297520v3-topclk";
+        reg = <0x0013b000 0x400>;
+        #clock-cells = <1>;
+        #reset-cells = <1>;
+        clocks = <&osc26m>, <&osc32k>;
+        clock-names = "osc26m", "osc32k";
+    };
+
+    matrixclk: matrixclk@1306000 {
+        compatible = "zte,zx297520v3-matrixclk";
+        reg = <0x01306000 0x400>;
+        #clock-cells = <1>;
+        #reset-cells = <1>;
+    };
+
+    lspclk: lspclk@1400000 {
+        compatible = "zte,zx297520v3-lspclk";
+        reg = <0x01400000 0x100>;
+        #clock-cells = <1>;
+        #reset-cells = <1>;
+
+        clocks = <&matrixclk ZX297520V3_LSP_MPLL_D5_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_MPLL_D4_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_MPLL_D6_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_MPLL_D8_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_MPLL_D12_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_OSC26M_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_OSC32K_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_PCLK>,
+                 <&matrixclk ZX297520V3_LSP_TDM_WCLK>,
+                 <&matrixclk ZX297520V3_LSP_DPLL_D4_WCLK>;
+        clock-names = "mpll_d5", "mpll_d4", "mpll_d6", "mpll_d8", "mpll_d12",
+                      "osc26m", "osc32k", "pclk", "tdm_wclk", "dpll_d4";
+    };
diff --git a/include/dt-bindings/clock/zte,zx297520v3-clk.h b/include/dt-bindings/clock/zte,zx297520v3-clk.h
new file mode 100644
index 000000000000..322b53be8b12
--- /dev/null
+++ b/include/dt-bindings/clock/zte,zx297520v3-clk.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Stefan Dösinger.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_ZX297520V3_H
+#define __DT_BINDINGS_CLOCK_ZX297520V3_H
+
+#define ZX297520V3_AHB_WCLK			0
+#define ZX297520V3_AHB_PCLK			1
+#define ZX297520V3_PMM_WCLK			2
+#define ZX297520V3_PMM_PCLK			3
+#define ZX297520V3_USB_24M			4
+#define ZX297520V3_USB_AHB			5
+#define ZX297520V3_TIMER_T08_WCLK		6
+#define ZX297520V3_TIMER_T08_PCLK		7
+#define ZX297520V3_TIMER_T09_WCLK		8
+#define ZX297520V3_TIMER_T09_PCLK		9
+#define ZX297520V3_TIMER_T12_WCLK		10
+#define ZX297520V3_TIMER_T12_PCLK		11
+#define ZX297520V3_TIMER_T13_WCLK		12
+#define ZX297520V3_TIMER_T13_PCLK		13
+#define ZX297520V3_TIMER_T14_WCLK		14
+#define ZX297520V3_TIMER_T14_PCLK		15
+#define ZX297520V3_TIMER_T15_WCLK		16
+#define ZX297520V3_TIMER_T15_PCLK		17
+#define ZX297520V3_TIMER_T16_WCLK		18
+#define ZX297520V3_TIMER_T16_PCLK		19
+#define ZX297520V3_TIMER_T17_WCLK		20
+#define ZX297520V3_TIMER_T17_PCLK		21
+#define ZX297520V3_WDT_T18_WCLK			22
+#define ZX297520V3_WDT_T18_PCLK			23
+#define ZX297520V3_UART0_WCLK			24
+#define ZX297520V3_UART0_PCLK			25
+#define ZX297520V3_I2C0_WCLK			26
+#define ZX297520V3_I2C0_PCLK			27
+#define ZX297520V3_RTC_WCLK			28
+#define ZX297520V3_RTC_PCLK			29
+#define ZX297520V3_LPM_GSM_WCLK			30
+#define ZX297520V3_LPM_GSM_PCLK			31
+#define ZX297520V3_LPM_LTE_WCLK			32
+#define ZX297520V3_LPM_LTE_PCLK			33
+#define ZX297520V3_LPM_TD_WCLK			34
+#define ZX297520V3_LPM_TD_PCLK			35
+#define ZX297520V3_LPM_W_WCLK			36
+#define ZX297520V3_LPM_W_PCLK			37
+#define ZX297520V3_USIM1_WCLK			38
+#define ZX297520V3_USIM1_PCLK			39
+#define ZX297520V3_M0_WCLK			40
+#define ZX297520V3_TOPCLK_END			41
+
+#define ZX297520V3_AHB_RESET			0
+#define ZX297520V3_TIMER_T08_RESET		1
+#define ZX297520V3_TIMER_T09_RESET		2
+#define ZX297520V3_TIMER_T12_RESET		3
+#define ZX297520V3_TIMER_T13_RESET		4
+#define ZX297520V3_TIMER_T14_RESET		5
+#define ZX297520V3_TIMER_T15_RESET		6
+#define ZX297520V3_TIMER_T16_RESET		7
+#define ZX297520V3_TIMER_T17_RESET		8
+#define ZX297520V3_WDT_T18_RESET		9
+#define ZX297520V3_UART0_RESET			10
+#define ZX297520V3_I2C0_RESET			11
+#define ZX297520V3_RTC_RESET			12
+#define ZX297520V3_USIM1_RESET			13
+#define ZX297520V3_PMM_RESET			14
+#define ZX297520V3_GPIO8_RESET			15
+#define ZX297520V3_GPIO_RESET			16
+#define ZX297520V3_ZSP_RESET			17
+#define ZX297520V3_USB_RESET			18
+#define ZX297520V3_TOPRST_END			19
+
+#define ZX297520V3_CPU_WCLK			0
+#define ZX297520V3_CPU_PCLK			1
+#define ZX297520V3_SD0_WCLK			2
+#define ZX297520V3_SD0_PCLK			3
+#define ZX297520V3_SD1_WCLK			4
+#define ZX297520V3_SD1_PCLK			5
+#define ZX297520V3_SD1_CDET			6
+#define ZX297520V3_NAND_WCLK			7
+#define ZX297520V3_NAND_PCLK			8
+#define ZX297520V3_SSC_WCLK			9
+#define ZX297520V3_SSC_PCLK			10
+#define ZX297520V3_EDCP_WCLK			11
+#define ZX297520V3_EDCP_PCLK			12
+#define ZX297520V3_EDCP_SYNCAXI			13
+#define ZX297520V3_VOU_WCLK			14
+#define ZX297520V3_VOU_PCLK			15
+#define ZX297520V3_PDCFG_WCLK			16
+#define ZX297520V3_PDCFG_PCLK			17
+#define ZX297520V3_GMAC_WCLK			18
+#define ZX297520V3_GMAC_RMII			19
+#define ZX297520V3_GMAC_PCLK			20
+#define ZX297520V3_ZSP_WCLK			21
+#define ZX297520V3_MBOX_PCLK			22
+#define ZX297520V3_DMA_PCLK			23
+#define ZX297520V3_LSP_MPLL_D5_WCLK		24
+#define ZX297520V3_LSP_MPLL_D4_WCLK		25
+#define ZX297520V3_LSP_MPLL_D6_WCLK		26
+#define ZX297520V3_LSP_MPLL_D8_WCLK		27
+#define ZX297520V3_LSP_MPLL_D12_WCLK		28
+#define ZX297520V3_LSP_OSC26M_WCLK		29
+#define ZX297520V3_LSP_OSC32K_WCLK		30
+#define ZX297520V3_LSP_PCLK			31
+#define ZX297520V3_LSP_TDM_WCLK			32
+#define ZX297520V3_LSP_DPLL_D4_WCLK		33
+#define ZX297520V3_MATRIXCLK_END		34
+
+#define ZX297520V3_CPU_RESET			0
+#define ZX297520V3_SD0_RESET			1
+#define ZX297520V3_SD1_RESET			2
+#define ZX297520V3_NAND_RESET			3
+#define ZX297520V3_SSC_RESET			4
+#define ZX297520V3_EDCP_RESET			5
+#define ZX297520V3_VOU_RESET			6
+#define ZX297520V3_PDCFG_RESET			7
+#define ZX297520V3_GMAC_RESET			8
+#define ZX297520V3_DMA_RESET			9
+#define ZX297520V3_MATRIXRST_END		10
+
+#define ZX297520V3_TIMER_L1_WCLK		0
+#define ZX297520V3_TIMER_L1_PCLK		1
+#define ZX297520V3_WDT_L2_WCLK			2
+#define ZX297520V3_WDT_L2_PCLK			3
+#define ZX297520V3_WDT_L3_WCLK			4
+#define ZX297520V3_WDT_L3_PCLK			5
+#define ZX297520V3_I2C1_WCLK			6
+#define ZX297520V3_I2C1_PCLK			7
+#define ZX297520V3_I2S0_WCLK			8
+#define ZX297520V3_I2S0_PCLK			9
+#define ZX297520V3_I2S1_WCLK			10
+#define ZX297520V3_I2S1_PCLK			11
+#define ZX297520V3_QSPI_WCLK			12
+#define ZX297520V3_QSPI_PCLK			13
+#define ZX297520V3_UART1_WCLK			14
+#define ZX297520V3_UART1_PCLK			15
+#define ZX297520V3_I2C2_WCLK			16
+#define ZX297520V3_I2C2_PCLK			17
+#define ZX297520V3_SPI0_WCLK			18
+#define ZX297520V3_SPI0_PCLK			19
+#define ZX297520V3_TIMER_LB_WCLK		20
+#define ZX297520V3_TIMER_LB_PCLK		21
+#define ZX297520V3_TIMER_LC_WCLK		22
+#define ZX297520V3_TIMER_LC_PCLK		23
+#define ZX297520V3_UART2_WCLK			24
+#define ZX297520V3_UART2_PCLK			25
+#define ZX297520V3_WDT_LE_WCLK			26
+#define ZX297520V3_WDT_LE_PCLK			27
+#define ZX297520V3_TIMER_LF_WCLK		28
+#define ZX297520V3_TIMER_LF_PCLK		29
+#define ZX297520V3_SPI1_WCLK			30
+#define ZX297520V3_SPI1_PCLK			31
+#define ZX297520V3_TIMER_L11_WCLK		32
+#define ZX297520V3_TIMER_L11_PCLK		33
+#define ZX297520V3_TDM_WCLK			34
+#define ZX297520V3_TDM_PCLK			35
+#define ZX297520V3_LSPCLK_END			36
+
+#define ZX297520V3_TIMER_L1_RESET		0
+#define ZX297520V3_WDT_L2_RESET			1
+#define ZX297520V3_WDT_L3_RESET			2
+#define ZX297520V3_I2C1_RESET			3
+#define ZX297520V3_I2S0_RESET			4
+#define ZX297520V3_I2S1_RESET			5
+#define ZX297520V3_QSPI_RESET			6
+#define ZX297520V3_UART1_RESET			7
+#define ZX297520V3_I2C2_RESET			8
+#define ZX297520V3_SPI0_RESET			9
+#define ZX297520V3_TIMER_LB_RESET		10
+#define ZX297520V3_TIMER_LC_RESET		11
+#define ZX297520V3_UART2_RESET			12
+#define ZX297520V3_WDT_LE_RESET			13
+#define ZX297520V3_TIMER_LF_RESET		14
+#define ZX297520V3_SPI1_RESET			15
+#define ZX297520V3_TIMER_L11_RESET		16
+#define ZX297520V3_TDM_RESET			17
+#define ZX297520V3_LSPRST_END			18
+
+#endif /* __DT_BINDINGS_CLOCK_ZX297520V3_H */

-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH] arm: boot: ep93xx: don't rely on machine_is_*() for removed board files
From: Alexander Sverdlin @ 2026-05-10 22:45 UTC (permalink / raw)
  To: Ethan Nelson-Moore, linux-arm-kernel, linux-kernel
  Cc: Russell King, Hartley Sweeten, Nikita Shubin
In-Reply-To: <20260509223820.50347-1-enelsonmoore@gmail.com>

Hi Ethan,

On Sat, 2026-05-09 at 15:38 -0700, Ethan Nelson-Moore wrote:
> Code in misc-ep93xx.h relies on machine_is_*() macros for several
> boards that no longer have legacy board files. They were removed in
> commit e5ef574dda70 ("ARM: ep93xx: delete all boardfiles"). This
> prevents the removal of machine IDs no longer used by the kernel from
> mach-types. To resolve this issue, create local copies of these macros.
> (The checks themselves are still valid because the IDs are still passed
> in by the bootloader on these machines.) Also take the opportunity to
> remove three repeated checks for the same ID.
> 
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>

Acked-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>

I'll pick the patch and send it to Arnd if there will be no objection
during this week.

> ---
>  arch/arm/boot/compressed/misc-ep93xx.h | 20 ++++++++++++++++----
>  1 file changed, 16 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm/boot/compressed/misc-ep93xx.h b/arch/arm/boot/compressed/misc-ep93xx.h
> index 65b4121d1490..b0a1b42aab31 100644
> --- a/arch/arm/boot/compressed/misc-ep93xx.h
> +++ b/arch/arm/boot/compressed/misc-ep93xx.h
> @@ -3,7 +3,22 @@
>   * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
>   */
>  
> -#include <asm/mach-types.h>
> +/*
> + * These machine IDs are no longer used by the kernel since EP93xx was converted
> + * to DT booting, but they are still passed in by bootloaders, so we use our own
> + * local definitions of the relevant macros.
> + */
> +#define machine_is_bk3()		(__machine_arch_type == 1880)
> +#define machine_is_edb9301()		(__machine_arch_type == 462)
> +#define machine_is_edb9302a()		(__machine_arch_type == 1127)
> +#define machine_is_edb9302()		(__machine_arch_type == 538)
> +#define machine_is_edb9307a()		(__machine_arch_type == 1128)
> +#define machine_is_edb9307()		(__machine_arch_type == 607)
> +#define machine_is_edb9312()		(__machine_arch_type == 451)
> +#define machine_is_edb9315a()		(__machine_arch_type == 772)
> +#define machine_is_edb9315()		(__machine_arch_type == 463)
> +#define machine_is_ts72xx()		(__machine_arch_type == 673)
> +#define machine_is_vision_ep9307()	(__machine_arch_type == 1578)
>  
>  static inline unsigned int __raw_readl(unsigned int ptr)
>  {
> @@ -60,14 +75,11 @@ static inline void ep93xx_decomp_setup(void)
>  	if (machine_is_edb9301() ||
>  	    machine_is_edb9302() ||
>  	    machine_is_edb9302a() ||
> -	    machine_is_edb9302a() ||
>  	    machine_is_edb9307() ||
>  	    machine_is_edb9307a() ||
> -	    machine_is_edb9307a() ||
>  	    machine_is_edb9312() ||
>  	    machine_is_edb9315() ||
>  	    machine_is_edb9315a() ||
> -	    machine_is_edb9315a() ||
>  	    machine_is_ts72xx() ||
>  	    machine_is_bk3() ||
>  	    machine_is_vision_ep9307())

-- 
Alexander Sverdlin.


^ permalink raw reply

* [RFC PATCH] KVM: arm64: vgic: Skip vCPU trylock for pre-init register access
From: David Woodhouse @ 2026-05-10 22:11 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Paolo Bonzini, Jonathan Cameron, Eric Auger,
	Raghavendra Rao Ananta, David Woodhouse, Maxim Levitsky,
	linux-arm-kernel, kvmarm, kvm

[-- Attachment #1: Type: text/plain, Size: 3676 bytes --]

From: David Woodhouse <dwmw@amazon.co.uk>

When creating a VM, userspace sets pre-init distributor registers such
as GICD_IIDR before the VGIC is initialized.

Strictly speaking there's no reason this couldn't be done at VM creation
time before any vCPUs exist at all, but the design of the userspace API
does require vCPU0 to have been created, as the system-wide registers
are effectively accessed via vCPU0.

So a VMM can't just set the IIDR at startup before spawning vCPU threads;
it has to be done once the vCPUs are being created.

However, kvm_trylock_all_vcpus() makes it unreliable by causing spurious
-EBUSY failures if any vCPU cannot be locked. So userspace is forced to
create the vCPUs (well, at least vCPU0), and is then forced to have a
full synchronization point and quiesce them all in order to reliably set
the IIDR.

To slightly reduce the pain of all this, skip the trylock when the VGIC
is not yet initialized. There is no need to lock the vCPUs if they can't
be accessing it anyway.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
Other options would include making it possible to set the IIDR before
creating any vCPUs, either by creating a new device-level attribute or
special-casing it not to require vCPU0 for DIST_REGS that aren't really
associated to a vCPU.

Deprecating kvm_trylock_all_vcpus() and killing every user of it with
fire would also be a good option, perhaps... :)

 arch/arm64/kvm/vgic/vgic-kvm-device.c | 38 ++++++++++++++++++++-------
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c
index a96c77dccf35..e17ea9f07f5f 100644
--- a/arch/arm64/kvm/vgic/vgic-kvm-device.c
+++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c
@@ -540,7 +540,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
 	struct vgic_reg_attr reg_attr;
 	gpa_t addr;
 	struct kvm_vcpu *vcpu;
-	bool uaccess;
+	bool uaccess, vcpus_locked = false;
 	u32 val;
 	int ret;
 
@@ -566,18 +566,37 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
 			return -EFAULT;
 	}
 
+	if (!vgic_initialized(dev->kvm) && !reg_allowed_pre_init(attr))
+		return -EBUSY;
+
 	mutex_lock(&dev->kvm->lock);
 
-	if (kvm_trylock_all_vcpus(dev->kvm)) {
-		mutex_unlock(&dev->kvm->lock);
-		return -EBUSY;
+	/*
+	 * Pre-init registers (e.g. GICD_IIDR) don't need vCPU quiescence
+	 * since the VGIC isn't live yet.  Skip the trylock to avoid spurious
+	 * -EBUSY when vCPU threads happen to be running.
+	 */
+	if (vgic_initialized(dev->kvm)) {
+		if (kvm_trylock_all_vcpus(dev->kvm)) {
+			mutex_unlock(&dev->kvm->lock);
+			return -EBUSY;
+		}
+		vcpus_locked = true;
 	}
-
 	mutex_lock(&dev->kvm->arch.config_lock);
 
-	if (!(vgic_initialized(dev->kvm) || reg_allowed_pre_init(attr))) {
-		ret = -EBUSY;
-		goto out;
+	/*
+	 * If the VGIC becomes initialized between the above check and taking
+	 * config_lock, drop config_lock to lock the VCPUS.
+	 */
+	if (vgic_initialized(dev->kvm) && !vcpus_locked) {
+		mutex_unlock(&dev->kvm->arch.config_lock);
+		if (kvm_trylock_all_vcpus(dev->kvm)) {
+			mutex_unlock(&dev->kvm->lock);
+			return -EBUSY;
+		}
+		mutex_lock(&dev->kvm->arch.config_lock);
+		vcpus_locked = true;
 	}
 
 	switch (attr->group) {
@@ -612,7 +631,8 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
 
 out:
 	mutex_unlock(&dev->kvm->arch.config_lock);
-	kvm_unlock_all_vcpus(dev->kvm);
+	if (vcpus_locked)
+		kvm_unlock_all_vcpus(dev->kvm);
 	mutex_unlock(&dev->kvm->lock);
 
 	if (!ret && uaccess && !is_write) {
-- 
2.43.0



[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply related

* Re: [PATCH] sound: soc: fsl: eukrea-tlv320: update board checks to use the DT
From: Ethan Nelson-Moore @ 2026-05-10 22:08 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-arm-kernel, linux-sound, Russell King, Liam Girdwood,
	Jaroslav Kysela, Takashi Iwai
In-Reply-To: <agCApXl_RwG3r2iu@sirena.co.uk>

Hi, Mark,

On Sun, May 10, 2026 at 5:57 AM Mark Brown <broonie@kernel.org> wrote:
> This renders the inclusion of asm/mach-types.h redundant.

Good point. I removed it in other drivers but missed it here, I'll
resend the patch with this fixed.

Ethan


^ permalink raw reply

* Re: [PATCH] arm: boot: ep93xx: don't rely on machine_is_*() for removed board files
From: Ethan Nelson-Moore @ 2026-05-10 21:53 UTC (permalink / raw)
  To: Alexander Sverdlin
  Cc: linux-arm-kernel, linux-kernel, Russell King, Hartley Sweeten,
	Nikita Shubin
In-Reply-To: <26be9a11b5f2a7d196904d9a97306d2aafb9bf1c.camel@gmail.com>

On Sun, May 10, 2026 at 10:40 AM Alexander Sverdlin
<alexander.sverdlin@gmail.com> wrote:
> I most probably miss something, but what is the reason for removing
> the machine IDs from the mach-types, especially if they are still used
> by the kernel (though only in its decompressor code)?

Hi, Alexander,

My plan after all my related patches are merged is to remove all
entries for legacy board files that are not used by the kernel (i.e.:
boards that were removed, never in mainline, or converted to DT
booting). This would reduce mach-types from 579 to 28 entries and
would allow it to be used as a list of non-DT boards remaining in the
kernel. The last time mach-types was similarly filtered was in 2016.

Ethan


^ permalink raw reply

* [PATCH RFC v2 3/4] clk: zte: Introduce a driver for zx297520v3 matrix clocks and resets.
From: Stefan Dösinger @ 2026-05-10 21:49 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel,
	Stefan Dösinger
In-Reply-To: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com>

This controls the CPU, DSP, DDR RAM, ethernet, SDIO controllers and a
few more devices. It also contains a number of clock gates to pass
clock signals down to the next controller.

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
 drivers/clk/zte/clk-zx297520v3.c | 215 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 214 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx297520v3.c
index aa304dd34b7b..d4b683cb6354 100644
--- a/drivers/clk/zte/clk-zx297520v3.c
+++ b/drivers/clk/zte/clk-zx297520v3.c
@@ -266,7 +266,7 @@ static int zx297520v3_pll(struct device *dev, void __iomem *base, const char *na
 	/* These are the fractionals of the PLLs I have seen. There should be a better way to
 	 * generate them than hardcode the list.
 	 */
-	static const unsigned int pll_fract[] = {2, 3, 4, 5, 6, 8, 12, 26};
+	static const unsigned int pll_fract[] = {2, 3, 4, 5, 6, 8, 12, 16, 26};
 
 	unsigned long ref, refdiv, fbdiv, vco, postdiv1, postdiv2, freq;
 	struct clk_hw *hw;
@@ -578,6 +578,219 @@ static struct platform_driver clk_zx297520v3_topclk = {
 };
 module_platform_driver(clk_zx297520v3_topclk);
 
+static const char * const cpu_sel[] = {
+	"osc26m",
+	"mpll",		/* 624 MHz */
+	"mpll_d2",	/* 312 MHz */
+	"mpll_d4",	/* 156 MHz */
+};
+
+static const char * const sd0_sel[] = {
+	"osc26m",
+	"mpll_d4",	/* 156 MHz */
+	"gpll_d2",	/* 100 MHz */
+	"mpll_d8",	/* 78 MHz */
+	"gpll_d4",	/* 50 MHz */
+	"gpll_d8",	/* 25 MHz */
+};
+
+static const char * const sd1_sel[] = {
+	"osc26m",
+	"gpll_d2",	/* 100 MHz */
+	"mpll_d8",	/* 78 MHz */
+	"gpll_d4",	/* 50 MHz */
+	"mpll_d16",	/* 39 MHz */
+	"gpll_d8",	/* 25 MHz */
+};
+
+static const char * const nand_sel[] = {
+	"mpll_d4",	/* 156 MHz */
+	"osc26m",
+};
+
+static const char * const edcp_sel[] = {
+	"osc26m",
+	"mpll_d4",	/* 156 MHz */
+	"mpll_d5",	/* 124.8 MHz */
+	"mpll_d6",	/* 104 MHz */
+};
+
+static const char * const tdm_sel[] = {
+	"osc26m",
+	"dpll_d4",	/* 122.88 MHz */
+	"mpll_d6",	/* 104 MHz */
+};
+
+static const struct zx297520v3_composite matrix_clocks[] = {
+	/* Both 0x24 and 0x28 bits 1 and 2 stop the CPU. There is also a bit in topclk+0x138, which
+	 * ZTE's uboot calls "A53 reset", which also stops the CPU. I can't really tell the
+	 * difference between matrix+28 and top+138. The clock can be disabled and enabled from the
+	 * Cortex M0 and it will nicely stop and restart the A53, retaining all state.
+	 *
+	 * 0x50, bits 0-3 have the DDR clock. A lot of DDR gates and resets are in 0x100.
+	 */
+	ZX_CLK_CRIT(CPU,    0x28,  1,  0x24,  1,  2, 0x20,  0, 2, cpu_sel,     0,     0, 0),
+	/* TODO: 0x54 bit 14 and 0x54 bit 6 are supposed to be card detection clocks. */
+	ZX_CLK(SD0,         0x58,  1,  0x54, 12, 13, 0x50,  4, 3, sd0_sel,     0,     0, 0),
+	ZX_CLK(SD1,         0x58,  0,  0x54,  4,  5, 0x50,  8, 3, sd1_sel,     0,     0, 0),
+	/* This is some "denali" NAND, not the qspi connected one. */
+	ZX_CLK(NAND,        0x58,  4,  0x54, 20, 21, 0x50, 12, 2, nand_sel,    0,     0, 0),
+	ZX_CLK(SSC,         0x94, 24,  0x84,  1,  2, 0,     0, 0, clk_unknown, 0,     0, 0),
+	ZX_CLK(EDCP,        0x68,  0,  0x64,  2,  1, 0x50, 16, 2, edcp_sel,    0,     0, 0),
+	/* PDCFG. Like PMM, either clock bit will allow the device to function. */
+	ZX_CLK_CRIT(PDCFG,  0x94, 20,  0x88,  0,  1, 0x50, 16, 2, clk_unknown, 0,     0, 0),
+	/* There are a lot more VOU related controls in these registers, but turning off the main
+	 * clock seems to shut off the entire VOU MMIO range.
+	 */
+	ZX_CLK(VOU,        0x16c,  0, 0x168,  0,  1, 0,     0, 0, clk_main,       0,     0, 0),
+};
+
+static const struct zx297520v3_gate matrix_gates[] = {
+	/* ZTE's driver has a statemt to the effect of *(matrix->base+0x11C) = 5, with a comment
+	 * suggesting that this sets a 50 mhz clock. The clock code itself lists the parents of
+	 * these clock as 50mhz pll output, but the GMAC driver never enables the clocks.
+	 *
+	 * The clocks below are enabled by the boot loader though, so they are on. And it turns
+	 * out that they are necessary for proper operation of the ethernet hardware. As far as
+	 * I can see trough experimentation, bit 1 affects the PHY whereas 0 and 2 affect the
+	 * MAC chip itself.
+	 *
+	 * Chain the wclk and rmii clk together for now. I haven't found a way to make either
+	 * the mdio node or the phy node enable a clock. According to ethernet-phy.yaml it is
+	 * supposed to be possible, but I can't find code to that effect in of_mdio.c.
+	 */
+	{ZX297520V3_GMAC_PCLK,	"gmac_pclk",		"gpll_d4",	0x110,	  0},
+	{ZX297520V3_GMAC_RMII,	"gmac_rmii",		"gpll_d4",	0x110,	  1},
+	{ZX297520V3_GMAC_RMII,	"gmac_wclk",		"gmac_rmii",	0x110,	  2},
+
+	/* ZSP aka LTE DSP clock. I think there is a mux at matrix+0x30, but I have no idea
+	 * about the frequencies it selects. Gate is at matrix+0x3c.
+	 */
+	{ZX297520V3_ZSP_WCLK,	"zsp_wclk",		"osc26m",	0x3c,	  0},
+
+	/* Mailbox. I haven't found a reset for this. It seems to have a PCLK only - turning it off
+	 * makes the MMIO area read 0x0. It looks like it does not need a WCLK. It generates IRQs
+	 * fine with just bit 2 set. Bits 1 and 3 are 0 by default in this register.
+	 */
+	{ZX297520V3_MBOX_PCLK,	"mbox_pclk",		"osc26m",	0x88,	  2},
+
+	/* DMA Controller. It has a reset and PCLK, but no WCLK. */
+	{ZX297520V3_DMA_PCLK,	"dma_pclk",		"osc26m",	0x94,	  3},
+
+	/* There is another clock controlling some "GSM" IP at 0xF3000000 in 0x88, bit 8. It appears
+	 * to be a PCLK, but I have not found a matching WCLK or reset yet.
+	 */
+
+	/* LSP uplink clocks. The PCLK is fairly obvious (disabling it shuts off the entire LSP
+	 * register area). The WCLK speeds were deduced by setting timers and qspi muxes to a
+	 * specific speed and seeing which bit in matrix+0x7c needs to be enabled for the device
+	 * to work.
+	 *
+	 * Due to the timers I am certain about the 26mhz and 32khz clocks. I cannot directly
+	 * observe the qspi mux frequency, so the clock rates depend on ZTE's qspi mux selection
+	 * being correct.
+	 *
+	 * Two additional bits are specific to sound components - the mux for the LSP's TDM IP is
+	 * in matrixclk and gets passed down. I2S has a mux in LSP, which can select the dpll_d4
+	 * clock.
+	 *
+	 * This code is commented out until the next patch because disabling unused clocks without
+	 * an LSP consumer breaks the UART.
+	 */
+#if 0
+	{ZX297520V3_LSP_MPLL_D5_WCLK,	"lsp_mpll_d5",	"mpll_d5",	0x7c,	  0},
+	{ZX297520V3_LSP_MPLL_D4_WCLK,	"lsp_mpll_d4",	"mpll_d4",	0x7c,	  1},
+	{ZX297520V3_LSP_MPLL_D6_WCLK,	"lsp_mpll_d6",	"mpll_d6",	0x7c,	  2},
+	{ZX297520V3_LSP_MPLL_D8_WCLK,	"lsp_mpll_d8",	"mpll_d8",	0x7c,	  3},
+	{ZX297520V3_LSP_MPLL_D12_WCLK,	"lsp_mpll_d12",	"mpll_d12",	0x7c,	  4},
+	{ZX297520V3_LSP_OSC26M_WCLK,	"lsp_osc26m",	"osc26m",	0x7c,	  5},
+	{ZX297520V3_LSP_OSC32K_WCLK,	"lsp_osc32k",	"osc32k",	0x7c,	  6},
+	{ZX297520V3_LSP_PCLK,		"lsp_pclk",	"osc26m",	0x7c,	  7},
+	{ZX297520V3_LSP_TDM_WCLK,	"lsp_tdm_wclk",	"tdm_mux",	0x7c,	  8},
+	{ZX297520V3_LSP_DPLL_D4_WCLK,	"lsp_dpll_d4",	"dpll_d4",	0x7c,	  9},
+#endif
+};
+
+static int zx297520_matrixclk_probe(struct platform_device *pdev)
+{
+	struct zx29_clk_controller *matrix;
+	struct device *dev = &pdev->dev;
+	struct clk_hw *hw;
+	unsigned int i;
+	int res;
+
+	dev_info(dev, "Registering zx297520v3 matrix clocks\n");
+
+	matrix = devm_kzalloc(dev, offsetof(struct zx29_clk_controller,
+					    resets[ZX297520V3_MATRIXRST_END]), GFP_KERNEL);
+	if (!matrix)
+		return -ENOMEM;
+
+	matrix->clocks = devm_kzalloc(dev, struct_size(matrix->clocks, hws,
+				   ZX297520V3_MATRIXCLK_END), GFP_KERNEL);
+	if (!matrix->clocks)
+		return -ENOMEM;
+	matrix->clocks->num = ZX297520V3_MATRIXCLK_END;
+
+	matrix->base = devm_platform_ioremap_resource(pdev, 0);
+	WARN_ON(!matrix->base);
+
+	/* One stray mux: The TDM mux is in matrixclk and it is passed to the LSP controller. In a
+	 * way the link gate (LSP_TDM_WCLK) could be considered a matching gate, but there is no
+	 * reset and no pclk.
+	 */
+	hw = devm_clk_hw_register_mux(dev, "tdm_mux", tdm_sel, ARRAY_SIZE(tdm_sel), 0,
+				      matrix->base + 0x50, 24, 2, 0, &reg_lock);
+
+	res = zx297520v3_composite(dev, matrix->base, matrix->clocks, matrix->resets,
+				   matrix_clocks, ARRAY_SIZE(matrix_clocks));
+	if (res)
+		return res;
+
+	res = zx297520v3_gate(dev, matrix->base, matrix->clocks,
+			      matrix_gates, ARRAY_SIZE(matrix_gates));
+	if (res)
+		return res;
+
+	for (i = 0; i < ZX297520V3_MATRIXCLK_END; i++) {
+		if (IS_ERR(matrix->clocks->hws[i])) {
+			pr_err("zx297520 clk %d: register failed with %ld\n",
+				i, PTR_ERR(matrix->clocks->hws[i]));
+			return -ENODEV;
+		}
+	}
+
+	res = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, matrix->clocks);
+	if (res)
+		return res;
+
+	matrix->resets[ZX297520V3_DMA_RESET].reg = matrix->base + 0x70;
+	matrix->resets[ZX297520V3_DMA_RESET].mask = BIT(0) | BIT(1);
+	matrix->resets[ZX297520V3_GMAC_RESET].reg = matrix->base + 0x114;
+	matrix->resets[ZX297520V3_GMAC_RESET].mask = BIT(0) | BIT(1);
+
+	matrix->rcdev.owner = THIS_MODULE;
+	matrix->rcdev.nr_resets = ZX297520V3_MATRIXRST_END;
+	matrix->rcdev.ops = &zx297520v3_rst_ops;
+	matrix->rcdev.of_node = dev->of_node;
+	return devm_reset_controller_register(dev, &matrix->rcdev);
+}
+
+static const struct of_device_id of_match_zx297520v3_matrixclk[] = {
+	{ .compatible = "zte,zx297520v3-matrixclk"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_match_zx297520v3_matrixclk);
+
+static struct platform_driver clk_zx297520v3_matrixclk = {
+	.probe = zx297520_matrixclk_probe,
+	.driver = {
+		.name = "clk-zx297520v3-matrixclk",
+		.of_match_table = of_match_zx297520v3_matrixclk,
+	},
+};
+module_platform_driver(clk_zx297520v3_matrixclk);
+
 MODULE_AUTHOR("Stefan Dösinger <stefandoesinger@gmail.com>");
 MODULE_DESCRIPTION("ZTE zx297520v3 clock driver");
 MODULE_LICENSE("GPL");

-- 
2.53.0



^ permalink raw reply related

* [PATCH RFC v2 4/4] clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets.
From: Stefan Dösinger @ 2026-05-10 21:49 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel,
	Stefan Dösinger
In-Reply-To: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com>

"LSP" is ZTE's term for this part of the SoC, I suspect it stands for
"low speed peripherals". The main UART is here, together with the flash
controller and more surplus proprietary timers.

It also has two more I2C controllers that supposedly connect to a
battery charger, SPI for displays and I2S for analog telephones. The
boards I have don't have any of these components though.

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
 drivers/clk/zte/clk-zx297520v3.c | 183 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)

diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx297520v3.c
index d4b683cb6354..05b02c8f266b 100644
--- a/drivers/clk/zte/clk-zx297520v3.c
+++ b/drivers/clk/zte/clk-zx297520v3.c
@@ -791,6 +791,189 @@ static struct platform_driver clk_zx297520v3_matrixclk = {
 };
 module_platform_driver(clk_zx297520v3_matrixclk);
 
+/* LSP clock entries have a common pattern: Bit 0 for PCLK, Bit 1 for WCLK. Bit 4 (and sometimes
+ * more) for WCLK mux.
+ *
+ * Bit 8 and 9 are reset bits. I don't know the difference between the two, but they both
+ * need to be set to deassert the reset.
+ *
+ * Bits 12-16 can be a divisor, but not all clocks have it. Some clocks have a divisor in 16-20.
+ *
+ * The ID given in this table is the first register in the device's MMIO space. ZTE's drivers
+ * usually call this a version register, but it looks more like a device identifier.
+ *
+ * It looks like the registers map to devices like this:
+ *
+ * Timer reg	function	div	dev offset(lsp + xxxx)	ID
+ * 0x0: Read-only, probably device identifier			0x00752100
+ * 0x4:		timer_l1	Y	0x1000			0x02020000
+ * 0x8:		watchdog_l2	Y	0x2000			0x02020000
+ * 0xc:		watchdog_l3	Y	0x3000			0x02020000
+ * 0x10:	i2c1		N	0x4000			0x01020000
+ * 0x14:	i2s0		Yh	0x5000			0x01030000
+ * 0x18:	always 0	N	-
+ * 0x1c:	i2s1		Yh	0x6000			0x01030000
+ * 0x20:	always 0	N	-
+ * 0x24:	qspi		N	0x7000			0x01040000
+ * 0x28:	uart1		N	0x8000			0x01060000
+ * 0x2c:	i2c2		N	0x9000			0x01020000
+ * 0x30:	spi0		Y	0xa000			0x01040000
+ * 0x34:	timer_lb	Y	0xb000			0x02020000
+ * 0x38:	timer_lc	Y	0xc000			0x02020000
+ * 0x3c:	uart2		N	0xd000			0x01060000
+ * 0x40:	watchdog_le	Y	0xe000			0x02020000
+ * 0x44:	timer_lf	Y	0xf000			0x02020000
+ * 0x48:	spi1		Y	0x10000			0x01040000
+ * 0x4c:	timer_l11	Y	0x11000			0x02020000
+ * 0x50:	tdm		Y	0x12000			0x01040000
+ *
+ * Registers 0x58, 0x5c, 0x60, 0x64, 0x68 seem to contain more controls for i2s and tdm.
+ */
+
+static const char * const timer_lsp_sel[] = {
+	"lsp_osc32k",
+	"lsp_osc26m",
+};
+
+static const char * const uart_lsp_sel[] = {
+	"lsp_osc26m",
+	"lsp_mpll_d6",
+};
+
+static const char * const i2s_lsp_sel[] = {
+	"lsp_osc26m",
+	"lsp_dpll_d4",
+	"lsp_mpll_d6",
+	/* Unknown */
+};
+
+static const char * const tdm_lsp_sel[] = {
+	"lsp_tdm_wclk",
+};
+
+static const char * const spi_lsp_sel[] = {
+	"lsp_osc26m",
+	"lsp_mpll_d4",
+	"lsp_mpll_d6",
+	/* Unknown */
+};
+
+static const char * const qspi_lsp_sel[] = {
+	"lsp_osc26m",
+	"lsp_mpll_d4",
+	"lsp_mpll_d5",
+	"lsp_mpll_d6",
+	"lsp_mpll_d8",
+	"lsp_mpll_d12",
+	"lsp_osc26m",
+	"lsp_osc26m",
+};
+
+#define LSP_CLOCK(offset, name, mux, div_shift, div_size) {\
+		ZX297520V3_##name##_RESET, ZX297520V3_##name##_WCLK, ZX297520V3_##name##_PCLK,\
+		#name, offset, 8, offset, 0, 1, "lsp_pclk", offset, 4, 4, mux, ARRAY_SIZE(mux),\
+		offset, div_shift, div_size, 0}
+
+static const struct zx297520v3_composite lsp_clocks[] =  {
+	LSP_CLOCK(0x4,	TIMER_L1,	timer_lsp_sel,	0,	0),
+	LSP_CLOCK(0x8,	WDT_L2,		timer_lsp_sel,	0,	0),
+	LSP_CLOCK(0xc,	WDT_L3,		timer_lsp_sel,	0,	0),
+	LSP_CLOCK(0x10,	I2C1,		uart_lsp_sel,	0,	0),
+	LSP_CLOCK(0x14,	I2S0,		i2s_lsp_sel,	16,	4),
+	LSP_CLOCK(0x1c,	I2S1,		i2s_lsp_sel,	16,	4),
+	LSP_CLOCK(0x24,	QSPI,		qspi_lsp_sel,	0,	0),
+	LSP_CLOCK(0x28,	UART1,		uart_lsp_sel,	0,	0),
+	LSP_CLOCK(0x2C,	I2C2,		uart_lsp_sel,	0,	0),
+	LSP_CLOCK(0x30,	SPI0,		spi_lsp_sel,	12,	4),
+	LSP_CLOCK(0x34,	TIMER_LB,	timer_lsp_sel,	12,	4),
+	LSP_CLOCK(0x38,	TIMER_LC,	timer_lsp_sel,	12,	4),
+	LSP_CLOCK(0x3c,	UART2,		uart_lsp_sel,	0,	0),
+	LSP_CLOCK(0x40,	WDT_LE,		timer_lsp_sel,	12,	4),
+	LSP_CLOCK(0x44,	TIMER_LF,	timer_lsp_sel,	12,	4),
+	LSP_CLOCK(0x48,	SPI1,		spi_lsp_sel,	12,	4),
+	LSP_CLOCK(0x4c,	TIMER_L11,	timer_lsp_sel,	12,	4),
+	LSP_CLOCK(0x50,	TDM,		tdm_lsp_sel,	16,	4),
+};
+
+#undef LSP_CLOCK
+
+static int zx297520_lspclk_probe(struct platform_device *pdev)
+{
+	static const char * const parent_names[] = { "mpll_d5", "mpll_d4", "mpll_d6", "mpll_d8",
+						     "mpll_d12", "osc26m", "osc32k", "pclk" };
+
+	struct zx29_clk_controller *lsp;
+	struct device *dev = &pdev->dev;
+	struct clk *parent;
+	unsigned int i;
+	int res;
+
+	dev_info(dev, "Registering zx297520v3 LSP clocks and resets\n");
+
+	lsp = devm_kzalloc(dev, offsetof(struct zx29_clk_controller,
+					 resets[ZX297520V3_LSPRST_END]), GFP_KERNEL);
+	if (!lsp)
+		return -ENOMEM;
+
+	lsp->clocks = devm_kzalloc(dev, struct_size(lsp->clocks, hws,
+				   ZX297520V3_LSPCLK_END), GFP_KERNEL);
+	if (!lsp->clocks)
+		return -ENOMEM;
+	lsp->clocks->num = ZX297520V3_LSPCLK_END;
+
+	lsp->base = devm_platform_ioremap_resource(pdev, 0);
+	WARN_ON(!lsp->base);
+
+	/* TODO: Technically we can disable the pclk if all LSP devices are shut down, but that
+	 * needs custom clk ops to tiptoe around a disabled LSP pclk before attempting to access
+	 * the actual clock. In normal operation it is unlikely that all LSP devices are shut down
+	 * simultaneously though as UART and NAND are located here.
+	 */
+	parent = devm_clk_get_enabled(dev, "pclk");
+	if (IS_ERR(parent)) {
+		dev_err(dev, "failed to find lsp pclk\n");
+		return PTR_ERR(parent);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(parent_names); ++i) {
+		parent = devm_clk_get(dev, parent_names[i]);
+		if (IS_ERR(parent)) {
+			dev_err(dev, "failed to find lsp %s clock\n", parent_names[i]);
+			return PTR_ERR(parent);
+		}
+	}
+
+	res = zx297520v3_composite(dev, lsp->base, lsp->clocks, lsp->resets,
+				 lsp_clocks, ARRAY_SIZE(lsp_clocks));
+	if (res)
+		return res;
+
+	res = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, lsp->clocks);
+	if (res)
+		return res;
+
+	lsp->rcdev.owner = THIS_MODULE;
+	lsp->rcdev.nr_resets = ZX297520V3_LSPRST_END;
+	lsp->rcdev.ops = &zx297520v3_rst_ops;
+	lsp->rcdev.of_node = dev->of_node;
+	return devm_reset_controller_register(dev, &lsp->rcdev);
+}
+
+static const struct of_device_id of_match_zx297520v3_lspclk[] = {
+	{ .compatible = "zte,zx297520v3-lspclk"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_match_zx297520v3_lspclk);
+
+static struct platform_driver zx297520v3_lspclk = {
+	.probe = zx297520_lspclk_probe,
+	.driver = {
+		.name = "clk-zx297520v3-lspclk",
+		.of_match_table = of_match_zx297520v3_lspclk,
+	},
+};
+module_platform_driver(zx297520v3_lspclk);
+
 MODULE_AUTHOR("Stefan Dösinger <stefandoesinger@gmail.com>");
 MODULE_DESCRIPTION("ZTE zx297520v3 clock driver");
 MODULE_LICENSE("GPL");

-- 
2.53.0



^ permalink raw reply related

* [PATCH RFC v2 2/4] clk: zte: Introduce a driver for zx297520v3 top clocks and resets.
From: Stefan Dösinger @ 2026-05-10 21:49 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel,
	Stefan Dösinger
In-Reply-To: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com>

This register space controls core devices: PLLs, the AHB bus, a lot of
timers, the USB controller, the Cortex M0 processor that boots the board
and a few other devices. For some reason the LTE coprocessor is also
partially controlled by it. The main application processor and DDR
memory are not found here though.

The register to reboot the board is also found here.

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
 drivers/clk/Kconfig              |   1 +
 drivers/clk/Makefile             |   1 +
 drivers/clk/zte/Kconfig          |  18 ++
 drivers/clk/zte/Makefile         |   2 +
 drivers/clk/zte/clk-zx297520v3.c | 583 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 605 insertions(+)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3d803b4cf5c1..971ea6daa2b6 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -539,6 +539,7 @@ source "drivers/clk/visconti/Kconfig"
 source "drivers/clk/x86/Kconfig"
 source "drivers/clk/xilinx/Kconfig"
 source "drivers/clk/zynqmp/Kconfig"
+source "drivers/clk/zte/Kconfig"
 
 # Kunit test cases
 config CLK_KUNIT_TEST
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f7bce3951a30..c164a3de2b14 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -165,5 +165,6 @@ ifeq ($(CONFIG_COMMON_CLK), y)
 obj-$(CONFIG_X86)			+= x86/
 endif
 obj-y					+= xilinx/
+obj-$(CONFIG_ARCH_ZTE)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_COMMON_CLK_ZYNQMP)         += zynqmp/
diff --git a/drivers/clk/zte/Kconfig b/drivers/clk/zte/Kconfig
new file mode 100644
index 000000000000..e7acd28832cd
--- /dev/null
+++ b/drivers/clk/zte/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ZTE Clock Drivers
+#
+menu "Clock driver for ZTE SoC"
+	depends on ARCH_ZTE || COMPILE_TEST
+
+config COMMON_CLK_ZX297520V3
+	tristate "Clock driver for ZTE zx297520v3"
+	default SOC_ZX297520V3
+	help
+	  This driver supports ZTE zx297520v3 basic clocks.
+
+	  Enable this if you want to build a kernel that is able to run on
+	  boards based on this SoC. You can safely enable multiple clock
+	  drivers. The one(s) matching the device tree will be used.
+
+endmenu
diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
new file mode 100644
index 000000000000..3751ebcba0b0
--- /dev/null
+++ b/drivers/clk/zte/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_COMMON_CLK_ZX297520V3) += clk-zx297520v3.o
diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx297520v3.c
new file mode 100644
index 000000000000..aa304dd34b7b
--- /dev/null
+++ b/drivers/clk/zte/clk-zx297520v3.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Stefan Dösinger
+ */
+#include <dt-bindings/clock/zte,zx297520v3-clk.h>
+#include <linux/reset-controller.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+static DEFINE_SPINLOCK(reg_lock);
+
+struct zx29_reset_reg {
+	void __iomem *reg;
+	u32 mask;
+};
+
+struct zx29_clk_controller {
+	void __iomem *base;
+	struct clk_hw_onecell_data *clocks;
+	struct reset_controller_dev rcdev;
+	struct zx29_reset_reg resets[];
+};
+
+static int zx297520v3_rst_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+	struct zx29_clk_controller *data = container_of(rcdev, struct zx29_clk_controller, rcdev);
+	u32 val;
+
+	val = readl(data->resets[id].reg);
+	val &= ~data->resets[id].mask;
+	writel(val, data->resets[id].reg);
+
+	return 0;
+}
+
+static int zx297520v3_rst_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+	struct zx29_clk_controller *data = container_of(rcdev, struct zx29_clk_controller, rcdev);
+	u32 val;
+
+	val = readl(data->resets[id].reg);
+	val |= data->resets[id].mask;
+	writel(val, data->resets[id].reg);
+
+	return 0;
+}
+
+static int zx297520v3_rst_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+	int ret;
+
+	ret = zx297520v3_rst_assert(rcdev, id);
+	if (ret)
+		return ret;
+
+	usleep_range(100, 100 * 2);
+
+	return zx297520v3_rst_deassert(rcdev, id);
+}
+
+static int zx297520v3_rst_status(struct reset_controller_dev *rcdev,
+			       unsigned long id)
+{
+	struct zx29_clk_controller *data = container_of(rcdev, struct zx29_clk_controller, rcdev);
+	u32 val;
+
+	val = readl(data->resets[id].reg);
+
+	return (val & data->resets[id].mask) == data->resets[id].mask;
+}
+
+const struct reset_control_ops zx297520v3_rst_ops = {
+	.assert		= zx297520v3_rst_assert,
+	.deassert	= zx297520v3_rst_deassert,
+	.reset		= zx297520v3_rst_reset,
+	.status		= zx297520v3_rst_status,
+};
+
+/* Used for gates where we don't know the parent input(s). Assume general bus clock. */
+static const char * const clk_unknown[] = {
+	"osc26m",
+};
+
+/* Used for gates where we know it is using the 26 mhz main clock. */
+static const char * const clk_main[] = {
+	"osc26m",
+};
+
+/* Top and matrix clocks are chaotic - I haven't found a consistent pattern behind their register
+ * and bit locations. Generally there are two gates (pclk, wclk), one mux, one reset and sometimes
+ * one divider, but exceptions apply. For some devices there is only a reset and some general
+ * (parent) clocks need setup. This structure plus macro handles the somewhat regular parts.
+ *
+ * There are some patterns that can be observed.
+ * mux 0x3c, div 0x48, gate 0x54
+ * mux 0x40, div 0x4c, gate 0x5c
+ * mux 0x44, div 0x50, gate 0x60
+ *
+ * For a 0 - 0xc - 0x18 pattern. Muxes from 0x3c to 0x44, dividers from 0x48 to 0x50, gates 0x54 to
+ * 0x60. The pattern is broken for timer t17 though.
+ *
+ * Gates have 4 bits per clock - bit 0 for wclk, bit 1 for pclk, bit 2 for something the ZTE kernel
+ * calls "gate" (the bits we use here are called "en"), which I don't know what it does, and bit 3
+ * seems unused. E.g. offset 0x54 accepts all bits in 0xF77F7F7F - suggesting RTC, I2C0 have an
+ * extra gate bit.
+ */
+struct zx297520v3_composite {
+	u32 reset_id, wclk_id, pclk_id;
+	const char *name;
+	u32 reset_reg, reset_shift;
+	u32 gate_reg, wclk_gate_shift, pclk_gate_shift;
+	const char *pclk_parent;
+	u32 mux_reg, mux_shift, mux_size;
+	const char * const *mux_sel;
+	u32 mux_sel_count;
+	u32 div_reg, div_shift, div_size;
+	u32 flags;
+};
+
+struct zx297520v3_gate {
+	u32 id;
+	const char *name, *parent;
+	u32 reg, shift;
+};
+
+#define _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift, pclk_parent,\
+		mux_reg, mux_shift, mux_size, mux_sel,\
+		div_reg, div_shift, div_size, flags) \
+		{ZX297520V3_##name##_RESET, ZX297520V3_##name##_WCLK, ZX297520V3_##name##_PCLK,\
+		 #name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift, pclk_parent,\
+		 mux_reg, mux_shift, mux_size, mux_sel, ARRAY_SIZE(mux_sel),\
+		 div_reg, div_shift, div_size, flags}
+
+#define ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift,\
+	       mux_reg, mux_shift, mux_size, mux_sel,\
+	       div_reg, div_shift, div_size) \
+	       _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift, "osc26m",\
+		mux_reg, mux_shift, mux_size, mux_sel,\
+		div_reg, div_shift, div_size, 0)
+
+#define ZX_CLK_CRIT(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift,\
+	       mux_reg, mux_shift, mux_size, mux_sel,\
+	       div_reg, div_shift, div_size) \
+	       _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift, "osc26m",\
+		mux_reg, mux_shift, mux_size, mux_sel,\
+		div_reg, div_shift, div_size, CLK_IS_CRITICAL)
+
+/* The default mpll settings multiply the 26 MHz reference clock times 24. A mux selection of 26 MHz
+ * could mean using the 26 MHz oscillator directly, or passing it through the PLL and divide by 24.
+ *
+ * If a UART is set to mpl_d6 (default 104 MHz), changing the mpll multipliers does affect UART
+ * timing as it should. This does not happen when the UART is set to 26 MHz input or timers that
+ * read 26 MHz input. This suggests 26 MHz clocks use the reference clock directly.
+ */
+static const char * const ahb_sel[] = {
+	"osc26m",
+	"mpll_d6",	/* 104 mhz */
+	"mpll_d8",	/* 78 mhz */
+	"mpll_d8",	/* 78 mhz */
+};
+
+static const char * const timer_top_sel[] = {
+	"osc32k",
+	"osc26m",
+};
+
+static const char * const uart_top_sel[] = {
+	"osc26m",
+	"mpll_d6",	/* 104 mhz */
+};
+
+static const char * const m0_sel[] = {
+	"osc26m",
+	"mpll_d6",	/* 104 mhz */
+	"mpll_d8",	/* 78 mhz */
+	"osc32k",	/* Yes, tested. It is SLLLLOOOOOWWW. */
+};
+
+static const struct zx297520v3_composite top_clocks[] = {
+	/*    (NAME,       RESET,     GATE,         MUX,                        DIV        ), */
+
+	/* AHB: Don't turn this one off. The clock mux works and impact can be tested e.g. with
+	 * iperf speed testing of the USB network connection. Values 2 and 3 give the same speed.
+	 */
+	ZX_CLK_CRIT(AHB,   0x70,  0,  0x54, 12, 13, 0x3c,  4, 2, ahb_sel,       0,     0, 0),
+
+	/* Pinmux (AON, TOP, IOCFG but not PDCFG). Critical as well until we have a driver that
+	 * consumes it. I don't think we'll realistically shut this off ever.
+	 *
+	 * Setting either bit 0 or 1 in register 0x58 makes the device work.
+	 */
+	ZX_CLK_CRIT(PMM,   0x74,  0,  0x58,  0,  1, 0x00,  0, 0, clk_unknown,   0,     0, 0),
+
+	/* Timers. We don't use any of them, just shut them off. The timers are named and sorted
+	 * by the IO address of the main timer controls. Some of the controls are documented in
+	 * ZTE's kernel. Some I found by trial and error.
+	 *
+	 * Timer T17 is used by the ZSP firmware. The rproc driver will enable them as needed.
+	 */
+	ZX_CLK(TIMER_T08,  0x78,   4, 0x5c,  8,  9, 0x40,  1, 1, timer_top_sel, 0x4c,   8, 4),
+	ZX_CLK(TIMER_T09,  0x78,   2, 0x5c,  4,  5, 0x40,  0, 1, timer_top_sel, 0x4c,   0, 4),
+	ZX_CLK(TIMER_T12,  0x74,   6, 0x54,  4,  5, 0x3c,  0, 1, timer_top_sel, 0x48,   0, 4),
+	ZX_CLK(TIMER_T13,  0x7c,   0, 0x60,  0,  1, 0x44,  0, 1, timer_top_sel, 0x50,   0, 4),
+	ZX_CLK(TIMER_T14,  0x7c,   2, 0x60,  4,  5, 0x44,  1, 1, timer_top_sel, 0x50,   4, 4),
+	ZX_CLK(TIMER_T15,  0x74,  10, 0x54, 20, 21, 0x3c,  3, 1, timer_top_sel, 0x48,   4, 4),
+	ZX_CLK(TIMER_T16,  0x7c,   4, 0x60,  8,  9, 0x44,  2, 1, timer_top_sel, 0x50,   8, 4),
+	ZX_CLK(TIMER_T17,  0x12c,  0, 0x128, 0,  1, 0x120, 0, 1, timer_top_sel, 0x124,  0, 4),
+
+	/* This watchdog is set up by the bootloader and in normal operation the m0 firmware will
+	 * feed the dog. The m0 firmware in turn wants to be fed in its own way. Since we normally
+	 * don't run any m0 firmware we shut it off by default and expose it to userspace via the
+	 * watchdog driver.
+	 */
+	ZX_CLK(WDT_T18,    0x74,  12, 0x54, 24, 25, 0x3c,  6, 1, timer_top_sel, 0x48,   8, 4),
+
+	ZX_CLK(I2C0,       0x74,   8, 0x54,  8,  9, 0x3c,  1, 1, uart_top_sel,  0,      0, 0),
+	ZX_CLK(UART0,      0x78,   6, 0x5c, 12, 13, 0x40,  2, 1, uart_top_sel,  0,      0, 0),
+
+	/* How does this RTC work? I don't know, the ZTE kernel does not talk to it. It has an
+	 * external RTC connected to I2C0.
+	 */
+	ZX_CLK(RTC,        0x74,   4, 0x54,  0,  1, 0x00,  0, 0, timer_top_sel, 0,      0, 0),
+
+	/* This doesn't see to be talking to the physical SIM card. I can turn it off on the ZTE
+	 * firmware without breaking LTE, and the "uicc" IRQ count keeps climbing. I think this is
+	 * a eSim-like chip that can be provisioned with data at runtime, but I have no idea how to
+	 * do it.
+	 */
+	ZX_CLK(USIM1,      0x74,  14, 0x54, 28, 29, 0x00,  0, 0, clk_main,      0x48,  12, 1),
+
+	/*    (NAME,       RESET,     GATE,         MUX,                        DIV         ), */
+};
+
+/* Stand-alone topclk gates. */
+static const struct zx297520v3_gate top_gates[] = {
+	{ZX297520V3_USB_24M,		"usb_24m",	"mpll_d26",	0x6c,	  3},
+	{ZX297520V3_USB_AHB,		"usb_ahb",	"AHB_wclk",	0x6c,	  4},
+	/* LTE: gate only as far as I can see. I looked for resets and did not find any. There may
+	 * be mux/div, but without understanding the behavior of this hardware it is impossible to
+	 * tell. They are sorted by physical MMIO address of the devices, which happens to be the
+	 * inverse order of the bits.
+	 *
+	 * I don't know what "LPM", "TD" and "W" mean. I copied them from ZTE's names.
+	 */
+	{ZX297520V3_LPM_GSM_WCLK,	"LPM_GSM_wclk",	clk_unknown[0],	0x58,	10},
+	{ZX297520V3_LPM_GSM_PCLK,	"LPM_GSM_pclk",	clk_unknown[0],	0x58,	11},
+	{ZX297520V3_LPM_LTE_WCLK,	"LPM_LTE_wclk",	clk_unknown[0],	0x58,	 8},
+	{ZX297520V3_LPM_LTE_PCLK,	"LPM_LTE_pclk",	clk_unknown[0],	0x58,	 9},
+	{ZX297520V3_LPM_TD_WCLK,	"LPM_TD_wclk",	clk_unknown[0],	0x58,	 6},
+	{ZX297520V3_LPM_TD_PCLK,	"LPM_TD_pclk",	clk_unknown[0],	0x58,	 7},
+	{ZX297520V3_LPM_W_WCLK,		"LPM_W_wclk",	clk_unknown[0],	0x58,	 4},
+	{ZX297520V3_LPM_W_PCLK,		"LPM_W_pclk",	clk_unknown[0],	0x58,	 5},
+	/* There are PCLKs for BROM/SRAM2 in 0x54, bit 16 and SRAM1 in 0x54, bit 18. Turning them
+	 * off locks up the Cortex M0 coproc. Not added to the kernel until a way is found to
+	 * recover the Cortex M0 or evidence of power savings.
+	 */
+};
+
+static int zx297520v3_pll(struct device *dev, void __iomem *base, const char *name,
+			  struct clk *parent)
+{
+	/* These are the fractionals of the PLLs I have seen. There should be a better way to
+	 * generate them than hardcode the list.
+	 */
+	static const unsigned int pll_fract[] = {2, 3, 4, 5, 6, 8, 12, 26};
+
+	unsigned long ref, refdiv, fbdiv, vco, postdiv1, postdiv2, freq;
+	struct clk_hw *hw;
+	char plldiv[16];
+	unsigned int i;
+	u32 val;
+
+	/* PLLs are configured by the boot rom, we only read their settings to know how the rate
+	 * of the derived clocks. ZTE's sources explain the PLL register contents only in a .cmm
+	 * file (A Lauterback TRACE32 script). When calculating the frequencies with the default
+	 * PLL configuration the results match the fixed rate clocks from their clock driver.
+	 *
+	 * The 26mhz and 32khz clocks can be easily observed with the timers. The 104mhz output
+	 * can be observed through the UART. All others can only be indirectly observed by e.g.
+	 * comparing the CPU speed at 26mhz and 624mhz.
+	 *
+	 * The contents of the PLL registers is as follows:
+	 *
+	 * Bit 31: PLL Locked
+	 * Bit 30: PLL disable bit (0 = PLL enabled, 1 = PLL disabled)
+	 * Bits 29:25: Unknown. Could be a parent mux
+	 * Bits 24:18: refdiv
+	 * Bits 17:6: fbdiv
+	 * Bits 5:3: post vco divider 1
+	 * Bits 2:0: post vco divider 2
+	 *
+	 * There is a second register following immediately afterwards that is supposed to have
+	 * another value that gets added to fbdiv, but it doesn't seem to make a difference in my
+	 * testing and it is always 0 in the preconfigured values.
+	 */
+	ref = clk_get_rate(parent);
+	val = readl(base);
+
+	refdiv = (val & GENMASK(24, 18)) >> 18;
+	fbdiv = (val & GENMASK(17, 6)) >> 6;
+	postdiv1 = (val & GENMASK(5, 3)) >> 3;
+	postdiv2 = (val & GENMASK(2, 0));
+	dev_dbg(dev, "%s: reference clock %lu HZ, PLL setting 0x%08x\n", name, ref, val);
+
+	if (!refdiv || !postdiv1 || !postdiv2)
+		return -EINVAL;
+
+	vco = (ref / refdiv) * fbdiv;
+	freq = vco / postdiv1 / postdiv2;
+	dev_dbg(dev, "%s: %lu MHZ\n", name, freq / 1000000);
+
+	hw = devm_clk_hw_register_fixed_factor(dev, name, __clk_get_name(parent), 0, fbdiv,
+					       refdiv * postdiv1 * postdiv2);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+
+	for (i = 0; i < ARRAY_SIZE(pll_fract); ++i) {
+		sprintf(plldiv, "%s_d%u", name, pll_fract[i]);
+		hw = devm_clk_hw_register_fixed_factor(dev, plldiv, name, 0, 1, pll_fract[i]);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+		dev_dbg(dev, "%s: %lu hz\n", clk_hw_get_name(hw), clk_hw_get_rate(hw));
+	}
+
+	return 0;
+}
+
+static int zx297520v3_composite(struct device *dev, void __iomem *base,
+				struct clk_hw_onecell_data *clocks, struct zx29_reset_reg *resets,
+				const struct zx297520v3_composite *input, size_t size)
+{
+	char pclk_name[32], wclk_name[32], mux_name[32], div_name[32];
+	const char *wclk_parent, *div_parent;
+	struct clk_hw *hw;
+	unsigned int i;
+
+	for (i = 0; i < size; ++i) {
+		strscpy(wclk_name, input[i].name, ARRAY_SIZE(wclk_name));
+		strcat(wclk_name, "_wclk");
+		strscpy(pclk_name, input[i].name, ARRAY_SIZE(pclk_name));
+		strcat(pclk_name, "_pclk");
+		strscpy(mux_name, input[i].name, ARRAY_SIZE(mux_name));
+		strcat(mux_name, "_mux");
+		strscpy(div_name, input[i].name, ARRAY_SIZE(div_name));
+		strcat(div_name, "_div");
+
+		resets[input[i].reset_id].reg = base + input[i].reset_reg;
+		resets[input[i].reset_id].mask = BIT(input[i].reset_shift) +
+						 BIT(input[i].reset_shift + 1);
+
+		if (input[i].div_size)
+			wclk_parent = div_name;
+		else if (input[i].mux_size)
+			wclk_parent = mux_name;
+		else
+			wclk_parent = input[i].mux_sel[0];
+
+		hw = devm_clk_hw_register_gate(dev, pclk_name, input[i].pclk_parent, input[i].flags,
+					       base + input[i].gate_reg, input[i].pclk_gate_shift,
+					       0, &reg_lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+		clocks->hws[input[i].pclk_id] = hw;
+
+		if (input[i].mux_size) {
+			hw = devm_clk_hw_register_mux(dev, mux_name, input[i].mux_sel,
+						      input[i].mux_sel_count, 0,
+						      base + input[i].mux_reg,
+						      input[i].mux_shift, input[i].mux_size, 0,
+						      &reg_lock);
+			if (IS_ERR(hw))
+				return PTR_ERR(hw);
+			div_parent = mux_name;
+		} else {
+			div_parent = input[i].mux_sel[0];
+		}
+
+		hw = devm_clk_hw_register_gate(dev, wclk_name, wclk_parent,
+					       input[i].flags | CLK_SET_RATE_PARENT,
+					       base + input[i].gate_reg, input[i].wclk_gate_shift,
+					       0, &reg_lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+		clocks->hws[input[i].wclk_id] = hw;
+
+		if (!input[i].div_size)
+			continue;
+
+		hw = devm_clk_hw_register_divider(dev, div_name, div_parent, CLK_SET_RATE_PARENT,
+						  base + input[i].div_reg, input[i].div_shift,
+						  input[i].div_size, 0, &reg_lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+	}
+
+	return 0;
+}
+
+static int zx297520v3_gate(struct device *dev, void __iomem *base,
+			   struct clk_hw_onecell_data *clocks,
+			   const struct zx297520v3_gate *input, size_t size)
+{
+	struct clk_hw *hw;
+	unsigned int i;
+
+	for (i = 0; i < size; ++i) {
+		hw = devm_clk_hw_register_gate(dev, input[i].name, input[i].parent,
+					       CLK_SET_RATE_PARENT, base + input[i].reg,
+					       input[i].shift, 0, &reg_lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+		clocks->hws[input[i].id] = hw;
+	}
+
+	return 0;
+}
+
+static int zx_restart_handle(struct sys_off_data *data)
+{
+	struct zx29_clk_controller *top = data->cb_data;
+
+	writel(1, top->base);
+	mdelay(1000);
+
+	pr_emerg("Unable to restart system\n");
+	return NOTIFY_DONE;
+}
+
+static int zx297520_topclk_probe(struct platform_device *pdev)
+{
+	struct zx29_clk_controller *top;
+	struct device *dev = &pdev->dev;
+	struct clk_hw *hw;
+	struct clk *clk;
+	int res;
+
+	dev_info(dev, "Registering zx297520v3 top clocks and resets\n");
+	top = devm_kzalloc(dev, offsetof(struct zx29_clk_controller,
+					 resets[ZX297520V3_TOPRST_END]), GFP_KERNEL);
+	if (!top)
+		return -ENOMEM;
+
+	top->clocks = devm_kzalloc(dev, struct_size(top->clocks, hws,
+				   ZX297520V3_TOPCLK_END), GFP_KERNEL);
+	if (!top->clocks)
+		return -ENOMEM;
+	top->clocks->num = ZX297520V3_TOPCLK_END;
+
+	top->base = devm_platform_ioremap_resource(pdev, 0);
+	WARN_ON(!top->base);
+
+	/* Offset 0x0 is the global board reset
+	 * Offset 0x4 gives some static boot information - raw NAND or SPI NAND
+	 */
+
+	clk = devm_clk_get_prepared(dev, "osc32k");
+	if (IS_ERR(clk)) {
+		dev_err(dev, "32 KHz input clock not found 1\n");
+		return PTR_ERR(clk);
+	}
+
+	clk = devm_clk_get_prepared(dev, "osc26m");
+	if (IS_ERR(clk)) {
+		dev_err(dev, "26 MHz input clock not found\n");
+		return PTR_ERR(clk);
+	}
+
+	/* Default setting: 0x48040c11. 624/312/156... */
+	res = zx297520v3_pll(dev, top->base + 0x8, "mpll", clk);
+	if (res)
+		return res;
+
+	/* There is a PLL at 0x10 called "upll" in ZTE's code, but I don't see any documented
+	 * consumers. Default setting 0x48347811. 480/240/160 MHz.
+	 */
+
+	/* Default value 0x4834902d. Feeds dpll. 46.08 MHz */
+	res = zx297520v3_pll(dev, top->base + 0x100, "unknownpll", clk);
+	if (res)
+		return res;
+
+	/* The documentation says 491.52 MHz and measurement with the LSP TDM device supports this.
+	 * The default value is 0x480C2011. To get to 491.52 with these settings it needs a 23.04
+	 * MHz reference clock, which matches unknownpll_d2. If unknownpll is disabled, dpll loses
+	 * its lock.
+	 *
+	 * The proprietary LTE driver or coproc enables and disables it. TDM and I2S can use it.
+	 *
+	 * FIXME: Isn't there a nicer way to get the struct clk for unknownpll_d2? I don't want to
+	 * return all generated clocks from zx297520v3_pll or store them in the controller because
+	 * I need one of them here. I could always pass the parent by name though.
+	 */
+	res = zx297520v3_pll(dev, top->base + 0x18, "dpll", __clk_lookup("unknownpll_d2"));
+	if (res)
+		return res;
+	res = zx297520v3_pll(dev, top->base + 0x110, "gpll", clk);
+	if (res)
+		return res;
+
+	res = zx297520v3_composite(dev, top->base, top->clocks, top->resets,
+				 top_clocks, ARRAY_SIZE(top_clocks));
+	if (res)
+		return res;
+
+	res = zx297520v3_gate(dev, top->base, top->clocks, top_gates, ARRAY_SIZE(top_gates));
+	if (res)
+		return res;
+
+	/* The Cortex M0 coprocessor. It is responsible for booting the board and runs some power
+	 * management helper code on the stock firmware, but isn't critical. We can run custom code
+	 * on it but currently do not.  These bits control the speed and the values are mentioned in
+	 * ZTE's uboot. It isn't clear to me if this is directly responsible for the m0 clock, or
+	 * if it is the input to another clock. I also haven't found a gate that shuts the m0 off
+	 * and allows restarting. There don't seem to be resets either.
+	 *
+	 * Also note the comment about SRAM1 and SRAM2 PCLKs. They can be turned off to crash the
+	 * M0 by feeding it garbage instructions.
+	 */
+	hw = devm_clk_hw_register_mux(dev, "m0_wclk", m0_sel, ARRAY_SIZE(m0_sel),
+				      0, top->base + 0x38, 0, 2, 0, &reg_lock);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+	top->clocks->hws[ZX297520V3_M0_WCLK] = hw;
+
+	of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, top->clocks);
+
+	res = devm_register_restart_handler(dev, zx_restart_handle, top);
+	if (res)
+		dev_err(dev, "can't register restart handler (res=%d)\n", res);
+
+	/* Stray reset bits follow.
+	 *
+	 * I haven't found any clocks for GPIO. It probably wouldn't make much
+	 * sense anyway. Only one bit per controller.
+	 */
+	top->resets[ZX297520V3_GPIO8_RESET].reg = top->base + 0x74;
+	top->resets[ZX297520V3_GPIO8_RESET].mask = BIT(2);
+	top->resets[ZX297520V3_GPIO_RESET].reg = top->base + 0x74;
+	top->resets[ZX297520V3_GPIO_RESET].mask = BIT(3);
+
+	/* USB reset. This is slightly special because it needs to wait for a ready bit after
+	 * deasserting.
+	 *
+	 * FIXME: Actually implement this waiting.
+	 */
+	top->resets[ZX297520V3_USB_RESET].reg = top->base + 0x80;
+	top->resets[ZX297520V3_USB_RESET].mask = BIT(3) | BIT(4) | BIT(5);
+
+	/* This bit is set by ZTE's cpko.ko blob, it looks like a reset bit for the LTE DSP
+	 * coprocessor. Clocks for it are in matrixclk.
+	 */
+	top->resets[ZX297520V3_ZSP_RESET].reg = top->base + 0x13c;
+	top->resets[ZX297520V3_ZSP_RESET].mask = BIT(0);
+
+	top->rcdev.owner = THIS_MODULE;
+	top->rcdev.nr_resets = ZX297520V3_TOPRST_END;
+	top->rcdev.ops = &zx297520v3_rst_ops;
+	top->rcdev.of_node = dev->of_node;
+	return devm_reset_controller_register(dev, &top->rcdev);
+}
+
+static const struct of_device_id of_match_zx297520v3_topclk[] = {
+	{ .compatible = "zte,zx297520v3-topclk"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_match_zx297520v3_topclk);
+
+static struct platform_driver clk_zx297520v3_topclk = {
+	.probe = zx297520_topclk_probe,
+	.driver = {
+		.name = "clk-zx297520v3-topclk",
+		.of_match_table = of_match_zx297520v3_topclk,
+	},
+};
+module_platform_driver(clk_zx297520v3_topclk);
+
+MODULE_AUTHOR("Stefan Dösinger <stefandoesinger@gmail.com>");
+MODULE_DESCRIPTION("ZTE zx297520v3 clock driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



^ permalink raw reply related

* [PATCH RFC v2 0/4] ZTE zx297520v3 clock bindings and driver
From: Stefan Dösinger @ 2026-05-10 21:49 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel,
	Stefan Dösinger

Hi,

I am sending my clk driver for zx297520v3 boards for comment. The patch
series sits on top of my previously sent patches for very basic support
for this board [0]. The first caveat is that b4 dependencies don't seem
to do what I expected, so I have backed out the MAINTAINERS and DTSI
changes from this RFC submission. It should still be good enough to
review the actual clock driver and bindings.

I have pretty good understanding of the clock hardware, but a number of 
questions about the prefered way of implementing such a driver. Either 
the clock framework and its documentation are constantly evolving or I 
am just bad at discovering things:

1) The clock controls on this board consist of 3 controllers that I 
called - following ZTE's nomenclature - top, matrix and LSP. The top 
controller is fed by two oscillators and generates other frequencies 
with PLLs. I have expressed this in the device tree: The clk driver 
binding requires these two clocks.

The matrix controller feeds 10 clocks to the LSP controller and can turn 
them on and off on a per-clock bases. The DT bindings express this as 
well - the LSP binding expects those 10 clock handles.

There are no explicit HW controls for the top->matrix link, but the 
muxes and gates in the matrix controller use the PLL outputs as parents. 
I have not expressed that in the binding as it would require 25 or so 
clock handles. Instead, the matrix driver quietly expects the right 
parent clock names to be registered by the top driver. (OTOH having 60 
or so clock parameters didn't stop st,stm32mp21-rcc)

The entire LSP part (I guess the name stands for low speed peripherals) 
looks like it might be reused on different baords. The top and matrix 
part are very interdependent.

2) The clk-reset interaction: Both clocks and resets are in the same IO 
space, sometimes in the same registers. I see a number of clk drivers 
that register a reset control. I noticed Yu-Chun Lin's RTD1625 clock 
submission added an aux device and placed the reset code in 
drivers/reset instead. Is there a preference for either way or any 
guideline of which way to use in which circumstances?

3) Unused clocks: I looked at recently introduced clk drivers 
(mediatek,mt8196-clock.h, sun55i-a523-mcu-ccu.h) and they do add all a 
lot of clocks that do not have an active consumer - which in a way means 
unused ABI. Please let me know if you prefer to add clocks one by one as 
their consumers are added.

That said, there are a lot of clocks that I want to define for the sole 
purpose of shutting them off. The boot loader leaves pretty much every 
device enabled, including proprietary timers that I don't even plan to 
write a driver for. Registering their clocks in the kernel will allow 
the kernel to shut them off, so they aren't entirely unused.

4) I took some naming from the old zx2967 code. In particular, each 
device has two clocks: "WCLK" for the device operation and "PCLK" for 
register access. Are there more standard names for them? Likewise I took 
some device names from ZTE's downstream sources and I am open to better 
suggestions.

5) I took care to test unbinding and rebinding my clock driver to the 
hardware. It works at least as no clock consumers are defined. It seems 
mutually exclusive though with declaring static initialization data as 
__initdata, as it will be gone after the first time the driver is bound. 
I also don't see how unbinding and rebinding will be tested later on 
when core peripherals are clock consumers.

6) Clock name string matching vs passing pointers: A presentation by
Chen-Yu Tsai from 2024 [1] gave me the impression that the kernel is
trying to move away from building the clock tree with string matching. I 
see APIs for passing a struct clk_hw * as parent instead of strings, but 
that makes it more difficult to build static initialization tables. I 
think the static tables make the code a lot easier to read and I prefer 
that over boot time performance.

I think the list of clocks in my driver is fairly complete; It is 
certainly a lot better than what the downstream ZTE drivers have. I 
deduced a lot of it by trial and error. I am sure there are some clocks 
missing that will need to be added to the binding later. Afaiu adding 
clocks is not an issue, but removing or reordering them is an ABI break.

0: https://lore.kernel.org/linux-arm-kernel/20260506-send-v8-0-f1bdf3243b34@gmail.com/
1: https://www.youtube.com/watch?v=d1VIAnVb3hI

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
v2: Fix build issues introduced by checkpatch.pl fixes that I didn't 
spot earlier.

---
Stefan Dösinger (4):
      dt-bindings: clk: zte: Add zx297520v3 clock and reset bindings.
      clk: zte: Introduce a driver for zx297520v3 top clocks and resets.
      clk: zte: Introduce a driver for zx297520v3 matrix clocks and resets.
      clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets.

 .../bindings/clock/zte,zx297520v3-clk.yaml         | 173 ++++
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/zte/Kconfig                            |  18 +
 drivers/clk/zte/Makefile                           |   2 +
 drivers/clk/zte/clk-zx297520v3.c                   | 979 +++++++++++++++++++++
 include/dt-bindings/clock/zte,zx297520v3-clk.h     | 179 ++++
 7 files changed, 1353 insertions(+)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260510-zx29clk-2e4d39e3128c
prerequisite-change-id: 20260416-send-5c08e095e5c9:v8
prerequisite-patch-id: 94a9b9f889829e5c1899cb1be89b7ee9899b2626
prerequisite-patch-id: 8a849ffe79ba35ef560250fb38587487cc5009fb
prerequisite-patch-id: ef282d0a261dc1097f05057cd43e9e75ae52d92b
prerequisite-patch-id: 5d615c9f855fca6da461168f45a5670a3c3cde81
prerequisite-patch-id: 980a7e66a1031cdcc244a5e461220d68c72309a5
prerequisite-patch-id: 45b6fc60cee81a793cd69e704bf098f1a68769a9

Best regards,
-- 
Stefan Dösinger <stefandoesinger@gmail.com>



^ permalink raw reply

* Re: [PATCH RFC 4/4] clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets.
From: Stefan Dösinger @ 2026-05-10 21:44 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel
  Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <20260510-zx29clk-v1-4-e1bacfffe967@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 486 bytes --]

Am Sonntag, 10. Mai 2026, 22:01:03 Ostafrikanische Zeit schrieben Sie:
> +       static const char * const *parent_names[] = { "mpll_d5", "mpll_d4",
> "mpll_d6", "mpll_d8", +                                                    
> "mpll_d12", "osc26m", "osc32k", "pclk" };

So this obviously doesn't build, and I didn't spot it because the git branch I 
sent this from didn't have the Kconfig options introduced by my other patch 
series. I'll resend, my apologies for the quick v2 spam.

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply

* Re: (subset) [PATCH 1/3] KVM: arm64: vgic: Fix IIDR revision field extracted from wrong value
From: David Woodhouse @ 2026-05-10 21:28 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini,
	Shuah Khan, Raghavendra Rao Ananta, Eric Auger, Kees Cook,
	Arnd Bergmann, Nathan Chancellor, linux-arm-kernel, kvmarm,
	linux-kernel, kvm, linux-kselftest
In-Reply-To: <106addd3f78918a9a584c43c181a9609aef1ceca.camel@infradead.org>

[-- Attachment #1: Type: text/plain, Size: 1468 bytes --]

On Fri, 2026-04-24 at 13:24 +0100, David Woodhouse wrote:
> On Fri, 2026-04-24 at 12:07 +0100, Marc Zyngier wrote:
> > On Tue, 07 Apr 2026 21:27:02 +0100, David Woodhouse wrote:
> > > The uaccess write handlers for GICD_IIDR in both GICv2 and GICv3
> > > extract the revision field from 'reg' (the current IIDR value read back
> > > from the emulated distributor) instead of 'val' (the value userspace is
> > > trying to write). This means userspace can never actually change the
> > > implementation revision — the extracted value is always the current one.
> > > 
> > > Fix the FIELD_GET to use 'val' so that userspace can select a different
> > > revision for migration compatibility.
> > > 
> > > [...]
> > 
> > Applied to fixes, thanks!
> > 
> > [1/3] KVM: arm64: vgic: Fix IIDR revision field extracted from wrong value
> >       commit: a0e6ae45af17e8b27958830595799c702ffbab8d
> 
> There was a v2 of this series which also cleaned up the weird
> inconsistency of the IIDR value with the actual behaviour, and which
> fixed the fact that it's currently not possible to maintain guest
> compatibility when upgrading from a pre-d53c2c29ae0d kernel to a new
> one — despite the fact that that kind of compatibility is *precisely*
> what the revision field in the IIDR is designed for.
> 
> https://lore.kernel.org/all/20260408113256.2095505-1-dwmw2@infradead.org/

Is there a reason the rest of these fixes didn't make 7.1?


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox