* Re: [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
From: sashiko-bot @ 2026-07-20 10:28 UTC (permalink / raw)
To: Joey Lu
Cc: robh, neil.armstrong, conor+dt, vkoul, olteanv, linux-phy,
devicetree
In-Reply-To: <20260720102222.251179-2-a0987203069@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `#phy-cells` property uses an `enum` constraint instead of a `const` constraint, violating Device Tree Bindings guidelines.
--
--- Patch [1]: [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG ---
commit 676c30318df404b9fb25b710326b359f41a5cf10
Author: Joey Lu <a0987203069@gmail.com>
dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
This commit updates the Nuvoton MA35D1 USB2 PHY binding to support dual ports
managed by the same hardware block. It modifies the #phy-cells property to
allow values of 0 or 1 for port selection, while preserving backward
compatibility. It also introduces optional properties for resistor calibration
trim codes and over-current detect polarity.
> diff --git a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
> index fff858c909a06..1bd22dd0e5fc5 100644
> --- a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
[ ... ]
> @@ -15,7 +27,12 @@ properties:
> - nuvoton,ma35d1-usb2-phy
>
> "#phy-cells":
> - const: 0
> + enum: [0, 1]
[Severity: Low]
Does this violate the device tree binding subsystem rules for cell-count
properties? The guidelines specify that each cell-count property must have
a const constraint matching the hardware, rather than an enum constraint.
If the hardware requires different configurations depending on the variant
or backward compatibility, should this be enforced using conditional if-then
blocks instead of allowing either value unconditionally?
> + description:
> + When 0, the node exposes PHY0 only and consumers reference it
> + with no cell argument. When 1, the single cell selects the port,
> + with 0 for the OTG port (USB0, shared with DWC2 gadget controller)
> + and 1 for the host-only port (USB1).
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720102222.251179-1-a0987203069@gmail.com?part=1
^ permalink raw reply
* [PATCH v4 3/3] phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
PHY0 and PHY1 use the same power-on/reset sequence in USBPMISCR, with
PHY1 control bits shifted 16 positions relative to PHY0. A separate
driver for PHY1 would duplicate this logic, so the existing driver is
extended to manage both ports.
The original driver polled only DEVCKSTB after releasing PHY0 from
reset. When USB0 operates in host mode (USB ID pin floating or tied
high) only HSTCKSTB and CK12MSTB assert; DEVCKSTB never sets. Polling
exclusively for DEVCKSTB in host mode causes a 1 ms timeout on every
phy_init() call from the EHCI driver. The init callback is changed to
accept either host-mode or device-mode clock stability, whichever
asserts first.
The power_on and power_off callbacks are replaced by a single init
callback that handles PHY reset and clock-stable polling, because
there is no PHY-specific clock gate on MA35D1; the PHY analog block
derives its reference from the HXT crystal.
A read-only USB role switch is registered for PHY0 to expose the USB0
role to userspace via the standard role-switch sysfs interface. The
.set callback returns -EOPNOTSUPP because the hardware mux is fully
automatic with no software override path. When CONFIG_USB_ROLE_SWITCH
is not enabled, the registration returns -ENODEV and the driver skips
the role switch gracefully without failing probe.
Two new optional device-tree properties are implemented:
- nuvoton,rcalcode: writes per-port 4-bit resistor calibration trim
codes to the RCALCODE field in USBPMISCR.
- nuvoton,oc-active-high: sets the UHOVRCURH bit in MISCFCR0 to treat
the over-current detect input as active-high.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
---
drivers/phy/nuvoton/phy-ma35d1-usb2.c | 284 +++++++++++++++++++-------
1 file changed, 210 insertions(+), 74 deletions(-)
diff --git a/drivers/phy/nuvoton/phy-ma35d1-usb2.c b/drivers/phy/nuvoton/phy-ma35d1-usb2.c
index 9a459b700ed4..2c60be0ee50f 100644
--- a/drivers/phy/nuvoton/phy-ma35d1-usb2.c
+++ b/drivers/phy/nuvoton/phy-ma35d1-usb2.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2024 Nuvoton Technology Corp.
+ * Nuvoton MA35D1 USB 2.0 PHY driver
+ *
+ * Supports PHY0 (USB0 OTG port, shared between DWC2 gadget and EHCI0/OHCI0)
+ * and PHY1 (USB1 host-only port, used by EHCI1/OHCI1). The hardware mux on
+ * PHY0 switches automatically via the USB ID pin.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -13,131 +18,262 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/usb/role.h>
-/* USB PHY Miscellaneous Control Register */
-#define MA35_SYS_REG_USBPMISCR 0x60
-#define PHY0POR BIT(0) /* PHY Power-On Reset Control Bit */
-#define PHY0SUSPEND BIT(1) /* PHY Suspend; 0: suspend, 1: operaion */
-#define PHY0COMN BIT(2) /* PHY Common Block Power-Down Control */
-#define PHY0DEVCKSTB BIT(10) /* PHY 60 MHz UTMI clock stable bit */
+#define MA35_SYS_PWRONOTP 0x04
+#define PWRONOTP_USBP0ID BIT(16)
+
+#define MA35_SYS_USBPMISCR 0x60
+#define USBPMISCR_PHY_POR(n) BIT(0 + (n) * 16)
+#define USBPMISCR_PHY_SUSPEND(n) BIT(1 + (n) * 16)
+#define USBPMISCR_PHY_COMN(n) BIT(2 + (n) * 16)
+#define USBPMISCR_PHY_HSTCKSTB(n) BIT(8 + (n) * 16)
+#define USBPMISCR_PHY_CK12MSTB(n) BIT(9 + (n) * 16)
+#define USBPMISCR_PHY_DEVCKSTB(n) BIT(10 + (n) * 16)
+/* Mask for control bits (POR, SUSPEND, COMN) */
+#define USBPMISCR_PHY_CTL_MASK(n) (0x7u << ((n) * 16))
+/* Host-mode ready */
+#define USBPMISCR_PHY_HOST_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \
+ USBPMISCR_PHY_HSTCKSTB(n) | \
+ USBPMISCR_PHY_CK12MSTB(n))
+/* Device-mode ready */
+#define USBPMISCR_PHY_DEV_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \
+ USBPMISCR_PHY_DEVCKSTB(n))
+/* RCALCODE: 4-bit resistor trim */
+#define USBPMISCR_RCAL_SHIFT(n) (12 + (n) * 16)
+#define USBPMISCR_RCAL_MASK(n) GENMASK(USBPMISCR_RCAL_SHIFT(n) + 3, \
+ USBPMISCR_RCAL_SHIFT(n))
+
+#define MA35_SYS_MISCFCR0 0x70
+/* USB host over-current detect polarity (shared, both ports) */
+#define MISCFCR0_UHOVRCURH BIT(12)
+
+#define MA35_PHY_NUM 2
+
+struct ma35_phy_port {
+ struct phy *phy;
+ unsigned int idx;
+};
struct ma35_usb_phy {
- struct clk *clk;
struct device *dev;
struct regmap *sysreg;
+ struct ma35_phy_port port[MA35_PHY_NUM];
+ struct usb_role_switch *role_sw;
};
-static int ma35_usb_phy_power_on(struct phy *phy)
+static int ma35_usb_phy_init(struct phy *phy)
{
- struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
+ struct ma35_phy_port *port = phy_get_drvdata(phy);
+ struct ma35_usb_phy *p = container_of(port - port->idx,
+ struct ma35_usb_phy, port[0]);
+ unsigned int n = port->idx;
unsigned int val;
int ret;
- ret = clk_prepare_enable(p_phy->clk);
- if (ret < 0) {
- dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
- return ret;
- }
+ regmap_read(p->sysreg, MA35_SYS_USBPMISCR, &val);
- regmap_read(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, &val);
- if (val & PHY0SUSPEND) {
- /*
- * USB PHY0 is in operation mode already
- * make sure USB PHY 60 MHz UTMI Interface Clock ready
- */
- ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
- val & PHY0DEVCKSTB, 10, 1000);
- if (ret == 0)
- return 0;
- }
+ if (val & USBPMISCR_PHY_SUSPEND(n))
+ return 0;
- /*
- * reset USB PHY0.
- * wait until USB PHY0 60 MHz UTMI Interface Clock ready
- */
- regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, (PHY0POR | PHY0SUSPEND));
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_PHY_CTL_MASK(n),
+ USBPMISCR_PHY_POR(n) | USBPMISCR_PHY_SUSPEND(n));
udelay(20);
- /* make USB PHY0 enter operation mode */
- regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, PHY0SUSPEND);
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_PHY_CTL_MASK(n),
+ USBPMISCR_PHY_SUSPEND(n));
- /* make sure USB PHY 60 MHz UTMI Interface Clock ready */
- ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
- val & PHY0DEVCKSTB, 10, 1000);
- if (ret == -ETIMEDOUT) {
- dev_err(p_phy->dev, "Check PHY clock, Timeout: %d\n", ret);
- clk_disable_unprepare(p_phy->clk);
+ if (n == 0) {
+ ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR,
+ val,
+ ((val & USBPMISCR_PHY_HOST_READY(0)) ==
+ USBPMISCR_PHY_HOST_READY(0)) ||
+ ((val & USBPMISCR_PHY_DEV_READY(0)) ==
+ USBPMISCR_PHY_DEV_READY(0)),
+ 10, 1000);
+ } else {
+ ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR,
+ val,
+ (val & USBPMISCR_PHY_HOST_READY(n)) ==
+ USBPMISCR_PHY_HOST_READY(n),
+ 10, 1000);
+ }
+
+ if (ret) {
+ dev_err(p->dev, "USB PHY%u clock not stable (USBPMISCR=0x%08x)\n",
+ n, val);
return ret;
}
return 0;
}
-static int ma35_usb_phy_power_off(struct phy *phy)
+static const struct phy_ops ma35_usb_phy_ops = {
+ .init = ma35_usb_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int ma35_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ return -EOPNOTSUPP;
+}
+
+static enum usb_role ma35_role_sw_get(struct usb_role_switch *sw)
+{
+ struct ma35_usb_phy *p = usb_role_switch_get_drvdata(sw);
+ u32 val;
+
+ regmap_read(p->sysreg, MA35_SYS_PWRONOTP, &val);
+
+ return (val & PWRONOTP_USBP0ID) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+}
+
+static int ma35_role_switch_init(struct platform_device *pdev,
+ struct ma35_usb_phy *p)
{
- struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
+ struct usb_role_switch_desc sw_desc = {0};
+
+ sw_desc.set = ma35_role_sw_set;
+ sw_desc.get = ma35_role_sw_get;
+ sw_desc.allow_userspace_control = true;
+ sw_desc.driver_data = p;
+ sw_desc.fwnode = dev_fwnode(&pdev->dev);
+
+ p->role_sw = usb_role_switch_register(&pdev->dev, &sw_desc);
+ if (IS_ERR(p->role_sw)) {
+ if (PTR_ERR(p->role_sw) == -ENODEV) {
+ p->role_sw = NULL;
+ return 0;
+ }
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->role_sw),
+ "failed to register role switch\n");
+ }
- clk_disable_unprepare(p_phy->clk);
return 0;
}
-static const struct phy_ops ma35_usb_phy_ops = {
- .power_on = ma35_usb_phy_power_on,
- .power_off = ma35_usb_phy_power_off,
- .owner = THIS_MODULE,
-};
+static void ma35_role_switch_exit(struct ma35_usb_phy *p)
+{
+ if (p->role_sw) {
+ usb_role_switch_unregister(p->role_sw);
+ p->role_sw = NULL;
+ }
+}
+
+static struct phy *ma35_usb_phy_xlate(struct device *dev,
+ const struct of_phandle_args *args)
+{
+ struct ma35_usb_phy *p = dev_get_drvdata(dev);
+ unsigned int idx;
+
+ if (args->args_count == 0)
+ idx = 0;
+ else
+ idx = args->args[0];
+
+ if (idx >= MA35_PHY_NUM)
+ return ERR_PTR(-EINVAL);
+
+ return p->port[idx].phy;
+}
static int ma35_usb_phy_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
- struct ma35_usb_phy *p_phy;
- struct phy *phy;
+ struct ma35_usb_phy *p;
+ struct clk *clk;
+ int n, ret;
+ u32 code;
- p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
- if (!p_phy)
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
return -ENOMEM;
- p_phy->dev = &pdev->dev;
- platform_set_drvdata(pdev, p_phy);
+ p->dev = &pdev->dev;
+ platform_set_drvdata(pdev, p);
+
+ p->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "nuvoton,sys");
+ if (IS_ERR(p->sysreg))
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->sysreg),
+ "failed to get SYS regmap\n");
- p_phy->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys");
- if (IS_ERR(p_phy->sysreg))
- return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->sysreg),
- "Failed to get SYS registers\n");
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "failed to get clock\n");
- p_phy->clk = of_clk_get(pdev->dev.of_node, 0);
- if (IS_ERR(p_phy->clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->clk),
- "failed to find usb_phy clock\n");
+ for (n = 0; n < MA35_PHY_NUM; n++) {
+ if (of_property_read_u32_index(pdev->dev.of_node,
+ "nuvoton,rcalcode", n, &code))
+ continue;
- phy = devm_phy_create(&pdev->dev, NULL, &ma35_usb_phy_ops);
- if (IS_ERR(phy))
- return dev_err_probe(&pdev->dev, PTR_ERR(phy), "Failed to create PHY\n");
+ if (code > 15)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "rcalcode[%d] %u out of range (0-15)\n",
+ n, code);
- phy_set_drvdata(phy, p_phy);
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_RCAL_MASK(n),
+ code << USBPMISCR_RCAL_SHIFT(n));
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "nuvoton,oc-active-high"))
+ regmap_update_bits(p->sysreg, MA35_SYS_MISCFCR0,
+ MISCFCR0_UHOVRCURH, MISCFCR0_UHOVRCURH);
+
+ for (n = 0; n < MA35_PHY_NUM; n++) {
+ p->port[n].idx = n;
+
+ p->port[n].phy = devm_phy_create(&pdev->dev, pdev->dev.of_node,
+ &ma35_usb_phy_ops);
+ if (IS_ERR(p->port[n].phy))
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->port[n].phy),
+ "failed to create PHY%d\n", n);
+
+ phy_set_drvdata(p->port[n].phy, &p->port[n]);
+ }
+
+ ret = ma35_role_switch_init(pdev, p);
+ if (ret)
+ return ret;
- provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
- if (IS_ERR(provider))
+ provider = devm_of_phy_provider_register(&pdev->dev, ma35_usb_phy_xlate);
+ if (IS_ERR(provider)) {
+ ma35_role_switch_exit(p);
return dev_err_probe(&pdev->dev, PTR_ERR(provider),
- "Failed to register PHY provider\n");
+ "failed to register PHY provider\n");
+ }
+
return 0;
}
+static void ma35_usb_phy_remove(struct platform_device *pdev)
+{
+ struct ma35_usb_phy *p = platform_get_drvdata(pdev);
+
+ ma35_role_switch_exit(p);
+}
+
static const struct of_device_id ma35_usb_phy_of_match[] = {
- { .compatible = "nuvoton,ma35d1-usb2-phy", },
- { },
+ { .compatible = "nuvoton,ma35d1-usb2-phy" },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match);
static struct platform_driver ma35_usb_phy_driver = {
.probe = ma35_usb_phy_probe,
- .driver = {
- .name = "ma35d1-usb2-phy",
- .of_match_table = ma35_usb_phy_of_match,
+ .remove = ma35_usb_phy_remove,
+ .driver = {
+ .name = "ma35d1-usb2-phy",
+ .of_match_table = ma35_usb_phy_of_match,
},
};
module_platform_driver(ma35_usb_phy_driver);
MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver");
MODULE_AUTHOR("Hui-Ping Chen <hpchen0nvt@gmail.com>");
+MODULE_AUTHOR("Joey Lu <a0987203069@gmail.com>");
MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related
* [PATCH v4 2/3] arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY node
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
Add the USB subsystem nodes for the MA35D1 SoC and boards:
- usb_phy: standalone PHY node with #phy-cells = <1>, covering both
the OTG port (PHY0, index 0) and the host-only port (PHY1, index 1).
Uses nuvoton,sys phandle and HUSBH0_GATE clock per the binding.
- usb (DWC2 gadget, USB0): phys = <&usb_phy 0>, dr_mode = peripheral.
- ehci0/ohci0 (USB0 host): phys = <&usb_phy 0>, companion pairing.
- ehci1/ohci1 (USB1 host): phys = <&usb_phy 1>.
Board files (ma35d1-som-256m.dts, ma35d1-iot-512m.dts) add pinctrl for
VBUSVLD, PWREN and OVC signals and enable all USB nodes.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
---
.../boot/dts/nuvoton/ma35d1-iot-512m.dts | 36 ++++++++++
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 36 ++++++++++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 65 +++++++++++++++++++
3 files changed, 137 insertions(+)
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
index 78534d613486..0a068abda6e0 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
@@ -97,6 +97,16 @@ pinctrl_uart14: uart14-pins {
power-source = <1>;
};
};
+
+ hsusb {
+ pinctrl_hsusb: hsusb-pins {
+ nuvoton,pins = <5 15 1>, /* VBUSVLD */
+ <11 12 9>, /* PWREN */
+ <11 13 9>; /* OVC */
+ bias-disable;
+ power-source = <1>;
+ };
+ };
};
&uart0 {
@@ -151,3 +161,29 @@ eth_phy1: ethernet-phy@1 {
reg = <1>;
};
};
+
+&usb_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hsusb>;
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
index a029b660e8dc..7f1e6d2a1fc2 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
@@ -100,6 +100,16 @@ pinctrl_uart16: uart16-pins {
power-source = <1>;
};
};
+
+ hsusb {
+ pinctrl_hsusb: hsusb-pins {
+ nuvoton,pins = <5 15 1>, /* VBUSVLD */
+ <11 12 9>, /* PWREN */
+ <11 13 9>; /* OVC */
+ bias-disable;
+ power-source = <1>;
+ };
+ };
};
&uart0 {
@@ -153,3 +163,29 @@ eth_phy1: ethernet-phy@1 {
reg = <1>;
};
};
+
+&usb_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hsusb>;
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
index 7228ad4735b5..5a7a39b1b514 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
@@ -88,6 +88,14 @@ sys: system-management@40460000 {
#reset-cells = <1>;
};
+ usb_phy: usb-phy {
+ compatible = "nuvoton,ma35d1-usb2-phy";
+ clocks = <&clk HUSBH0_GATE>;
+ nuvoton,sys = <&sys>;
+ #phy-cells = <1>;
+ status = "disabled";
+ };
+
clk: clock-controller@40460200 {
compatible = "nuvoton,ma35d1-clk";
reg = <0x00000000 0x40460200 0x0 0x100>;
@@ -431,5 +439,62 @@ mdio1: mdio {
#size-cells = <0>;
};
};
+
+ usb: usb@40200000 {
+ compatible = "snps,dwc2";
+ reg = <0x0 0x40200000 0x0 0x1000>;
+ interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk USBD_GATE>;
+ clock-names = "otg";
+ phys = <&usb_phy 0>;
+ phy-names = "usb2-phy";
+ dr_mode = "peripheral";
+ g-np-tx-fifo-size = <16>;
+ g-rx-fifo-size = <0x100>;
+ g-tx-fifo-size = <256 256 64 64 64 32 32 32>;
+ status = "disabled";
+ };
+
+ ehci0: usb@40140000 {
+ compatible = "generic-ehci";
+ reg = <0x0 0x40140000 0x0 0x1000>;
+ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH0_GATE>;
+ phys = <&usb_phy 0>;
+ phy-names = "usb";
+ companion = <&ohci0>;
+ status = "disabled";
+ };
+
+ ehci1: usb@401c0000 {
+ compatible = "generic-ehci";
+ reg = <0x0 0x401c0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH1_GATE>;
+ phys = <&usb_phy 1>;
+ phy-names = "usb";
+ companion = <&ohci1>;
+ status = "disabled";
+ };
+
+ ohci0: usb@40150000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0x40150000 0x0 0x1000>;
+ interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH0_GATE>;
+ phys = <&usb_phy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@401d0000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0x401d0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH1_GATE>;
+ phys = <&usb_phy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
};
};
--
2.43.0
^ permalink raw reply related
* [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu, Krzysztof Kozlowski
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
The MA35D1 exposes two USB PHY ports (PHY0 and PHY1) managed by the
same hardware block (USBPMISCR register in the system-management syscon).
PHY0 is shared between the DWC2 gadget controller and EHCI0/OHCI0
through an automatic hardware mux that follows the USB ID pin; PHY1 is
dedicated to EHCI1/OHCI1. Because both ports share the same register
topology a single binding and driver should cover both.
Changing '#phy-cells' from const: 0 to enum: [0, 1] lets consumers name
the port they need while preserving backward compatibility: boards already
using '#phy-cells = <0>' continue to validate and function unchanged.
The two new optional properties:
- nuvoton,rcalcode: the resistor calibration trim code is determined at
board design time to match the PCB trace impedance.
- nuvoton,oc-active-high: the over-current detect polarity depends on
the choice of VBUS power switch.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../bindings/phy/nuvoton,ma35d1-usb2-phy.yaml | 44 ++++++++++++++++++-
1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
index fff858c909a0..1bd22dd0e5fc 100644
--- a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
@@ -8,6 +8,18 @@ title: Nuvoton MA35D1 USB2 phy
maintainers:
- Hui-Ping Chen <hpchen0nvt@gmail.com>
+ - Joey Lu <yclu4@nuvoton.com>
+
+description:
+ USB 2.0 PHY for the Nuvoton MA35D1 SoC. The PHY node is a standalone
+ platform device that accesses the USB PHY control registers inside the
+ system-management syscon block via the nuvoton,sys phandle.
+
+ PHY0 is the OTG port whose signals are routed to either the DWC2 gadget
+ controller or the EHCI0/OHCI0 host controller by a hardware mux that
+ follows the USB ID pin automatically.
+
+ PHY1 is a dedicated host-only port used by EHCI1/OHCI1.
properties:
compatible:
@@ -15,7 +27,12 @@ properties:
- nuvoton,ma35d1-usb2-phy
"#phy-cells":
- const: 0
+ enum: [0, 1]
+ description:
+ When 0, the node exposes PHY0 only and consumers reference it
+ with no cell argument. When 1, the single cell selects the port,
+ with 0 for the OTG port (USB0, shared with DWC2 gadget controller)
+ and 1 for the host-only port (USB1).
clocks:
maxItems: 1
@@ -23,7 +40,29 @@ properties:
nuvoton,sys:
$ref: /schemas/types.yaml#/definitions/phandle
description:
- phandle to syscon for checking the PHY clock status.
+ Phandle to the system-management syscon node providing access to the
+ USB PHY control registers.
+
+ nuvoton,rcalcode:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ minimum: 0
+ maximum: 15
+ description:
+ Resistor calibration trim codes for PHY0 and PHY1 respectively.
+ Each 4-bit value is written to the RCALCODE field in USBPMISCR and
+ adjusts the PHY's internal termination resistance. Both entries must
+ be supplied when this property is present; when absent the hardware
+ reset default is used for each port.
+
+ nuvoton,oc-active-high:
+ type: boolean
+ description:
+ When present, the over-current detect input from the VBUS power switch
+ is treated as active-high. The default (property absent) is active-low.
+ This setting is shared by both USB host ports.
required:
- compatible
@@ -42,4 +81,5 @@ examples:
clocks = <&clk USBD_GATE>;
nuvoton,sys = <&sys>;
#phy-cells = <0>;
+ nuvoton,rcalcode = <7 7>;
};
--
2.43.0
^ permalink raw reply related
* [PATCH v4 0/3] phy: nuvoton: extend MA35D1 USB2 PHY driver for dual-port OTG support
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
The MA35D1 SoC has two USB PHY ports managed by a shared hardware block:
- PHY0 (USB0): OTG port shared between the DWC2 gadget controller and
the EHCI0/OHCI0 host controllers. A hardware mux automatically routes
USB0 signals to the correct controller based on the USB ID pin state.
- PHY1 (USB1): dedicated host-only port for EHCI1/OHCI1.
This series extends the existing phy-ma35d1-usb2.c driver and its binding
to cover both ports and add OTG role-switch support, while keeping full
backward compatibility with existing device trees.
Changes since v3:
- dt-bindings: add nuvoton,rcalcode = <7 7> example to illustrate the
optional resistor calibration property; revert redundant clock example
change (back to USBD_GATE as in the original binding).
- driver: handle -ENODEV from usb_role_switch_register() gracefully so
that probe succeeds when CONFIG_USB_ROLE_SWITCH is not enabled.
Joey Lu (3):
dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and
OTG
arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY
node
phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
.../bindings/phy/nuvoton,ma35d1-usb2-phy.yaml | 44 ++-
.../boot/dts/nuvoton/ma35d1-iot-512m.dts | 36 +++
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 36 +++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 65 ++++
drivers/phy/nuvoton/phy-ma35d1-usb2.c | 284 +++++++++++++-----
5 files changed, 389 insertions(+), 76 deletions(-)
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.43.0
^ permalink raw reply
* Re: [PATCH v6 08/14] dt-bindings: media: mediatek: vcodec: add decoder dt-bindings for mt8196
From: Krzysztof Kozlowski @ 2026-07-20 10:19 UTC (permalink / raw)
To: Kyrie Wu (吴晗)
Cc: matthias.bgg@gmail.com, fshao@chromium.org,
Yunfei Dong (董云飞), conor+dt@kernel.org,
fanwu01@zju.edu.cn, jacopo.mondi@ideasonboard.com,
haoxiang_li2024@163.com, tfiga@chromium.org,
AngeloGioacchino Del Regno,
Andrew-CT Chen (陳智迪),
linux-media@vger.kernel.org, ribalda@chromium.org,
wenst@chromium.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, kees@kernel.org,
rongqianfeng@vivo.com, Irui Wang (王瑞),
krzk+dt@kernel.org, linux-mediatek@lists.infradead.org,
Tiffany Lin (林慧珊),
sakari.ailus@linux.intel.com, benjamin.gaignard@collabora.com,
p.zabel@pengutronix.de, mchehab@kernel.org,
sebastian.fricke@collabora.com, robh@kernel.org,
linux-arm-kernel@lists.infradead.org,
nicolas.dufresne@collabora.com, hverkuil+cisco@kernel.org,
laurent.pinchart+renesas@ideasonboard.com
In-Reply-To: <a9123dce5ae7ef184f5640b12e3e9427084ec542.camel@mediatek.com>
On 20/07/2026 08:26, Kyrie Wu (吴晗) wrote:
> On Mon, 2026-07-20 at 07:49 +0200, Krzysztof Kozlowski wrote:
>> On 20/07/2026 07:46, Krzysztof Kozlowski wrote:
>>> On Mon, Jul 20, 2026 at 09:20:50AM +0800, Kyrie Wu wrote:
>>>> Add the MT8196 compatible string to the MediaTek vcodec subdev
>>>> decoder binding.
>>>>
>>>> Compared to previous ICs, the MT8196 supports a 10-bit decoder
>>>> and has a decoding capability of 4K@120fps. It also supports
>>>> 36-bit DRAM IOVA address and Video Power Control to optimize
>>>> bandwidth and voltage usage.
>>>
>>> Please wrap commit message according to Linux coding style /
>>> submission
>>> process (neither too early nor over the limit):
>>> https://urldefense.com/v3/__https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst*L597__;Iw!!CTRNKA9wMg0ARbw!gDjbq0EZNkhLTzuJM_w5PopnDFej1mG4qnkRFgyoiU7Dvhm7tj3MeG5eZYNGA0L0G_61j-cmdIxORA$
>>>
>>>
>>>>
>>>> Signed-off-by: Kyrie Wu <kyrie.wu@mediatek.com>
>>>> Acked-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
>>>> ---
>>>> .../bindings/media/mediatek,vcodec-subdev-decoder.yaml
>>>> | 1 +
>>>> 1 file changed, 1 insertion(+)
>>>
>>> Acked-by: Krzysztof Kozlowski
>>> <krzysztof.kozlowski@oss.qualcomm.com>
>>
>> Actually no. I checked previous comments and you received feedback
>> which
>> you basically ignored, so explain why this model has flexible number
>> of
>> clocks
>>
>> Best regards,
>> Krzysztof
>
> Dear Krzysztof,
>
> As mentioned in the previous version of the communication, the
> information regarding clk was incorrect, and I have corrected it in
> this version.
>
> Thank you for your attention and reminder.
Read my message carefully instead of responding whatever necessary to
make me go away.
NAK
Where did you explain that device has flexible number of clocks?
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 2/4] dt-bindings: sound: fsl,easrc: add ports binding for multiple contexts
From: sashiko-bot @ 2026-07-20 10:16 UTC (permalink / raw)
To: shengjiu.wang; +Cc: conor+dt, Frank.Li, devicetree, imx, robh
In-Reply-To: <20260720095450.1618747-3-shengjiu.wang@oss.nxp.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The schema fails to restrict the `ports` container to the 4 supported contexts (ports 0-3), allowing invalid extraneous ports to silently pass validation.
--
--- Patch [2]: [PATCH 2/4] dt-bindings: sound: fsl,easrc: add ports binding for multiple contexts ---
commit 0461b121d300fd40233634f4b977c32906d647ce
Author: Shengjiu Wang <shengjiu.wang@nxp.com>
dt-bindings: sound: fsl,easrc: add ports binding for multiple contexts
The i.MX EASRC hardware supports up to four conversion contexts
(A, B, C, D). Add a ports container property to the binding to allow
each context to be represented as an individual audio-graph port.
> diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
> index d5727f8bfb0b5..0f579870110d9 100644
> --- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
> +++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
> @@ -55,6 +55,29 @@ properties:
> - const: imx/easrc/easrc-imx8mn.bin
> description: The coefficient table for the filters
>
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
> + properties:
> + port@0:
> + $ref: audio-graph-port.yaml#
> + unevaluatedProperties: false
> + description: port for context A
[Severity: Low]
Is this ports container missing a constraint to reject unsupported unit
addresses?
Since the inherited graph.yaml schema permits any port@.*, a device tree
defining port@4 or higher will currently pass dt-validate without an error,
even though the hardware only supports 4 contexts.
Would adding patternProperties: "^port@[0-3]$" or unevaluatedProperties: false
to the ports block help catch invalid device tree configurations?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720095450.1618747-1-shengjiu.wang@oss.nxp.com?part=2
^ permalink raw reply
* Re: [PATCH v2 4/4] regulator: fan53555: Add support for mode operations on Silergy devices
From: sashiko-bot @ 2026-07-20 10:16 UTC (permalink / raw)
To: Victor Krawiec; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260720100336.62004-5-victor.krawiec@arturia.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] fan53555_set_mode() incorrectly updates the voltage register instead of the mode register when setting REGULATOR_MODE_NORMAL, causing severe voltage corruption on chips where these registers are distinct.
--
--- Patch [4]: [PATCH v2 4/4] regulator: fan53555: Add support for mode operations on Silergy devices ---
commit 4b739645e4ed09b01a00b9bb826b1cf9a4da63a8
Author: Victor Krawiec <victor.krawiec@arturia.com>
regulator: fan53555: Add support for mode operations on Silergy devices
This commit makes the PWM mode configurable from devicetree for Silergy
devices. It adds a mapping function for devicetree mode flags to regulator
modes, allowing boards to force PWM mode to manage supply ripple.
> diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
> index c282236959b18..85f46874eb49d 100644
> --- a/drivers/regulator/fan53555.c
> +++ b/drivers/regulator/fan53555.c
[ ... ]
> @@ -389,6 +390,18 @@ static int rk8602_voltages_setup_rockchip(struct fan53555_device_info *di)
> return 0;
> }
>
> +static inline unsigned int fan53555_map_mode(unsigned int mode)
> +{
> + switch (mode) {
> + case FAN53555_REGULATOR_MODE_FORCE_PWM:
> + return REGULATOR_MODE_FAST;
> + case FAN53555_REGULATOR_MODE_AUTO:
> + return REGULATOR_MODE_NORMAL;
[Severity: High]
This isn't a bug introduced by this patch, but mapping to REGULATOR_MODE_NORMAL
exposes a pre-existing issue in fan53555_set_mode().
When REGULATOR_MODE_NORMAL is selected, fan53555_set_mode() updates di->vol_reg
instead of di->mode_reg:
fan53555_set_mode() {
...
case REGULATOR_MODE_NORMAL:
regmap_update_bits(rdev->regmap, di->vol_reg, di->mode_mask, 0);
break;
...
}
On chips where the mode register and voltage register are distinct (such as
RK8602, FAN53526, or TCS), clearing di->mode_mask inside di->vol_reg corrupts
the voltage selection bits. This can cause a sudden, unintended drop in output
voltage leading to a system lockup.
Is it possible to correct the register used in fan53555_set_mode() before
enabling this mode mapping?
> + default:
> + return REGULATOR_MODE_INVALID;
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720100336.62004-1-victor.krawiec@arturia.com?part=4
^ permalink raw reply
* Re: [PATCH v13 3/3] media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
From: Loic Poulain @ 2026-07-20 10:15 UTC (permalink / raw)
To: guoniu.zhou
Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Bryan O'Donoghue,
Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, imx,
linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou
In-Reply-To: <20260720-csi_formatter-v13-3-4dc9a80e4cfd@oss.nxp.com>
On Mon, Jul 20, 2026 at 11:56 AM <guoniu.zhou@oss.nxp.com> wrote:
>
> From: Guoniu Zhou <guoniu.zhou@nxp.com>
>
> The CSI pixel formatter is a module found on i.MX95 used to reformat
> packet info, pixel and non-pixel data from CSI-2 host controller to
> match Pixel Link(PL) definition.
>
> Add data formatting support.
>
> Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
Looks good to me but if you're planning another version, I'd recommend
using devm_pm_clk_create(dev) together with of_pm_clk_add_clks(dev)
and relying on the generic PM clock infrastructure instead of handling
the clocks explicitly in the driver.
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> Changes in v13:
> - Replace pr_warn_once() with dev_warn() in csi_formatter_get_index_by_dt()
> to provide device context and warn on every occurrence (Loic)
> - Add WARN_ON() check for csi_formatter_find_format() return value in
> start/stop_stream functions to catch unexpected NULL (Loic)
> - Use regmap_set_bits()/regmap_clear_bits() instead of read-modify-write
> pattern and remove unused csi_formatter_read/write helper functions (Loic)
> - Add Reviewed-by tag from Frank Li
>
> Changes in v12:
> - Fix stream ID handling: iterate routing table instead of assuming
> stream ID equals loop index (0-7)
> - Remove stream_to_vc[] array: derive VC from routing table and frame
> descriptor on each start/stop operation
> - Remove V4L2_SUBDEV_FL_HAS_EVENTS flag since driver does not generate events
> - Support stream IDs 0-63 by using BIT_ULL() for stream masks
> - Add get_frame_desc call in stop_stream with proper error handling
> - Add csi_formatter_read() helper function for register reads
> - Use read-modify-write for CSI_VC_PIXEL_DATA_TYPE register to support
> multiplexed streams sharing the same virtual channel
> - Use route->sink_pad instead of hardcoded CSI_FORMATTER_PAD_SINK
> - Write back coerced format in set_fmt before propagating to source stream
> - Drop Frank's Reviewed-by tag due to significant changes, requesting re-review
>
> Changes in v10:
> - Use u8 for vc in csi_formatter_get_vc() and drop vc < 0 check
> - Add MFD_SYSCON dependency to Kconfig
> - Fix stream/VC mapping potential mismatch in start/stop_stream functions
>
> Changes in v8:
> - Remove fmt field and look up format from subdev state instead
> - Unify function and structure naming to use csi_formatter_ prefix
> - Remove misleading alignment comment from set_fmt function
> - Optimize get_frame_desc to call once per start_stream
> - Replace V4L2_FRAME_DESC_ENTRY_MAX with CSI_FORMATTER_VC_NUM in loops
> - Remove redundant debug message in enable_streams
> - Use MEDIA_PAD_FL_MUST_CONNECT flag instead of manual link check
> - Fix typo: Formater -> Formatter in Kconfig help text
> - Improve grammar in data type index mapping comment
>
> Changes in v7:
> - Update references from imx9 to imx95 for consistency with dt-bindings
> - Enable PM runtime before async registration
>
> Changes in v6:
> - Remove unused header includes
> - Unify macro naming: VCx/VCX -> VC and parameter x -> vc
> - Remove unused format field from csi_formatter struct
> - Use compact initialization for formats array
> - Make find_csi_format() return NULL instead of default format
> - Use unsigned int for array index in find_csi_format()
> - Add err_ prefix to error handling labels
> - Add v4l2_subdev_cleanup() and reorder cleanup sequence
> - Update enable_streams debug output format
> - Rename VC_MAX to VC_NUM and fix boundary check
> - Update CSI formatter Kconfig description
> - Use v4l2_subdev_get_frame_desc_passthrough() helper
> - Fix error paths in async registration and probe
> - Add mutex to protect enabled_streams
> - Switch to devm_pm_runtime_enable()
> - Remove redundant num_routes check in set_routing
> - Optimize get_index_by_dt() and add warning for unsupported type
> - csi_formatter_start/stop_stream: Process all streams in mask
> ---
> MAINTAINERS | 8 +
> drivers/media/platform/nxp/Kconfig | 15 +
> drivers/media/platform/nxp/Makefile | 1 +
> drivers/media/platform/nxp/imx95-csi-formatter.c | 808 +++++++++++++++++++++++
> 4 files changed, 832 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index efbf808063e5..05009228b162 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19275,6 +19275,14 @@ S: Maintained
> F: Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
> F: drivers/media/platform/nxp/imx-jpeg
>
> +NXP i.MX 95 CSI PIXEL FORMATTER V4L2 DRIVER
> +M: Guoniu Zhou <guoniu.zhou@nxp.com>
> +L: imx@lists.linux.dev
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
> +F: drivers/media/platform/nxp/imx95-csi-formatter.c
> +
> NXP i.MX CLOCK DRIVERS
> M: Abel Vesa <abelvesa@kernel.org>
> R: Peng Fan <peng.fan@nxp.com>
> diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
> index 40e3436669e2..8f49908b0022 100644
> --- a/drivers/media/platform/nxp/Kconfig
> +++ b/drivers/media/platform/nxp/Kconfig
> @@ -28,6 +28,21 @@ config VIDEO_IMX8MQ_MIPI_CSI2
> Video4Linux2 driver for the MIPI CSI-2 receiver found on the i.MX8MQ
> SoC.
>
> +config VIDEO_IMX95_CSI_FORMATTER
> + tristate "NXP i.MX95 CSI Pixel Formatter driver"
> + depends on ARCH_MXC || COMPILE_TEST
> + depends on MFD_SYSCON
> + depends on VIDEO_DEV
> + select MEDIA_CONTROLLER
> + select V4L2_FWNODE
> + select VIDEO_V4L2_SUBDEV_API
> + help
> + This driver provides support for the CSI Pixel Formatter found on
> + i.MX95 series SoCs. This module unpacks the pixels received from the
> + CSI-2 interface and reformats them to meet pixel link requirements.
> +
> + Say Y here to enable CSI Pixel Formatter module for i.MX95 SoC.
> +
> config VIDEO_IMX_MIPI_CSIS
> tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models"
> depends on ARCH_MXC || COMPILE_TEST
> diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
> index 4d90eb713652..6410115d870e 100644
> --- a/drivers/media/platform/nxp/Makefile
> +++ b/drivers/media/platform/nxp/Makefile
> @@ -6,6 +6,7 @@ obj-y += imx8-isi/
>
> obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
> obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
> +obj-$(CONFIG_VIDEO_IMX95_CSI_FORMATTER) += imx95-csi-formatter.o
> obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
> obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
> obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
> diff --git a/drivers/media/platform/nxp/imx95-csi-formatter.c b/drivers/media/platform/nxp/imx95-csi-formatter.c
> new file mode 100644
> index 000000000000..b0e8e753e94a
> --- /dev/null
> +++ b/drivers/media/platform/nxp/imx95-csi-formatter.c
> @@ -0,0 +1,808 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +
> +#include <media/mipi-csi2.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-mc.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* CSI Pixel Formatter registers map */
> +
> +#define CSI_VC_INTERLACED_LINE_CNT(vc) (0x00 + (vc) * 0x04)
> +#define INTERLACED_ODD_LINE_CNT_SET(x) FIELD_PREP(GENMASK(13, 0), (x))
> +#define INTERLACED_EVEN_LINE_CNT_SET(x) FIELD_PREP(GENMASK(29, 16), (x))
> +
> +#define CSI_VC_INTERLACED_CTRL 0x20
> +
> +#define CSI_VC_INTERLACED_ERR 0x24
> +#define CSI_VC_ERR_MASK GENMASK(7, 0)
> +#define CSI_VC_ERR(vc) BIT((vc))
> +
> +#define CSI_VC_YUV420_FIRST_LINE_EVEN 0x28
> +#define YUV420_FIRST_LINE_EVEN(vc) BIT((vc))
> +
> +#define CSI_RAW32_CTRL 0x30
> +#define CSI_VC_RAW32_MODE(vc) BIT((vc))
> +#define CSI_VC_RAW32_SWAP_MODE(vc) BIT((vc) + 8)
> +
> +#define CSI_STREAM_FENCING_CTRL 0x34
> +#define CSI_VC_STREAM_FENCING(vc) BIT((vc))
> +#define CSI_VC_STREAM_FENCING_RST(vc) BIT((vc) + 8)
> +
> +#define CSI_STREAM_FENCING_STS 0x38
> +#define CSI_STREAM_FENCING_STS_MASK GENMASK(7, 0)
> +
> +#define CSI_VC_NON_PIXEL_DATA_TYPE(vc) (0x40 + (vc) * 0x04)
> +
> +#define CSI_VC_PIXEL_DATA_CTRL(vc) (0x60 + (vc) * 0x04)
> +#define NEW_VC(vc) FIELD_PREP(GENMASK(3, 1), vc)
> +#define REROUTE_VC_ENABLE BIT(0)
> +
> +#define CSI_VC_ROUTE_PIXEL_DATA_TYPE(vc) (0x80 + (vc) * 0x04)
> +
> +#define CSI_VC_NON_PIXEL_DATA_CTRL(vc) (0xa0 + (vc) * 0x04)
> +
> +#define CSI_VC_PIXEL_DATA_TYPE(vc) (0xc0 + (vc) * 0x04)
> +
> +#define CSI_VC_PIXEL_DATA_TYPE_ERR(vc) (0xe0 + (vc) * 0x04)
> +
> +#define CSI_FORMATTER_PAD_SINK 0
> +#define CSI_FORMATTER_PAD_SOURCE 1
> +#define CSI_FORMATTER_PAD_NUM 2
> +
> +#define CSI_FORMATTER_VC_NUM 8 /* Number of virtual channels */
> +
> +struct csi_formatter_pix_format {
> + u32 code;
> + u32 data_type;
> +};
> +
> +struct csi_formatter {
> + struct device *dev;
> + struct regmap *regs;
> + struct clk *clk;
> +
> + struct v4l2_subdev sd;
> + struct v4l2_subdev *csi_sd;
> + struct v4l2_async_notifier notifier;
> + struct media_pad pads[CSI_FORMATTER_PAD_NUM];
> +
> + u32 remote_pad;
> + u32 reg_offset;
> +
> + /* Protects enabled_streams */
> + struct mutex lock;
> + u64 enabled_streams;
> +};
> +
> +struct csi_formatter_dt_index {
> + u8 dtype;
> + u8 index;
> +};
> +
> +/*
> + * The index corresponds to the bit index in the register that enables
> + * the data type of pixel data transported by the Formatter.
> + */
> +static const struct csi_formatter_dt_index formatter_dt_to_index_map[] = {
> + { .dtype = MIPI_CSI2_DT_YUV420_8B, .index = 0 },
> + { .dtype = MIPI_CSI2_DT_YUV420_8B_LEGACY, .index = 2 },
> + { .dtype = MIPI_CSI2_DT_YUV422_8B, .index = 6 },
> + { .dtype = MIPI_CSI2_DT_RGB444, .index = 8 },
> + { .dtype = MIPI_CSI2_DT_RGB555, .index = 9 },
> + { .dtype = MIPI_CSI2_DT_RGB565, .index = 10 },
> + { .dtype = MIPI_CSI2_DT_RGB666, .index = 11 },
> + { .dtype = MIPI_CSI2_DT_RGB888, .index = 12 },
> + { .dtype = MIPI_CSI2_DT_RAW6, .index = 16 },
> + { .dtype = MIPI_CSI2_DT_RAW7, .index = 17 },
> + { .dtype = MIPI_CSI2_DT_RAW8, .index = 18 },
> + { .dtype = MIPI_CSI2_DT_RAW10, .index = 19 },
> + { .dtype = MIPI_CSI2_DT_RAW12, .index = 20 },
> + { .dtype = MIPI_CSI2_DT_RAW14, .index = 21 },
> + { .dtype = MIPI_CSI2_DT_RAW16, .index = 22 },
> +};
> +
> +static const struct csi_formatter_pix_format formats[] = {
> + /* YUV formats */
> + { MEDIA_BUS_FMT_UYVY8_1X16, MIPI_CSI2_DT_YUV422_8B },
> + /* RGB formats */
> + { MEDIA_BUS_FMT_RGB565_1X16, MIPI_CSI2_DT_RGB565 },
> + { MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888 },
> + /* RAW (Bayer and greyscale) formats */
> + { MEDIA_BUS_FMT_SBGGR8_1X8, MIPI_CSI2_DT_RAW8 },
> + { MEDIA_BUS_FMT_SGBRG8_1X8, MIPI_CSI2_DT_RAW8 },
> + { MEDIA_BUS_FMT_SGRBG8_1X8, MIPI_CSI2_DT_RAW8 },
> + { MEDIA_BUS_FMT_SRGGB8_1X8, MIPI_CSI2_DT_RAW8 },
> + { MEDIA_BUS_FMT_Y8_1X8, MIPI_CSI2_DT_RAW8 },
> + { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
> + { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
> + { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
> + { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
> + { MEDIA_BUS_FMT_Y10_1X10, MIPI_CSI2_DT_RAW10 },
> + { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
> + { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
> + { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
> + { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
> + { MEDIA_BUS_FMT_Y12_1X12, MIPI_CSI2_DT_RAW12 },
> + { MEDIA_BUS_FMT_SBGGR14_1X14, MIPI_CSI2_DT_RAW14 },
> + { MEDIA_BUS_FMT_SGBRG14_1X14, MIPI_CSI2_DT_RAW14 },
> + { MEDIA_BUS_FMT_SGRBG14_1X14, MIPI_CSI2_DT_RAW14 },
> + { MEDIA_BUS_FMT_SRGGB14_1X14, MIPI_CSI2_DT_RAW14 },
> + { MEDIA_BUS_FMT_SBGGR16_1X16, MIPI_CSI2_DT_RAW16 },
> + { MEDIA_BUS_FMT_SGBRG16_1X16, MIPI_CSI2_DT_RAW16 },
> + { MEDIA_BUS_FMT_SGRBG16_1X16, MIPI_CSI2_DT_RAW16 },
> + { MEDIA_BUS_FMT_SRGGB16_1X16, MIPI_CSI2_DT_RAW16 },
> +};
> +
> +static const struct v4l2_mbus_framefmt formatter_default_fmt = {
> + .code = MEDIA_BUS_FMT_UYVY8_1X16,
> + .width = 1920U,
> + .height = 1080U,
> + .field = V4L2_FIELD_NONE,
> + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> + .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
> + .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
> + .quantization = V4L2_QUANTIZATION_LIM_RANGE,
> +};
> +
> +static const struct csi_formatter_pix_format *csi_formatter_find_format(u32 code)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(formats); i++)
> + if (code == formats[i].code)
> + return &formats[i];
> +
> + return NULL;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static inline struct csi_formatter *sd_to_formatter(struct v4l2_subdev *sdev)
> +{
> + return container_of(sdev, struct csi_formatter, sd);
> +}
> +
> +static int __csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_krouting *routing)
> +{
> + int ret;
> +
> + ret = v4l2_subdev_routing_validate(sd, routing,
> + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
> + if (ret)
> + return ret;
> +
> + return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
> + &formatter_default_fmt);
> +}
> +
> +static int csi_formatter_subdev_init_state(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state)
> +{
> + struct v4l2_subdev_route routes[] = {
> + {
> + .sink_pad = CSI_FORMATTER_PAD_SINK,
> + .sink_stream = 0,
> + .source_pad = CSI_FORMATTER_PAD_SOURCE,
> + .source_stream = 0,
> + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
> + },
> + };
> +
> + struct v4l2_subdev_krouting routing = {
> + .num_routes = ARRAY_SIZE(routes),
> + .routes = routes,
> + };
> +
> + return __csi_formatter_subdev_set_routing(sd, sd_state, &routing);
> +}
> +
> +static int csi_formatter_subdev_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->pad == CSI_FORMATTER_PAD_SOURCE) {
> + struct v4l2_mbus_framefmt *fmt;
> +
> + if (code->index > 0)
> + return -EINVAL;
> +
> + fmt = v4l2_subdev_state_get_format(sd_state, code->pad,
> + code->stream);
> + code->code = fmt->code;
> + return 0;
> + }
> +
> + if (code->index >= ARRAY_SIZE(formats))
> + return -EINVAL;
> +
> + code->code = formats[code->index].code;
> +
> + return 0;
> +}
> +
> +static int csi_formatter_subdev_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct csi_formatter_pix_format const *format;
> + struct v4l2_mbus_framefmt *fmt;
> +
> + if (sdformat->pad == CSI_FORMATTER_PAD_SOURCE)
> + return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
> +
> + format = csi_formatter_find_format(sdformat->format.code);
> + if (!format)
> + format = &formats[0];
> +
> + v4l_bound_align_image(&sdformat->format.width, 1, 0xffff, 2,
> + &sdformat->format.height, 1, 0xffff, 0, 0);
> +
> + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad,
> + sdformat->stream);
> + *fmt = sdformat->format;
> +
> + /* Set default code if user set an invalid value */
> + fmt->code = format->code;
> + sdformat->format = *fmt;
> +
> + /* Propagate the format from sink stream to source stream */
> + fmt = v4l2_subdev_state_get_opposite_stream_format(sd_state, sdformat->pad,
> + sdformat->stream);
> + if (!fmt)
> + return -EINVAL;
> +
> + *fmt = sdformat->format;
> +
> + return 0;
> +}
> +
> +static int csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + enum v4l2_subdev_format_whence which,
> + struct v4l2_subdev_krouting *routing)
> +{
> + if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
> + media_entity_is_streaming(&sd->entity))
> + return -EBUSY;
> +
> + return __csi_formatter_subdev_set_routing(sd, state, routing);
> +}
> +
> +static u8 csi_formatter_get_index_by_dt(struct csi_formatter *formatter,
> + u8 data_type)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(formatter_dt_to_index_map); ++i) {
> + const struct csi_formatter_dt_index *entry =
> + &formatter_dt_to_index_map[i];
> +
> + if (data_type == entry->dtype)
> + return entry->index;
> + }
> +
> + dev_warn(formatter->dev, "Unsupported data type 0x%x, using default\n",
> + data_type);
> +
> + return formatter_dt_to_index_map[0].index;
> +}
> +
> +static int csi_formatter_get_vc(struct csi_formatter *formatter,
> + struct v4l2_mbus_frame_desc *fd,
> + unsigned int stream)
> +{
> + struct v4l2_mbus_frame_desc_entry *entry = NULL;
> + unsigned int i;
> + u8 vc;
> +
> + for (i = 0; i < fd->num_entries; ++i) {
> + if (fd->entry[i].stream == stream) {
> + entry = &fd->entry[i];
> + break;
> + }
> + }
> +
> + if (!entry) {
> + dev_err(formatter->dev,
> + "No frame desc entry for stream %u\n", stream);
> + return -EPIPE;
> + }
> +
> + vc = entry->bus.csi2.vc;
> +
> + if (vc >= CSI_FORMATTER_VC_NUM) {
> + dev_err(formatter->dev, "Invalid virtual channel %u\n", vc);
> + return -EINVAL;
> + }
> +
> + return vc;
> +}
> +
> +static void csi_formatter_stop_stream(struct csi_formatter *formatter,
> + struct v4l2_subdev_state *state,
> + u64 stream_mask)
> +{
> + const struct csi_formatter_pix_format *pix_fmt;
> + struct v4l2_mbus_frame_desc fd = {};
> + struct v4l2_subdev_route *route;
> + struct v4l2_mbus_framefmt *fmt;
> + unsigned int reg;
> + unsigned int mask;
> + int vc;
> + int ret;
> +
> + ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
> + formatter->remote_pad, &fd);
> + if (ret < 0 && ret != -ENOIOCTLCMD) {
> + dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
> + return;
> + }
> +
> + for_each_active_route(&state->routing, route) {
> + if (route->source_pad != CSI_FORMATTER_PAD_SOURCE)
> + continue;
> +
> + if (!(stream_mask & BIT_ULL(route->source_stream)))
> + continue;
> +
> + if (ret == -ENOIOCTLCMD) {
> + /*
> + * Source doesn't implement get_frame_desc, use
> + * default VC 0
> + */
> + vc = 0;
> + } else {
> + vc = csi_formatter_get_vc(formatter, &fd,
> + route->sink_stream);
> + if (vc < 0)
> + continue;
> + }
> +
> + fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
> + route->sink_stream);
> +
> + pix_fmt = csi_formatter_find_format(fmt->code);
> + if (WARN_ON(!pix_fmt))
> + continue;
> +
> + reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
> + mask = BIT(csi_formatter_get_index_by_dt(formatter,
> + pix_fmt->data_type));
> +
> + /* Clear the data type bit to disable this VC */
> + regmap_clear_bits(formatter->regs, reg, mask);
> + }
> +}
> +
> +static int csi_formatter_start_stream(struct csi_formatter *formatter,
> + struct v4l2_subdev_state *state,
> + u64 stream_mask)
> +{
> + const struct csi_formatter_pix_format *pix_fmt;
> + struct v4l2_subdev_route *route;
> + struct v4l2_mbus_framefmt *fmt;
> + struct v4l2_mbus_frame_desc fd = {};
> + u64 configured_streams = 0;
> + unsigned int reg;
> + unsigned int mask;
> + int vc;
> + int ret;
> +
> + ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
> + formatter->remote_pad, &fd);
> + if (ret < 0 && ret != -ENOIOCTLCMD) {
> + dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
> + return ret;
> + }
> +
> + for_each_active_route(&state->routing, route) {
> + if (route->source_pad != CSI_FORMATTER_PAD_SOURCE)
> + continue;
> +
> + if (!(stream_mask & BIT_ULL(route->source_stream)))
> + continue;
> +
> + if (ret == -ENOIOCTLCMD) {
> + /*
> + * Source doesn't implement get_frame_desc, use
> + * default VC 0
> + */
> + vc = 0;
> + } else {
> + vc = csi_formatter_get_vc(formatter, &fd,
> + route->sink_stream);
> + if (vc < 0) {
> + ret = vc;
> + goto err_cleanup;
> + }
> + }
> +
> + fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
> + route->sink_stream);
> +
> + pix_fmt = csi_formatter_find_format(fmt->code);
> + if (WARN_ON(!pix_fmt)) {
> + ret = -EINVAL;
> + goto err_cleanup;
> + }
> +
> + reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
> + mask = BIT(csi_formatter_get_index_by_dt(formatter,
> + pix_fmt->data_type));
> +
> + /* Set the data type bit to enable this VC */
> + regmap_set_bits(formatter->regs, reg, mask);
> +
> + configured_streams |= BIT_ULL(route->source_stream);
> + }
> +
> + return 0;
> +
> +err_cleanup:
> + csi_formatter_stop_stream(formatter, state, configured_streams);
> + return ret;
> +}
> +
> +static int csi_formatter_subdev_enable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> + struct device *dev = formatter->dev;
> + u64 sink_streams;
> + int ret;
> +
> + sink_streams = v4l2_subdev_state_xlate_streams(state,
> + CSI_FORMATTER_PAD_SOURCE,
> + CSI_FORMATTER_PAD_SINK,
> + &streams_mask);
> + if (!sink_streams || !streams_mask)
> + return -EINVAL;
> +
> + guard(mutex)(&formatter->lock);
> +
> + if (!formatter->enabled_streams) {
> + ret = pm_runtime_resume_and_get(formatter->dev);
> + if (ret < 0) {
> + dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = csi_formatter_start_stream(formatter, state, streams_mask);
> + if (ret)
> + goto err_runtime_put;
> +
> + ret = v4l2_subdev_enable_streams(formatter->csi_sd,
> + formatter->remote_pad,
> + sink_streams);
> + if (ret)
> + goto err_stop_stream;
> +
> + formatter->enabled_streams |= streams_mask;
> +
> + return 0;
> +
> +err_stop_stream:
> + csi_formatter_stop_stream(formatter, state, streams_mask);
> +err_runtime_put:
> + if (!formatter->enabled_streams)
> + pm_runtime_put(formatter->dev);
> + return ret;
> +}
> +
> +static int csi_formatter_subdev_disable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> + u64 sink_streams;
> + int ret;
> +
> + sink_streams = v4l2_subdev_state_xlate_streams(state,
> + CSI_FORMATTER_PAD_SOURCE,
> + CSI_FORMATTER_PAD_SINK,
> + &streams_mask);
> + if (!sink_streams || !streams_mask)
> + return -EINVAL;
> +
> + guard(mutex)(&formatter->lock);
> +
> + ret = v4l2_subdev_disable_streams(formatter->csi_sd, formatter->remote_pad,
> + sink_streams);
> + if (ret)
> + dev_err(formatter->dev, "Failed to disable streams: %d\n", ret);
> +
> + csi_formatter_stop_stream(formatter, state, streams_mask);
> +
> + formatter->enabled_streams &= ~streams_mask;
> +
> + if (!formatter->enabled_streams)
> + pm_runtime_put(formatter->dev);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_subdev_pad_ops formatter_subdev_pad_ops = {
> + .enum_mbus_code = csi_formatter_subdev_enum_mbus_code,
> + .get_fmt = v4l2_subdev_get_fmt,
> + .set_fmt = csi_formatter_subdev_set_fmt,
> + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
> + .set_routing = csi_formatter_subdev_set_routing,
> + .enable_streams = csi_formatter_subdev_enable_streams,
> + .disable_streams = csi_formatter_subdev_disable_streams,
> +};
> +
> +static const struct v4l2_subdev_ops formatter_subdev_ops = {
> + .pad = &formatter_subdev_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops formatter_internal_ops = {
> + .init_state = csi_formatter_subdev_init_state,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Media entity operations
> + */
> +
> +static const struct media_entity_operations formatter_entity_ops = {
> + .link_validate = v4l2_subdev_link_validate,
> + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
> +};
> +
> +static int csi_formatter_subdev_init(struct csi_formatter *formatter)
> +{
> + struct v4l2_subdev *sd = &formatter->sd;
> + int ret;
> +
> + v4l2_subdev_init(sd, &formatter_subdev_ops);
> +
> + snprintf(sd->name, sizeof(sd->name), "%s", dev_name(formatter->dev));
> + sd->internal_ops = &formatter_internal_ops;
> +
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> + V4L2_SUBDEV_FL_STREAMS;
> + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> + sd->entity.ops = &formatter_entity_ops;
> + sd->dev = formatter->dev;
> +
> + formatter->pads[CSI_FORMATTER_PAD_SINK].flags = MEDIA_PAD_FL_SINK
> + | MEDIA_PAD_FL_MUST_CONNECT;
> + formatter->pads[CSI_FORMATTER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +
> + ret = media_entity_pads_init(&sd->entity, CSI_FORMATTER_PAD_NUM,
> + formatter->pads);
> + if (ret) {
> + dev_err(formatter->dev, "Failed to init pads\n");
> + return ret;
> + }
> +
> + ret = v4l2_subdev_init_finalize(sd);
> + if (ret)
> + media_entity_cleanup(&sd->entity);
> +
> + return ret;
> +}
> +
> +static inline struct csi_formatter *
> +notifier_to_csi_formatter(struct v4l2_async_notifier *n)
> +{
> + return container_of(n, struct csi_formatter, notifier);
> +}
> +
> +static int csi_formatter_notify_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_connection *asc)
> +{
> + const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
> + | MEDIA_LNK_FL_ENABLED;
> + struct csi_formatter *formatter = notifier_to_csi_formatter(notifier);
> + struct v4l2_subdev *sdev = &formatter->sd;
> + struct media_pad *sink = &sdev->entity.pads[CSI_FORMATTER_PAD_SINK];
> + struct media_pad *remote_pad;
> + int ret;
> +
> + formatter->csi_sd = sd;
> +
> + dev_dbg(formatter->dev, "Bound subdev: %s pad\n", sd->name);
> +
> + ret = v4l2_create_fwnode_links_to_pad(sd, sink, link_flags);
> + if (ret < 0)
> + return ret;
> +
> + remote_pad = media_pad_remote_pad_first(sink);
> + if (!remote_pad) {
> + dev_err(formatter->dev, "Pipe not setup correctly\n");
> + return -EPIPE;
> + }
> + formatter->remote_pad = remote_pad->index;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_async_notifier_operations formatter_notify_ops = {
> + .bound = csi_formatter_notify_bound,
> +};
> +
> +static int csi_formatter_async_register(struct csi_formatter *formatter)
> +{
> + struct device *dev = formatter->dev;
> + struct v4l2_async_connection *asc;
> + int ret;
> +
> + struct fwnode_handle *ep __free(fwnode_handle) =
> + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (!ep)
> + return -ENOTCONN;
> +
> + v4l2_async_subdev_nf_init(&formatter->notifier, &formatter->sd);
> +
> + asc = v4l2_async_nf_add_fwnode_remote(&formatter->notifier, ep,
> + struct v4l2_async_connection);
> + if (IS_ERR(asc)) {
> + ret = PTR_ERR(asc);
> + goto err_cleanup_notifier;
> + }
> +
> + formatter->notifier.ops = &formatter_notify_ops;
> +
> + ret = v4l2_async_nf_register(&formatter->notifier);
> + if (ret)
> + goto err_cleanup_notifier;
> +
> + ret = v4l2_async_register_subdev(&formatter->sd);
> + if (ret)
> + goto err_unregister_notifier;
> +
> + return 0;
> +
> +err_unregister_notifier:
> + v4l2_async_nf_unregister(&formatter->notifier);
> +err_cleanup_notifier:
> + v4l2_async_nf_cleanup(&formatter->notifier);
> + return ret;
> +}
> +
> +static void csi_formatter_async_unregister(struct csi_formatter *formatter)
> +{
> + v4l2_async_unregister_subdev(&formatter->sd);
> + v4l2_async_nf_unregister(&formatter->notifier);
> + v4l2_async_nf_cleanup(&formatter->notifier);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Suspend/resume
> + */
> +
> +static int csi_formatter_runtime_suspend(struct device *dev)
> +{
> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> +
> + clk_disable_unprepare(formatter->clk);
> +
> + return 0;
> +}
> +
> +static int csi_formatter_runtime_resume(struct device *dev)
> +{
> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> +
> + return clk_prepare_enable(formatter->clk);
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(csi_formatter_pm_ops,
> + csi_formatter_runtime_suspend,
> + csi_formatter_runtime_resume, NULL);
> +
> +static int csi_formatter_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct csi_formatter *formatter;
> + u32 val;
> + int ret;
> +
> + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
> + if (!formatter)
> + return -ENOMEM;
> +
> + formatter->dev = dev;
> +
> + ret = devm_mutex_init(dev, &formatter->lock);
> + if (ret)
> + return ret;
> +
> + formatter->regs = syscon_node_to_regmap(dev->parent->of_node);
> + if (IS_ERR(formatter->regs))
> + return dev_err_probe(dev, PTR_ERR(formatter->regs),
> + "Failed to get csi formatter regmap\n");
> +
> + ret = of_property_read_u32(dev->of_node, "reg", &val);
> + if (ret < 0)
> + return dev_err_probe(dev, ret,
> + "Failed to get csi formatter reg property\n");
> +
> + formatter->reg_offset = val;
> +
> + formatter->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(formatter->clk))
> + return dev_err_probe(dev, PTR_ERR(formatter->clk),
> + "Failed to get pixel clock\n");
> +
> + ret = csi_formatter_subdev_init(formatter);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to initialize formatter subdev\n");
> +
> + platform_set_drvdata(pdev, &formatter->sd);
> +
> + /* Enable runtime PM. */
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + goto err_cleanup_subdev;
> +
> + ret = csi_formatter_async_register(formatter);
> + if (ret < 0) {
> + dev_err_probe(dev, ret, "Failed to register async subdevice\n");
> + goto err_cleanup_subdev;
> + }
> +
> + return 0;
> +
> +err_cleanup_subdev:
> + v4l2_subdev_cleanup(&formatter->sd);
> + media_entity_cleanup(&formatter->sd.entity);
> + return ret;
> +}
> +
> +static void csi_formatter_remove(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *sd = platform_get_drvdata(pdev);
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> +
> + csi_formatter_async_unregister(formatter);
> +
> + v4l2_subdev_cleanup(&formatter->sd);
> + media_entity_cleanup(&formatter->sd.entity);
> +}
> +
> +static const struct of_device_id csi_formatter_of_match[] = {
> + { .compatible = "fsl,imx95-csi-formatter" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, csi_formatter_of_match);
> +
> +static struct platform_driver csi_formatter_device_driver = {
> + .driver = {
> + .name = "csi-pixel-formatter",
> + .of_match_table = csi_formatter_of_match,
> + .pm = pm_ptr(&csi_formatter_pm_ops),
> + },
> + .probe = csi_formatter_probe,
> + .remove = csi_formatter_remove,
> +};
> +
> +module_platform_driver(csi_formatter_device_driver);
> +
> +MODULE_AUTHOR("NXP Semiconductor, Inc.");
> +MODULE_DESCRIPTION("NXP i.MX95 CSI Pixel Formatter driver");
> +MODULE_LICENSE("GPL");
>
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v2 1/4] regulator: dt-bindings: Add fan53555 allowed modes
From: sashiko-bot @ 2026-07-20 10:12 UTC (permalink / raw)
To: Victor Krawiec; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260720100336.62004-2-victor.krawiec@arturia.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The regulator driver lacks the `.of_map_mode` callback required to parse the newly added `regulator-initial-mode` device tree property.
- [High] The patch is missing the board and script modifications explicitly listed in the commit message, and includes raw `git status` output.
- [Medium] The schema fails to constrain the `regulator-allowed-modes` property despite the commit explicitly claiming to add allowed modes.
Pre-existing issues:
- [High] The driver's `fan53555_set_mode()` function accidentally writes to the voltage register (`vol_reg`) instead of the mode register (`mode_reg`) when switching to `REGULATOR_MODE_NORMAL`.
--
--- Patch [1]: [PATCH v2 1/4] regulator: dt-bindings: Add fan53555 allowed modes ---
commit 6198556f842d1d3f4521256414c33ff637d763cb
Author: Victor Krawiec <victor.krawiec@arturia.com>
regulator: dt-bindings: Add fan53555 allowed modes
This commit adds device tree binding definitions for the FAN53555 regulator's
operational modes (Auto-PFM and Forced PWM). It introduces a new header file
with mode constants and updates the YAML schema to allow specifying an initial
mode.
> Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml # new file:
> include/dt-bindings/regulator/fcs,fan53555-regulator.h #
> arch/arm64/boot/dts/rockchip/rk3399-drumfreak.dts #
> arch/arm64/boot/simple-boot.img # arch/arm64/boot/simple-kernel.img #
> arch/arm64/configs/drumfreak_defconfig #
> scripts/arturia_simple_boot_image.sh #
[Severity: High]
Are these files intentionally missing from the diff? The commit message lists
several board and script files, as well as driver implementation details, but
they are not included in the patch. It looks like accidental output from git
status was included in the commit message, and the patch submission might be
incomplete.
> diff --git a/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml b/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml
> index 69bae90fc4b28..9a18891f721e6 100644
> --- a/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml
> +++ b/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml
> @@ -48,6 +48,12 @@ properties:
> VSEL0 register. When this pin is HIGH, VOUT is set by the VSEL1 register.
> maxItems: 1
>
> + regulator-initial-mode:
> + enum:
> + [ 1, 2 ]
[Severity: Medium]
The commit message mentions adding allowed modes, but the schema only constrains
regulator-initial-mode. Should regulator-allowed-modes also be added to the
schema with constraints?
Without it, dt_binding_check will permit any values for regulator-allowed-modes
since it falls back to the generic uint32-array schema.
> + description:
> + Defined in include/dt-bindings/regulator/fcs,fan53555-regulator.h
[ ... ]
While reviewing the proposed bindings, I noticed a couple of issues in the
corresponding driver implementation in drivers/regulator/fan53555.c.
drivers/regulator/fan53555.c:fan53555_regulator_register() {
...
rdesc->ops = &fan53555_regulator_ops;
rdesc->type = REGULATOR_VOLTAGE;
...
}
[Severity: High]
Does the driver need an .of_map_mode callback assigned to the regulator_desc?
Without this callback, the regulator core cannot parse regulator-initial-mode
during probe, and will silently ignore the property with a warning.
This is a pre-existing issue, but while reviewing the mode switching logic
that these bindings interact with:
drivers/regulator/fan53555.c:fan53555_set_mode() {
...
case REGULATOR_MODE_NORMAL:
regmap_update_bits(rdev->regmap, di->vol_reg, di->mode_mask, 0);
break;
...
}
[Severity: High]
Does this mistakenly write to di->vol_reg instead of di->mode_reg?
On variants like the TCS4525 where di->mode_mask is BIT(7) or BIT(6), clearing
this bit in the voltage register clears VSEL_BUCK_EN. This would unexpectedly
turn off the regulator instead of changing its mode.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720100336.62004-1-victor.krawiec@arturia.com?part=1
^ permalink raw reply
* Re: [PATCH v2 2/4] arm64: dts: rk3399-rock-4c-plus: Use device tree bindings constants for SYR828 regulator initial mode
From: sashiko-bot @ 2026-07-20 10:11 UTC (permalink / raw)
To: Victor Krawiec; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260720100336.62004-3-victor.krawiec@arturia.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The fan53555 regulator driver lacks an `of_map_mode` callback, causing the `regulator-initial-mode` device tree property to be ignored.
--
--- Patch [2]: [PATCH v2 2/4] arm64: dts: rk3399-rock-4c-plus: Use device tree bindings constants for SYR828 regulator initial mode ---
commit bd8e5b9ed62ff2e741725d000597a403df903434
Author: Victor Krawiec <victor.krawiec@arturia.com>
arm64: dts: rk3399-rock-4c-plus: Use device tree bindings constants for SYR828 regulator initial mode
This patch updates the device tree to prefer using a bindings constant
over a hardcoded value for the regulator initial mode.
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
> --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
> +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
> @@ -448,7 +449,7 @@ vdd_gpu: regulator@41 {
> regulator-always-on;
> regulator-boot-on;
> vin-supply = <&vcc5v0_sys>;
> - regulator-initial-mode = <1>; /* 1:force PWM 2:auto */
> + regulator-initial-mode = <FAN53555_REGULATOR_MODE_FORCE_PWM>;
[Severity: High]
This is a pre-existing issue, but does the underlying fan53555 regulator
driver actually support mapping this property?
Looking at the driver initialization in fan53555_regulator_register(), the
regulator_desc lacks an of_map_mode callback:
drivers/regulator/fan53555.c:fan53555_regulator_register() {
...
rdesc->ops = &fan53555_regulator_ops;
rdesc->type = REGULATOR_VOLTAGE;
...
}
When the regulator core parses this device tree property, it requires that
callback to translate the value:
drivers/regulator/of_regulator.c:of_get_regulation_constraints() {
...
if (desc && desc->of_map_mode) {
mode = desc->of_map_mode(pval);
if (mode == REGULATOR_MODE_INVALID)
pr_err("%pOFn: invalid mode %u\n", np, pval);
else
constraints->initial_mode = mode;
} else {
pr_warn("%pOFn: mapping for mode %d not defined\n",
np, pval);
}
...
}
Without the callback, constraints->initial_mode is never populated, a warning
is printed, and the hardware defaults to its reset mode (Auto-PFM) instead of
the requested Forced PWM mode. Could failing to apply the Forced PWM mode lead
to hardware instability or crashes under light load conditions?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720100336.62004-1-victor.krawiec@arturia.com?part=2
^ permalink raw reply
* Re: [PATCH v5 01/17] spi: dt-bindings: add spi-max-post-config-frequency-hz property
From: Krzysztof Kozlowski @ 2026-07-20 10:09 UTC (permalink / raw)
To: Santhosh Kumar K
Cc: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
vigneshr, pratyush, mwalle, takahiro.kuwano, linux-spi,
devicetree, linux-kernel, linux-mtd, praneeth, u-kumar1, a-dutta,
Conor Dooley
In-Reply-To: <d6b3c768-bff2-40b1-b767-f17219281fb7@ti.com>
On 20/07/2026 10:48, Santhosh Kumar K wrote:
>
>
> On 20/07/26 13:10, Krzysztof Kozlowski wrote:
>> On 20/07/2026 09:38, Santhosh Kumar K wrote:
>>>>> diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
>>>>> index 880a9f624566..12211f8c9f7d 100644
>>>>> --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
>>>>> +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
>>>>> @@ -45,6 +45,11 @@ properties:
>>>>> description:
>>>>> Maximum SPI clocking speed of the device in Hz.
>>>>>
>>>>> + spi-max-post-config-frequency-hz:
>>>>> + description:
>>>>> + Maximum SPI clock frequency in Hz achievable after controller-side
>>>>> + configuration.
>>>>
>>>> How did you implement my feedback?
>>>
>>> Three points from your feedback:
>>>
>>> 1. -hz suffix: added
>>>
>>> 2. Array property: I still lean towards keeping this as a scalar for
>>> now, as we don't have a concrete use case for multiple values today, and
>>> supporting two frequencies should be sufficient for the foreseeable
>>> future. I had responded to this in the v4 discussion as well, you may
>>> have missed that response.
>>>
>>> 3. maxItems: Since the property remains a scalar, I have not added maxItems.
>>
>> So how did you make this property scalar?
>
> The -hz suffix constraints the property to a uint32 scalar through the
> DT schema framework's property-units.yaml.
Hm, where? I completely missed that, can you point me to the specific
line? I see clearly that it is not a scalar.
>
> Adding an explicit $ref or maxItems for a unit-suffixed property is
> rejected by dtbs_check with:
> - "Standard unit suffix properties don't need a type $ref"
This I did not propose - the type is fixed.
> - "Scalar properties should not have array keywords"
Cannot reproduce it.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v13 3/3] media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
From: sashiko-bot @ 2026-07-20 10:08 UTC (permalink / raw)
To: guoniu.zhou; +Cc: Frank.Li, imx, devicetree, conor+dt, media-ci, robh
In-Reply-To: <20260720-csi_formatter-v13-3-4dc9a80e4cfd@oss.nxp.com>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Returning early on `get_frame_desc` failure in `csi_formatter_stop_stream()` leaves hardware streams enabled.
- [High] Missing `.unbind` in async notifier leaves a dangling `csi_sd` pointer.
- [Medium] Functions mix `goto`-based error handling with scope-based cleanup (`guard()` and `__free()`), violating kernel cleanup guidelines.
- [Medium] Propagating downstream disable errors causes V4L2 core state desynchronization and unbalanced PM runtime puts.
- [Medium] PM runtime references and clocks are leaked if the driver is removed while streams are active.
- [Medium] `set_fmt` fails with `-EINVAL` for valid, unrouted streams.
--
--- Patch [3]: [PATCH v13 3/3] media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver ---
commit 72e988a703f84a35fe9af4531ed1cd3a2a57330d
Author: Guoniu Zhou <guoniu.zhou@nxp.com>
Subject: media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
This commit introduces the V4L2 subdevice driver for the i.MX95 CSI
pixel formatter. It unpacks pixels received from the CSI-2 interface and
reformats them to meet pixel link requirements.
> diff --git a/drivers/media/platform/nxp/imx95-csi-formatter.c b/drivers/media/platform/nxp/imx95-csi-formatter.c
> new file mode 100644
> index 0000000000000..b0e8e753e94aa
> --- /dev/null
> +++ b/drivers/media/platform/nxp/imx95-csi-formatter.c
[ ... ]
> +static int csi_formatter_subdev_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *sdformat)
> +{
[ ... ]
> + /* Propagate the format from sink stream to source stream */
> + fmt = v4l2_subdev_state_get_opposite_stream_format(sd_state, sdformat->pad,
> + sdformat->stream);
> + if (!fmt)
> + return -EINVAL;
[Severity: Medium]
Will this fail with -EINVAL for valid, unrouted streams?
If userspace attempts to configure formats before establishing routes,
v4l2_subdev_state_get_opposite_stream_format() returns NULL. Does the
V4L2 API permit setting formats on unrouted streams, and should we
silently ignore the propagation instead of returning an error?
> +
> + *fmt = sdformat->format;
> +
> + return 0;
> +}
[ ... ]
> +static void csi_formatter_stop_stream(struct csi_formatter *formatter,
> + struct v4l2_subdev_state *state,
> + u64 stream_mask)
> +{
> + const struct csi_formatter_pix_format *pix_fmt;
> + struct v4l2_mbus_frame_desc fd = {};
> + struct v4l2_subdev_route *route;
> + struct v4l2_mbus_framefmt *fmt;
> + unsigned int reg;
> + unsigned int mask;
> + int vc;
> + int ret;
> +
> + ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
> + formatter->remote_pad, &fd);
> + if (ret < 0 && ret != -ENOIOCTLCMD) {
> + dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
> + return;
> + }
[Severity: High]
Does returning early here leave the hardware streams enabled?
If get_frame_desc fails during stream shutdown (for instance, if the
source subdev is suspended or in a reset state), the function returns
early and bypasses the logic that clears the CSI_VC_PIXEL_DATA_TYPE
register bits.
Could a subsequent stream start on the same VC set multiple data type
bits simultaneously, potentially hanging the hardware or corrupting
the pipeline?
[ ... ]
> +static int csi_formatter_subdev_enable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> + struct device *dev = formatter->dev;
> + u64 sink_streams;
> + int ret;
> +
> + sink_streams = v4l2_subdev_state_xlate_streams(state,
> + CSI_FORMATTER_PAD_SOURCE,
> + CSI_FORMATTER_PAD_SINK,
> + &streams_mask);
> + if (!sink_streams || !streams_mask)
> + return -EINVAL;
> +
> + guard(mutex)(&formatter->lock);
> +
> + if (!formatter->enabled_streams) {
> + ret = pm_runtime_resume_and_get(formatter->dev);
> + if (ret < 0) {
> + dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = csi_formatter_start_stream(formatter, state, streams_mask);
> + if (ret)
> + goto err_runtime_put;
> +
> + ret = v4l2_subdev_enable_streams(formatter->csi_sd,
> + formatter->remote_pad,
> + sink_streams);
> + if (ret)
> + goto err_stop_stream;
[Severity: Medium]
Is it safe to mix guard(mutex) with goto-based error handling labels?
The kernel cleanup guidelines recommend against mixing scope-based cleanup
helpers with goto labels in the same function. Could this create confusing
ownership semantics or risk bugs if this function is modified in the future?
> +
> + formatter->enabled_streams |= streams_mask;
> +
> + return 0;
> +
> +err_stop_stream:
> + csi_formatter_stop_stream(formatter, state, streams_mask);
> +err_runtime_put:
> + if (!formatter->enabled_streams)
> + pm_runtime_put(formatter->dev);
> + return ret;
> +}
> +
> +static int csi_formatter_subdev_disable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> + u64 sink_streams;
> + int ret;
> +
> + sink_streams = v4l2_subdev_state_xlate_streams(state,
> + CSI_FORMATTER_PAD_SOURCE,
> + CSI_FORMATTER_PAD_SINK,
> + &streams_mask);
> + if (!sink_streams || !streams_mask)
> + return -EINVAL;
> +
> + guard(mutex)(&formatter->lock);
> +
> + ret = v4l2_subdev_disable_streams(formatter->csi_sd, formatter->remote_pad,
> + sink_streams);
> + if (ret)
> + dev_err(formatter->dev, "Failed to disable streams: %d\n", ret);
> +
> + csi_formatter_stop_stream(formatter, state, streams_mask);
> +
> + formatter->enabled_streams &= ~streams_mask;
> +
> + if (!formatter->enabled_streams)
> + pm_runtime_put(formatter->dev);
> +
> + return ret;
[Severity: Medium]
Will propagating the downstream disable error cause V4L2 core state
desynchronization and unbalanced PM runtime puts?
If v4l2_subdev_disable_streams() fails, the driver performs its local state
cleanup and drops the PM reference, but returns the error to the V4L2 core.
The V4L2 core might abort and leave the stream in active_state.
If userspace attempts to disable the stream again, could the driver's
disable routine run again, performing a second pm_runtime_put() and
underflowing the usage counter?
> +}
[ ... ]
> +static int csi_formatter_notify_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_connection *asc)
> +{
> + const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
> + | MEDIA_LNK_FL_ENABLED;
> + struct csi_formatter *formatter = notifier_to_csi_formatter(notifier);
> + struct v4l2_subdev *sdev = &formatter->sd;
> + struct media_pad *sink = &sdev->entity.pads[CSI_FORMATTER_PAD_SINK];
> + struct media_pad *remote_pad;
> + int ret;
> +
> + formatter->csi_sd = sd;
[Severity: High]
Does this leave a dangling pointer when the upstream subdevice is unbound?
The csi_sd pointer is cached here, but there appears to be no unbind
callback for the async notifier. If the bound upstream subdevice driver
is unloaded or unbound via sysfs, the V4L2 async core unbinds the connection.
Without an unbind callback to set formatter->csi_sd to NULL, if userspace
subsequently shuts down the stream, could csi_formatter_subdev_disable_streams()
dereference the csi_sd pointer after it is freed?
> +
> + dev_dbg(formatter->dev, "Bound subdev: %s pad\n", sd->name);
[ ... ]
> +static int csi_formatter_async_register(struct csi_formatter *formatter)
> +{
> + struct device *dev = formatter->dev;
> + struct v4l2_async_connection *asc;
> + int ret;
> +
> + struct fwnode_handle *ep __free(fwnode_handle) =
> + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (!ep)
> + return -ENOTCONN;
> +
> + v4l2_async_subdev_nf_init(&formatter->notifier, &formatter->sd);
> +
> + asc = v4l2_async_nf_add_fwnode_remote(&formatter->notifier, ep,
> + struct v4l2_async_connection);
> + if (IS_ERR(asc)) {
> + ret = PTR_ERR(asc);
> + goto err_cleanup_notifier;
> + }
> +
> + formatter->notifier.ops = &formatter_notify_ops;
> +
> + ret = v4l2_async_nf_register(&formatter->notifier);
> + if (ret)
> + goto err_cleanup_notifier;
> +
> + ret = v4l2_async_register_subdev(&formatter->sd);
> + if (ret)
> + goto err_unregister_notifier;
> +
> + return 0;
> +
> +err_unregister_notifier:
> + v4l2_async_nf_unregister(&formatter->notifier);
> +err_cleanup_notifier:
> + v4l2_async_nf_cleanup(&formatter->notifier);
> + return ret;
[Severity: Medium]
Is it safe to mix __free(fwnode_handle) with goto-based error handling
labels in this function?
The subsystem cleanup guidelines recommend either converting all resources
that need goto cleanup to scope-based cleanup, or converting none of them.
> +}
[ ... ]
> +static void csi_formatter_remove(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *sd = platform_get_drvdata(pdev);
> + struct csi_formatter *formatter = sd_to_formatter(sd);
> +
> + csi_formatter_async_unregister(formatter);
> +
> + v4l2_subdev_cleanup(&formatter->sd);
> + media_entity_cleanup(&formatter->sd.entity);
> +}
[Severity: Medium]
Are PM runtime references and clocks leaked if the driver is removed
while streams are active?
If the driver is unbound via sysfs while streams are active, this remove
function is called. It does not ensure streams are stopped or that the PM
runtime usage counter is balanced.
Since the usage counter might remain greater than zero when
devm_pm_runtime_enable disables runtime PM, could the device fail to
suspend and leave hardware clocks permanently enabled?
> +
> +static const struct of_device_id csi_formatter_of_match[] = {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-csi_formatter-v13-0-4dc9a80e4cfd@oss.nxp.com?part=3
^ permalink raw reply
* Re: [PATCH] arm64: dts: qcom: sc8280xp: Flatten usb controller nodes
From: Konrad Dybcio @ 2026-07-20 10:07 UTC (permalink / raw)
To: Xilin Wu, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Manivannan Sadhasivam,
Qiang Yu
Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <b461c125-947b-4e06-b502-abb354e0cba6@oss.qualcomm.com>
On 7/1/26 6:12 PM, Konrad Dybcio wrote:
> On 7/1/26 6:04 PM, Xilin Wu wrote:
>> On 7/1/2026 11:10 PM, Konrad Dybcio wrote:
>>> On 5/7/26 4:29 PM, Xilin Wu wrote:
>>>> Flatten usb controller nodes and update to using latest bindings
>>>> and flattened driver approach.
>>>>
>>>> Signed-off-by: Xilin Wu <sophon@radxa.com>
>>>> ---
>>>
>>> I tested this on a CRD and an x13s, both work fine across suspend.
>>>
>>> Turned out in my local experiments I made a typo in the name of the
>>> interrupt that was carried over to the big node..
>>>
>>> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>> Tested-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> # X13s, CRD8280
>>>
>>> Konrad
>>>
>>
>> Unfortunately as I mentioned on IRC [1], this actually causes random NoC timeout on reboot, which seems to be related to pcie.
>>
>> [1] https://oftc.catirclogs.org/linux-msm/2026-06-11#35405057;
>
> Ah, you mentioned this:
>
> https://gist.github.com/strongtz/50bbd71a9f41d0d1aa9849b8cf1dce21
>
> This is an attempt to read CONFIG_SPACE+0x0.. I don't really know..
Could you try adding something like this
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index b123da16b63b..5e08cb239e78 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -43,6 +43,7 @@ int noinline pci_bus_read_config_##size \
if (PCI_##size##_BAD) \
return PCIBIOS_BAD_REGISTER_NUMBER; \
\
+ dev_err(&bus->dev, "Reading configspace+0x%x as %s\n", pos, #size); \
pci_lock_config(flags); \
res = bus->ops->read(bus, devfn, pos, len, &data); \
if (res)
(perhaps with a WARN_ON(1) to get a stack trace and only print for the
crashing reg)?
FWIW I can't repro on a CRD8280
Konrad
^ permalink raw reply related
* [PATCH v3 18/18] clk: mediatek: Add MT8189 ufs clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 ufs clock controller,
which provides clock gate control for Universal Flash Storage.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 12 +++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-ufs.c | 133 ++++++++++++++++++++++++++++++++++
3 files changed, 146 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 919a916f1f4f..34a270a377cc 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -882,6 +882,18 @@ config COMMON_CLK_MT8189_SCP
management for SCP-related features, ensuring proper clock
distribution and gating for power efficiency and functionality.
+config COMMON_CLK_MT8189_UFS
+ tristate "Clock driver for MediaTek MT8189 ufs"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this to support the clock management for the Universal Flash
+ Storage (UFS) interface on MediaTek MT8189 SoCs. This includes
+ clock sources, dividers, and gates that are specific to the UFS
+ feature of the MT8189 platform. It is recommended to enable this
+ option if the system includes a UFS device that relies on the MT8189
+ SoC for clock management.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index a3a93a16b369..1aa9f4265225 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -130,6 +130,7 @@ obj-$(CONFIG_COMMON_CLK_MT8189_DBGAO) += clk-mt8189-dbgao.o
obj-$(CONFIG_COMMON_CLK_MT8189_DVFSRC) += clk-mt8189-dvfsrc.o
obj-$(CONFIG_COMMON_CLK_MT8189_IIC) += clk-mt8189-iic.o
obj-$(CONFIG_COMMON_CLK_MT8189_SCP) += clk-mt8189-scp.o
+obj-$(CONFIG_COMMON_CLK_MT8189_UFS) += clk-mt8189-ufs.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-ufs.c b/drivers/clk/mediatek/clk-mt8189-ufs.c
new file mode 100644
index 000000000000..85afab04420f
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-ufs.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+#include <dt-bindings/reset/mediatek,mt8189-resets.h>
+
+#define MT8189_UFSCFG_AO_RST0_SET_OFFSET 0x48
+#define MT8189_UFSCFG_PDN_RST0_SET_OFFSET 0x48
+
+static const struct mtk_gate_regs ufscfg_ao_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_AO_REG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ufscfg_ao_reg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate ufscfg_ao_reg_clks[] = {
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_UNIPRO_TX_SYM,
+ "ufscfg_ao_unipro_tx_sym", "clk26m", 1),
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM0,
+ "ufscfg_ao_unipro_rx_sym0", "clk26m", 2),
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM1,
+ "ufscfg_ao_unipro_rx_sym1", "clk26m", 3),
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_UNIPRO_SYS,
+ "ufscfg_ao_unipro_sys", "ufs_sel", 4),
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_U_SAP_CFG,
+ "ufscfg_ao_u_sap_cfg", "clk26m", 5),
+ GATE_UFSCFG_AO_REG(CLK_UFSCFG_AO_REG_U_PHY_TOP_AHB_S_BUS,
+ "ufscfg_ao_u_phy_ahb_s_bus", "axi_u_sel", 6),
+};
+
+static u16 ufscfg_ao_rst_ofs[] = {
+ MT8189_UFSCFG_AO_RST0_SET_OFFSET,
+};
+
+static u16 ufscfg_ao_rst_idx_map[] = {
+ [MT8189_UFSAO_RST_UFS_MPHY] = 8,
+};
+
+static const struct mtk_clk_rst_desc ufscfg_ao_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = ufscfg_ao_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(ufscfg_ao_rst_ofs),
+ .rst_idx_map = ufscfg_ao_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(ufscfg_ao_rst_idx_map),
+};
+
+static const struct mtk_clk_desc ufscfg_ao_reg_mcd = {
+ .clks = ufscfg_ao_reg_clks,
+ .num_clks = ARRAY_SIZE(ufscfg_ao_reg_clks),
+ .rst_desc = &ufscfg_ao_rst_desc,
+};
+
+static const struct mtk_gate_regs ufscfg_pdn_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_PDN_REG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ufscfg_pdn_reg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate ufscfg_pdn_reg_clks[] = {
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_UFS,
+ "ufscfg_ufshci_ufs", "ufs_sel", 0),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_AES,
+ "ufscfg_ufshci_aes", "aes_ufsfde_sel", 1),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AHB,
+ "ufscfg_ufshci_u_ahb", "axi_u_sel", 3),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AXI,
+ "ufscfg_ufshci_u_axi", "mem_sub_u_sel", 5),
+};
+
+static u16 ufscfg_pdn_rst_ofs[] = {
+ MT8189_UFSCFG_PDN_RST0_SET_OFFSET,
+};
+
+static u16 ufscfg_pdn_rst_idx_map[] = {
+ [MT8189_UFSPDN_RST_UFS_UNIPRO] = 0,
+ [MT8189_UFSPDN_RST_UFS_CRYPTO] = 1,
+ [MT8189_UFSPDN_RST_UFS_HCI] = 2,
+};
+
+static const struct mtk_clk_rst_desc ufscfg_pdn_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = ufscfg_pdn_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(ufscfg_pdn_rst_ofs),
+ .rst_idx_map = ufscfg_pdn_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(ufscfg_pdn_rst_idx_map),
+};
+
+static const struct mtk_clk_desc ufscfg_pdn_reg_mcd = {
+ .clks = ufscfg_pdn_reg_clks,
+ .num_clks = ARRAY_SIZE(ufscfg_pdn_reg_clks),
+ .rst_desc = &ufscfg_pdn_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8189_ufs[] = {
+ { .compatible = "mediatek,mt8189-ufscfg-ao", .data = &ufscfg_ao_reg_mcd },
+ { .compatible = "mediatek,mt8189-ufscfg-pdn", .data = &ufscfg_pdn_reg_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_ufs);
+
+static struct platform_driver clk_mt8189_ufs_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-ufs",
+ .of_match_table = of_match_clk_mt8189_ufs,
+ },
+};
+module_platform_driver(clk_mt8189_ufs_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 ufs clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 17/18] clk: mediatek: Add MT8189 scp clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 scp clock controller,
which provides clock gate control for System Control Processor.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 10 +++++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-scp.c | 77 +++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index bba631138b07..919a916f1f4f 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -872,6 +872,16 @@ config COMMON_CLK_MT8189_IIC
the MT8189 chipset, improving the overall performance and power
efficiency of the device.
+config COMMON_CLK_MT8189_SCP
+ tristate "Clock driver for MediaTek MT8189 scp"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this to support the clock framework for the System Control
+ Processor (SCP) in the MediaTek MT8189 SoC. This includes clock
+ management for SCP-related features, ensuring proper clock
+ distribution and gating for power efficiency and functionality.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index bfc075023d9b..a3a93a16b369 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -129,6 +129,7 @@ obj-$(CONFIG_COMMON_CLK_MT8189_BUS) += clk-mt8189-bus.o
obj-$(CONFIG_COMMON_CLK_MT8189_DBGAO) += clk-mt8189-dbgao.o
obj-$(CONFIG_COMMON_CLK_MT8189_DVFSRC) += clk-mt8189-dvfsrc.o
obj-$(CONFIG_COMMON_CLK_MT8189_IIC) += clk-mt8189-iic.o
+obj-$(CONFIG_COMMON_CLK_MT8189_SCP) += clk-mt8189-scp.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-scp.c b/drivers/clk/mediatek/clk-mt8189-scp.c
new file mode 100644
index 000000000000..75197cd98b52
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-scp.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs scp_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_SCP(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &scp_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate scp_clks[] = {
+ GATE_SCP(CLK_SCP_SET_SPI0, "scp_set_spi0", "clk26m", 0),
+ GATE_SCP(CLK_SCP_SET_SPI1, "scp_set_spi1", "clk26m", 1),
+};
+
+static const struct mtk_clk_desc scp_mcd = {
+ .clks = scp_clks,
+ .num_clks = ARRAY_SIZE(scp_clks),
+};
+
+static const struct mtk_gate_regs scp_iic_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_SCP_IIC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &scp_iic_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate scp_iic_clks[] = {
+ GATE_SCP_IIC(CLK_SCP_IIC_I2C0_W1S, "scp_iic_i2c0_w1s", "vlp_scp_iic_sel", 0),
+ GATE_SCP_IIC(CLK_SCP_IIC_I2C1_W1S, "scp_iic_i2c1_w1s", "vlp_scp_iic_sel", 1),
+};
+
+static const struct mtk_clk_desc scp_iic_mcd = {
+ .clks = scp_iic_clks,
+ .num_clks = ARRAY_SIZE(scp_iic_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_scp[] = {
+ { .compatible = "mediatek,mt8189-scp-clk", .data = &scp_mcd },
+ { .compatible = "mediatek,mt8189-scp-i2c-clk", .data = &scp_iic_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_scp);
+
+static struct platform_driver clk_mt8189_scp_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-scp",
+ .of_match_table = of_match_clk_mt8189_scp,
+ },
+};
+module_platform_driver(clk_mt8189_scp_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 scp clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 16/18] clk: mediatek: Add MT8189 i2c clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 i2c clock controller,
which provides clock gate control for i2c.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 13 ++++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-iic.c | 122 ++++++++++++++++++++++++++++++++++
3 files changed, 136 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 245d3b83b5d3..bba631138b07 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -859,6 +859,19 @@ config COMMON_CLK_MT8189_DVFSRC
vcore dvfs clocks. If you want to control its clocks, say Y or M
to include this driver in your kernel build.
+config COMMON_CLK_MT8189_IIC
+ tristate "Clock driver for MediaTek MT8189 iic"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this option to support the clock framework for MediaTek MT8189
+ integrated circuits (iic). This driver is responsible for managing
+ clock sources, dividers, and gates specifically designed for MT8189
+ SoCs. Enabling this driver ensures that the system can correctly
+ manage clock frequencies and power for various components within
+ the MT8189 chipset, improving the overall performance and power
+ efficiency of the device.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 4dbfc9ac83ba..bfc075023d9b 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -128,6 +128,7 @@ obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o
obj-$(CONFIG_COMMON_CLK_MT8189_BUS) += clk-mt8189-bus.o
obj-$(CONFIG_COMMON_CLK_MT8189_DBGAO) += clk-mt8189-dbgao.o
obj-$(CONFIG_COMMON_CLK_MT8189_DVFSRC) += clk-mt8189-dvfsrc.o
+obj-$(CONFIG_COMMON_CLK_MT8189_IIC) += clk-mt8189-iic.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-iic.c b/drivers/clk/mediatek/clk-mt8189-iic.c
new file mode 100644
index 000000000000..80a01706791a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-iic.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs impe_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMPE(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &impe_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_OPS_PARENT_ENABLE)
+
+static const struct mtk_gate impe_clks[] = {
+ GATE_IMPE(CLK_IMPE_I2C0, "impe_i2c0", "i2c_sel", 0),
+ GATE_IMPE(CLK_IMPE_I2C1, "impe_i2c1", "i2c_sel", 1),
+};
+
+static const struct mtk_clk_desc impe_mcd = {
+ .clks = impe_clks,
+ .num_clks = ARRAY_SIZE(impe_clks),
+};
+
+static const struct mtk_gate_regs impen_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMPEN(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &impen_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_OPS_PARENT_ENABLE)
+
+static const struct mtk_gate impen_clks[] = {
+ GATE_IMPEN(CLK_IMPEN_I2C7, "impen_i2c7", "i2c_sel", 0),
+ GATE_IMPEN(CLK_IMPEN_I2C8, "impen_i2c8", "i2c_sel", 1),
+};
+
+static const struct mtk_clk_desc impen_mcd = {
+ .clks = impen_clks,
+ .num_clks = ARRAY_SIZE(impen_clks),
+};
+
+static const struct mtk_gate_regs imps_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMPS(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &imps_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_OPS_PARENT_ENABLE)
+
+static const struct mtk_gate imps_clks[] = {
+ GATE_IMPS(CLK_IMPS_I2C3, "imps_i2c3", "i2c_sel", 0),
+ GATE_IMPS(CLK_IMPS_I2C4, "imps_i2c4", "i2c_sel", 1),
+ GATE_IMPS(CLK_IMPS_I2C5, "imps_i2c5", "i2c_sel", 2),
+ GATE_IMPS(CLK_IMPS_I2C6, "imps_i2c6", "i2c_sel", 3),
+};
+
+static const struct mtk_clk_desc imps_mcd = {
+ .clks = imps_clks,
+ .num_clks = ARRAY_SIZE(imps_clks),
+};
+
+static const struct mtk_gate_regs impws_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMPWS(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &impws_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_OPS_PARENT_ENABLE)
+
+static const struct mtk_gate impws_clks[] = {
+ GATE_IMPWS(CLK_IMPWS_I2C2, "impws_i2c2", "i2c_sel", 0),
+};
+
+static const struct mtk_clk_desc impws_mcd = {
+ .clks = impws_clks,
+ .num_clks = ARRAY_SIZE(impws_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_iic[] = {
+ { .compatible = "mediatek,mt8189-iic-wrap-e", .data = &impe_mcd },
+ { .compatible = "mediatek,mt8189-iic-wrap-en", .data = &impen_mcd },
+ { .compatible = "mediatek,mt8189-iic-wrap-s", .data = &imps_mcd },
+ { .compatible = "mediatek,mt8189-iic-wrap-ws", .data = &impws_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_iic);
+
+static struct platform_driver clk_mt8189_iic_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-iic",
+ .of_match_table = of_match_clk_mt8189_iic,
+ },
+};
+module_platform_driver(clk_mt8189_iic_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 iic clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 15/18] clk: mediatek: Add MT8189 dvfsrc clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 dvfsrc clock controller,
which provides clock gate control for dram dvfs.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 10 ++++++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-dvfsrc.c | 58 ++++++++++++++++++++++++++++++++
3 files changed, 69 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 635b0109ec07..245d3b83b5d3 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -849,6 +849,16 @@ config COMMON_CLK_MT8189_DBGAO
vcore debug system clocks. If you want to control its clocks, say Y or M
to include this driver in your kernel build.
+config COMMON_CLK_MT8189_DVFSRC
+ tristate "Clock driver for MediaTek MT8189 dvfsrc"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this to support the clock management for the dvfsrc
+ on MediaTek MT8189 SoCs. This includes enabling and disabling
+ vcore dvfs clocks. If you want to control its clocks, say Y or M
+ to include this driver in your kernel build.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 6ab6df7ebf2a..4dbfc9ac83ba 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -127,6 +127,7 @@ obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o
clk-mt8189-vlpckgen.o clk-mt8189-vlpcfg.o
obj-$(CONFIG_COMMON_CLK_MT8189_BUS) += clk-mt8189-bus.o
obj-$(CONFIG_COMMON_CLK_MT8189_DBGAO) += clk-mt8189-dbgao.o
+obj-$(CONFIG_COMMON_CLK_MT8189_DVFSRC) += clk-mt8189-dvfsrc.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-dvfsrc.c b/drivers/clk/mediatek/clk-mt8189-dvfsrc.c
new file mode 100644
index 000000000000..37b81dc0b882
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-dvfsrc.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs dvfsrc_top_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_DVFSRC_TOP_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &dvfsrc_top_cg_regs, _shift, \
+ &mtk_clk_gate_ops_no_setclr_inv, _flags)
+
+static const struct mtk_gate dvfsrc_top_clks[] = {
+ GATE_DVFSRC_TOP_FLAGS(CLK_DVFSRC_TOP_DVFSRC_EN, "dvfsrc_dvfsrc_en",
+ "clk26m", 0, CLK_IS_CRITICAL),
+};
+
+static const struct mtk_clk_desc dvfsrc_top_mcd = {
+ .clks = dvfsrc_top_clks,
+ .num_clks = ARRAY_SIZE(dvfsrc_top_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_dvfsrc[] = {
+ { .compatible = "mediatek,mt8189-dvfsrc-top", .data = &dvfsrc_top_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_dvfsrc);
+
+static struct platform_driver clk_mt8189_dvfsrc_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-dvfsrc",
+ .of_match_table = of_match_clk_mt8189_dvfsrc,
+ },
+};
+module_platform_driver(clk_mt8189_dvfsrc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 dvfsrc clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 14/18] clk: mediatek: Add MT8189 dbgao clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 dbgao clock controller,
which provides clock gate control for debug-system.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 10 ++++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-dbgao.c | 98 +++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 8eba45f05968..635b0109ec07 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -839,6 +839,16 @@ config COMMON_CLK_MT8189_BUS
MT8189 chipset, ensuring that all bus-related components receive the
correct clock signals for optimal performance.
+config COMMON_CLK_MT8189_DBGAO
+ tristate "Clock driver for MediaTek MT8189 debug ao"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this to support the clock management for the debug function
+ on MediaTek MT8189 SoCs. This includes enabling and disabling
+ vcore debug system clocks. If you want to control its clocks, say Y or M
+ to include this driver in your kernel build.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index aabfb42cb1b2..6ab6df7ebf2a 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o \
clk-mt8189-vlpckgen.o clk-mt8189-vlpcfg.o
obj-$(CONFIG_COMMON_CLK_MT8189_BUS) += clk-mt8189-bus.o
+obj-$(CONFIG_COMMON_CLK_MT8189_DBGAO) += clk-mt8189-dbgao.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-dbgao.c b/drivers/clk/mediatek/clk-mt8189-dbgao.c
new file mode 100644
index 000000000000..40307bdc93eb
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-dbgao.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs dbgao_cg_regs = {
+ .set_ofs = 0x70,
+ .clr_ofs = 0x70,
+ .sta_ofs = 0x70,
+};
+
+#define GATE_DBGAO(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &dbgao_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate dbgao_clks[] = {
+ GATE_DBGAO(CLK_DBGAO_ATB_EN, "dbgao_atb_en", "atb_sel", 0),
+};
+
+static const struct mtk_clk_desc dbgao_mcd = {
+ .clks = dbgao_clks,
+ .num_clks = ARRAY_SIZE(dbgao_clks),
+};
+
+static const struct mtk_gate_regs dem0_cg_regs = {
+ .set_ofs = 0x2c,
+ .clr_ofs = 0x2c,
+ .sta_ofs = 0x2c,
+};
+
+static const struct mtk_gate_regs dem1_cg_regs = {
+ .set_ofs = 0x30,
+ .clr_ofs = 0x30,
+ .sta_ofs = 0x30,
+};
+
+static const struct mtk_gate_regs dem2_cg_regs = {
+ .set_ofs = 0x70,
+ .clr_ofs = 0x70,
+ .sta_ofs = 0x70,
+};
+
+#define GATE_DEM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &dem0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_DEM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &dem1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_DEM2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &dem2_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate dem_clks[] = {
+ /* DEM0 */
+ GATE_DEM0(CLK_DEM_BUSCLK_EN, "dem_busclk_en", "axi_sel", 0),
+ /* DEM1 */
+ GATE_DEM1(CLK_DEM_SYSCLK_EN, "dem_sysclk_en", "axi_sel", 0),
+ /* DEM2 */
+ GATE_DEM2(CLK_DEM_ATB_EN, "dem_atb_en", "atb_sel", 0),
+};
+
+static const struct mtk_clk_desc dem_mcd = {
+ .clks = dem_clks,
+ .num_clks = ARRAY_SIZE(dem_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_dbgao[] = {
+ { .compatible = "mediatek,mt8189-dbg-ao", .data = &dbgao_mcd },
+ { .compatible = "mediatek,mt8189-dem", .data = &dem_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_dbgao);
+
+static struct platform_driver clk_mt8189_dbgao_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-dbgao",
+ .of_match_table = of_match_clk_mt8189_dbgao,
+ },
+};
+module_platform_driver(clk_mt8189_dbgao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 dbgao system clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* Re: [PATCH] arm64: dts: qcom: sm8150: Add slim nodes
From: Konrad Dybcio @ 2026-07-20 10:05 UTC (permalink / raw)
To: XiaoYeZi, andersson, konradybcio, robh, krzk+dt
Cc: conor+dt, linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <tencent_DF2E181B86AC588DD7B70DE904F6670BC207@qq.com>
On 7/20/26 11:33 AM, XiaoYeZi wrote:
> The slimbus node is an important node in audio.
That is true, but this is not a great commit message. Please
check out:
https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
[...]
> + slimbam: dma-controller@17184000 {
> + compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0";
> + qcom,controlled-remotely;
> + reg = <0 0x17184000 0 0x2c000>;
> + num-channels = <31>;
> + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
> + #dma-cells = <1>;
> + qcom,ee = <1>;
> + qcom,num-ees = <2>;
> + iommus = <&apps_smmu 0x1b46 0x0>;
Please assign the iommus values as the downstream kernel does, so
that all SIDs meant for the OS are assigned
Konrad
^ permalink raw reply
* [PATCH v3 13/18] clk: mediatek: Add MT8189 bus clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 bus clock controller,
which provides clock gate control for infra/peri IPs
(such as spi, uart, msdc, flashif ...).
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 11 ++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-bus.c | 200 ++++++++++++++++++++++++++++++++++
3 files changed, 212 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index f67dfb6cd019..8eba45f05968 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -828,6 +828,17 @@ config COMMON_CLK_MT8189
with the MediaTek MT8189 hardware capabilities, providing efficient management of
clock speeds and power consumption.
+config COMMON_CLK_MT8189_BUS
+ tristate "Clock driver for MediaTek MT8189 bus"
+ depends on COMMON_CLK_MT8189
+ default COMMON_CLK_MT8189
+ help
+ Enable this configuration option to support the clock framework for
+ MediaTek MT8189 SoC bus clocks. It includes the necessary clock
+ management for bus-related peripherals and interconnects within the
+ MT8189 chipset, ensuring that all bus-related components receive the
+ correct clock signals for optimal performance.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index d9279b237b7b..aabfb42cb1b2 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o \
clk-mt8189-vlpckgen.o clk-mt8189-vlpcfg.o
+obj-$(CONFIG_COMMON_CLK_MT8189_BUS) += clk-mt8189-bus.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-bus.c b/drivers/clk/mediatek/clk-mt8189-bus.c
new file mode 100644
index 000000000000..494f25e85d11
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-bus.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs ifrao0_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x84,
+ .sta_ofs = 0x90,
+};
+
+static const struct mtk_gate_regs ifrao1_cg_regs = {
+ .set_ofs = 0x88,
+ .clr_ofs = 0x8c,
+ .sta_ofs = 0x94,
+};
+
+static const struct mtk_gate_regs ifrao2_cg_regs = {
+ .set_ofs = 0xa4,
+ .clr_ofs = 0xa8,
+ .sta_ofs = 0xac,
+};
+
+#define GATE_IFRAO0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifrao0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_IFRAO1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifrao1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_IFRAO2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifrao2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate ifrao_clks[] = {
+ /* IFRAO0 */
+ GATE_IFRAO0(CLK_IFRAO_CQ_DMA_FPC, "ifrao_dma", "ap2con", 28),
+ /* IFRAO1 */
+ GATE_IFRAO1(CLK_IFRAO_DEBUGSYS, "ifrao_debugsys", "axi_sel", 24),
+ GATE_IFRAO1(CLK_IFRAO_DBG_TRACE, "ifrao_dbg_trace", "axi_sel", 29),
+ /* IFRAO2 */
+ GATE_IFRAO2(CLK_IFRAO_CQ_DMA, "ifrao_cq_dma", "axi_sel", 27),
+};
+
+static const struct mtk_clk_desc ifrao_mcd = {
+ .clks = ifrao_clks,
+ .num_clks = ARRAY_SIZE(ifrao_clks),
+};
+
+static const struct mtk_gate_regs perao0_cg_regs = {
+ .set_ofs = 0x24,
+ .clr_ofs = 0x28,
+ .sta_ofs = 0x10,
+};
+
+static const struct mtk_gate_regs perao1_cg_regs = {
+ .set_ofs = 0x2c,
+ .clr_ofs = 0x30,
+ .sta_ofs = 0x14,
+};
+
+static const struct mtk_gate_regs perao2_cg_regs = {
+ .set_ofs = 0x34,
+ .clr_ofs = 0x38,
+ .sta_ofs = 0x18,
+};
+
+#define GATE_PERAO0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &perao0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERAO1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &perao1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERAO2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &perao2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate perao_clks[] = {
+ /* PERAO0 */
+ GATE_PERAO0(CLK_PERAO_UART0, "perao_uart0", "uart_sel", 0),
+ GATE_PERAO0(CLK_PERAO_UART1, "perao_uart1", "uart_sel", 1),
+ GATE_PERAO0(CLK_PERAO_UART2, "perao_uart2", "uart_sel", 2),
+ GATE_PERAO0(CLK_PERAO_UART3, "perao_uart3", "uart_sel", 3),
+ GATE_PERAO0(CLK_PERAO_PWM_H, "perao_pwm_h", "axi_peri_sel", 4),
+ GATE_PERAO0(CLK_PERAO_PWM_B, "perao_pwm_b", "pwm_sel", 5),
+ GATE_PERAO0(CLK_PERAO_PWM_FB1, "perao_pwm_fb1", "pwm_sel", 6),
+ GATE_PERAO0(CLK_PERAO_PWM_FB2, "perao_pwm_fb2", "pwm_sel", 7),
+ GATE_PERAO0(CLK_PERAO_PWM_FB3, "perao_pwm_fb3", "pwm_sel", 8),
+ GATE_PERAO0(CLK_PERAO_PWM_FB4, "perao_pwm_fb4", "pwm_sel", 9),
+ GATE_PERAO0(CLK_PERAO_DISP_PWM0, "perao_disp_pwm0", "disp_pwm_sel", 10),
+ GATE_PERAO0(CLK_PERAO_DISP_PWM1, "perao_disp_pwm1", "disp_pwm_sel", 11),
+ GATE_PERAO0(CLK_PERAO_SPI0_B, "perao_spi0_b", "spi0_sel", 12),
+ GATE_PERAO0(CLK_PERAO_SPI1_B, "perao_spi1_b", "spi1_sel", 13),
+ GATE_PERAO0(CLK_PERAO_SPI2_B, "perao_spi2_b", "spi2_sel", 14),
+ GATE_PERAO0(CLK_PERAO_SPI3_B, "perao_spi3_b", "spi3_sel", 15),
+ GATE_PERAO0(CLK_PERAO_SPI4_B, "perao_spi4_b", "spi4_sel", 16),
+ GATE_PERAO0(CLK_PERAO_SPI5_B, "perao_spi5_b", "spi5_sel", 17),
+ GATE_PERAO0(CLK_PERAO_SPI0_H, "perao_spi0_h", "axi_peri_sel", 18),
+ GATE_PERAO0(CLK_PERAO_SPI1_H, "perao_spi1_h", "axi_peri_sel", 19),
+ GATE_PERAO0(CLK_PERAO_SPI2_H, "perao_spi2_h", "axi_peri_sel", 20),
+ GATE_PERAO0(CLK_PERAO_SPI3_H, "perao_spi3_h", "axi_peri_sel", 21),
+ GATE_PERAO0(CLK_PERAO_SPI4_H, "perao_spi4_h", "axi_peri_sel", 22),
+ GATE_PERAO0(CLK_PERAO_SPI5_H, "perao_spi5_h", "axi_peri_sel", 23),
+ GATE_PERAO0(CLK_PERAO_AXI, "perao_axi", "mem_sub_peri_sel", 24),
+ GATE_PERAO0(CLK_PERAO_AHB_APB, "perao_ahb_apb", "axi_peri_sel", 25),
+ GATE_PERAO0(CLK_PERAO_TL, "perao_tl", "pcie_mac_tl_sel", 26),
+ GATE_PERAO0(CLK_PERAO_REF, "perao_ref", "pcie_f26m", 27),
+ GATE_PERAO0(CLK_PERAO_I2C, "perao_i2c", "axi_peri_sel", 28),
+ GATE_PERAO0(CLK_PERAO_DMA_B, "perao_dma_b", "axi_peri_sel", 29),
+ /* PERAO1 */
+ GATE_PERAO1(CLK_PERAO_SSUSB0_REF, "perao_ssusb0_ref", "usb2_26m_p0_en", 1),
+ GATE_PERAO1(CLK_PERAO_SSUSB0_FRMCNT, "perao_ssusb0_frmcnt", "fmcnt_p0_en", 2),
+ GATE_PERAO1(CLK_PERAO_SSUSB0_SYS, "perao_ssusb0_sys", "usb_p0_sel", 4),
+ GATE_PERAO1(CLK_PERAO_SSUSB0_XHCI, "perao_ssusb0_xhci", "ssusb_xhci_p0_sel", 5),
+ GATE_PERAO1(CLK_PERAO_SSUSB0_F, "perao_ssusb0_f", "axi_peri_sel", 6),
+ GATE_PERAO1(CLK_PERAO_SSUSB0_H, "perao_ssusb0_h", "axi_peri_sel", 7),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_REF, "perao_ssusb1_ref", "usb2_26m_p1_en", 8),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_FRMCNT, "perao_ssusb1_frmcnt", "fmcnt_p1_en", 9),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_SYS, "perao_ssusb1_sys", "usb_p1_sel", 11),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_XHCI, "perao_ssusb1_xhci", "ssusb_xhci_p1_sel", 12),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_F, "perao_ssusb1_f", "axi_peri_sel", 13),
+ GATE_PERAO1(CLK_PERAO_SSUSB1_H, "perao_ssusb1_h", "axi_peri_sel", 14),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_REF, "perao_ssusb2_ref", "usb2_26m_p2_en", 15),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_FRMCNT, "perao_ssusb2_frmcnt", "fmcnt_p2_en", 16),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_SYS, "perao_ssusb2_sys", "usb_p2_sel", 18),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_XHCI, "perao_ssusb2_xhci", "ssusb_xhci_p2_sel", 19),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_F, "perao_ssusb2_f", "axi_peri_sel", 20),
+ GATE_PERAO1(CLK_PERAO_SSUSB2_H, "perao_ssusb2_h", "axi_peri_sel", 21),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_REF, "perao_ssusb3_ref", "usb2_26m_p3_en", 23),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_FRMCNT, "perao_ssusb3_frmcnt", "fmcnt_p3_en", 24),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_SYS, "perao_ssusb3_sys", "usb_p3_sel", 26),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_XHCI, "perao_ssusb3_xhci", "ssusb_xhci_p3_sel", 27),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_F, "perao_ssusb3_f", "axi_peri_sel", 28),
+ GATE_PERAO1(CLK_PERAO_SSUSB3_H, "perao_ssusb3_h", "axi_peri_sel", 29),
+ /* PERAO2 */
+ GATE_PERAO2(CLK_PERAO_SSUSB4_REF, "perao_ssusb4_ref", "usb2_26m_p4_en", 0),
+ GATE_PERAO2(CLK_PERAO_SSUSB4_FRMCNT, "perao_ssusb4_frmcnt", "fmcnt_p4_en", 1),
+ GATE_PERAO2(CLK_PERAO_SSUSB4_SYS, "perao_ssusb4_sys", "usb_p4_sel", 3),
+ GATE_PERAO2(CLK_PERAO_SSUSB4_XHCI, "perao_ssusb4_xhci", "ssusb_xhci_p4_sel", 4),
+ GATE_PERAO2(CLK_PERAO_SSUSB4_F, "perao_ssusb4_f", "axi_peri_sel", 5),
+ GATE_PERAO2(CLK_PERAO_SSUSB4_H, "perao_ssusb4_h", "axi_peri_sel", 6),
+ GATE_PERAO2(CLK_PERAO_MSDC0, "perao_msdc0", "msdc50_0_sel", 7),
+ GATE_PERAO2(CLK_PERAO_MSDC0_H, "perao_msdc0_h", "msdc5hclk_sel", 8),
+ GATE_PERAO2(CLK_PERAO_MSDC0_FAES, "perao_msdc0_faes", "aes_msdcfde_sel", 9),
+ GATE_PERAO2(CLK_PERAO_MSDC0_MST_F, "perao_msdc0_mst_f", "axi_peri_sel", 10),
+ GATE_PERAO2(CLK_PERAO_MSDC0_SLV_H, "perao_msdc0_slv_h", "axi_peri_sel", 11),
+ GATE_PERAO2(CLK_PERAO_MSDC1, "perao_msdc1", "msdc30_1_sel", 12),
+ GATE_PERAO2(CLK_PERAO_MSDC1_H, "perao_msdc1_h", "msdc30_1_h_sel", 13),
+ GATE_PERAO2(CLK_PERAO_MSDC1_MST_F, "perao_msdc1_mst_f", "axi_peri_sel", 14),
+ GATE_PERAO2(CLK_PERAO_MSDC1_SLV_H, "perao_msdc1_slv_h", "axi_peri_sel", 15),
+ GATE_PERAO2(CLK_PERAO_MSDC2, "perao_msdc2", "msdc30_2_sel", 16),
+ GATE_PERAO2(CLK_PERAO_MSDC2_H, "perao_msdc2_h", "msdc30_2_h_sel", 17),
+ GATE_PERAO2(CLK_PERAO_MSDC2_MST_F, "perao_msdc2_mst_f", "axi_peri_sel", 18),
+ GATE_PERAO2(CLK_PERAO_MSDC2_SLV_H, "perao_msdc2_slv_h", "axi_peri_sel", 19),
+ GATE_PERAO2(CLK_PERAO_SFLASH, "perao_sflash", "sflash_sel", 20),
+ GATE_PERAO2(CLK_PERAO_SFLASH_F, "perao_sflash_f", "axi_peri_sel", 21),
+ GATE_PERAO2(CLK_PERAO_SFLASH_H, "perao_sflash_h", "axi_peri_sel", 22),
+ GATE_PERAO2(CLK_PERAO_SFLASH_P, "perao_sflash_p", "axi_peri_sel", 23),
+ GATE_PERAO2(CLK_PERAO_AUDIO0, "perao_audio0", "axi_peri_sel", 24),
+ GATE_PERAO2(CLK_PERAO_AUDIO1, "perao_audio1", "axi_peri_sel", 25),
+ GATE_PERAO2(CLK_PERAO_AUDIO2, "perao_audio2", "aud_intbus_sel", 26),
+ GATE_PERAO2(CLK_PERAO_AUXADC_26M, "perao_auxadc_26m", "clk26m", 27),
+};
+
+static const struct mtk_clk_desc perao_mcd = {
+ .clks = perao_clks,
+ .num_clks = ARRAY_SIZE(perao_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_bus[] = {
+ { .compatible = "mediatek,mt8189-infra-ao", .data = &ifrao_mcd },
+ { .compatible = "mediatek,mt8189-peri-ao", .data = &perao_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_bus);
+
+static struct platform_driver clk_mt8189_bus_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-bus",
+ .of_match_table = of_match_clk_mt8189_bus,
+ },
+};
+module_platform_driver(clk_mt8189_bus_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 bus/peripheral clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 12/18] clk: mediatek: Add MT8189 vlpcfg clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 vlpcfg clock controller,
which provides clock gate control for vlp domain IPs.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Makefile | 2 +-
drivers/clk/mediatek/clk-mt8189-vlpcfg.c | 116 +++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 3b25df9e7b50..d9279b237b7b 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -124,7 +124,7 @@ obj-$(CONFIG_COMMON_CLK_MT8188_VENCSYS) += clk-mt8188-venc.o
obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o \
- clk-mt8189-vlpckgen.o
+ clk-mt8189-vlpckgen.o clk-mt8189-vlpcfg.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-vlpcfg.c b/drivers/clk/mediatek/clk-mt8189-vlpcfg.c
new file mode 100644
index 000000000000..cc4ebfd46a6f
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-vlpcfg.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static const struct mtk_gate_regs vlpcfg_ao_reg_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VLPCFG_AO_REG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vlpcfg_ao_reg_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
+
+static const struct mtk_gate vlpcfg_ao_reg_clks[] = {
+ GATE_VLPCFG_AO_REG(CLK_VLPCFG_AO_APEINT_RX, "vlpcfg_ao_apeint_rx", "clk26m", 8),
+};
+
+static const struct mtk_clk_desc vlpcfg_ao_reg_mcd = {
+ .clks = vlpcfg_ao_reg_clks,
+ .num_clks = ARRAY_SIZE(vlpcfg_ao_reg_clks),
+};
+
+static const struct mtk_gate_regs vlpcfg_reg_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_VLPCFG_REG_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &vlpcfg_reg_cg_regs, _shift, \
+ &mtk_clk_gate_ops_no_setclr_inv, _flags)
+
+#define GATE_VLPCFG_REG(_id, _name, _parent, _shift) \
+ GATE_VLPCFG_REG_FLAGS(_id, _name, _parent, _shift, 0)
+
+static const struct mtk_gate vlpcfg_reg_clks[] = {
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SCP, "vlpcfg_scp",
+ "vlp_scp_sel", 28, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_RG_R_APXGPT_26M, "vlpcfg_r_apxgpt_26m",
+ "clk26m", 24, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_DPMSRCK_TEST, "vlpcfg_dpmsrck_test",
+ "clk26m", 23, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_RG_DPMSRRTC_TEST, "vlpcfg_dpmsrrtc_test",
+ "clk32k", 22, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_DPMSRULP_TEST, "vlpcfg_dpmsrulp_test",
+ "osc_d10", 21, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SPMI_P_MST, "vlpcfg_spmi_p",
+ "vlp_spmi_p_sel", 20, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SPMI_P_MST_32K, "vlpcfg_spmi_p_32k",
+ "clk32k", 18, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_PMIF_SPMI_P_SYS, "vlpcfg_pmif_spmi_p_sys",
+ "vlp_pwrap_ulposc_sel", 13, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_PMIF_SPMI_P_TMR, "vlpcfg_pmif_spmi_p_tmr",
+ "vlp_pwrap_ulposc_sel", 12, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG(CLK_VLPCFG_REG_PMIF_SPMI_M_SYS, "vlpcfg_pmif_spmi_m_sys",
+ "vlp_pwrap_ulposc_sel", 11),
+ GATE_VLPCFG_REG(CLK_VLPCFG_REG_PMIF_SPMI_M_TMR, "vlpcfg_pmif_spmi_m_tmr",
+ "vlp_pwrap_ulposc_sel", 10),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_DVFSRC, "vlpcfg_dvfsrc",
+ "vlp_dvfsrc_sel", 9, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_PWM_VLP, "vlpcfg_pwm_vlp",
+ "vlp_pwm_vlp_sel", 8, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SRCK, "vlpcfg_srck",
+ "vlp_srck_sel", 7, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SSPM_F26M, "vlpcfg_sspm_f26m",
+ "vlp_sspm_f26m_sel", 4, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SSPM_F32K, "vlpcfg_sspm_f32k",
+ "clk32k", 3, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_SSPM_ULPOSC, "vlpcfg_sspm_ulposc",
+ "vlp_sspm_ulposc_sel", 2, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_VLP_32K_COM, "vlpcfg_vlp_32k_com",
+ "clk32k", 1, CLK_IS_CRITICAL),
+ GATE_VLPCFG_REG_FLAGS(CLK_VLPCFG_REG_VLP_26M_COM, "vlpcfg_vlp_26m_com",
+ "clk26m", 0, CLK_IS_CRITICAL),
+};
+
+static const struct mtk_clk_desc vlpcfg_reg_mcd = {
+ .clks = vlpcfg_reg_clks,
+ .num_clks = ARRAY_SIZE(vlpcfg_reg_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8189_vlpcfg[] = {
+ { .compatible = "mediatek,mt8189-vlpcfg", .data = &vlpcfg_reg_mcd },
+ { .compatible = "mediatek,mt8189-vlpcfg-ao", .data = &vlpcfg_ao_reg_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_vlpcfg);
+
+static struct platform_driver clk_mt8189_vlpcfg_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-vlpcfg",
+ .of_match_table = of_match_clk_mt8189_vlpcfg,
+ },
+};
+module_platform_driver(clk_mt8189_vlpcfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 vlpcfg clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 11/18] clk: mediatek: Add MT8189 vlpckgen clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 vlpckgen clock controller, which provides
muxes and dividers for clock selection in vlp domain for other IP blocks.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Makefile | 3 +-
drivers/clk/mediatek/clk-mt8189-vlpckgen.c | 285 +++++++++++++++++++++++++++++
2 files changed, 287 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 9d3d2983bfb2..3b25df9e7b50 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -123,7 +123,8 @@ obj-$(CONFIG_COMMON_CLK_MT8188_VDOSYS) += clk-mt8188-vdo0.o clk-mt8188-vdo1.o
obj-$(CONFIG_COMMON_CLK_MT8188_VENCSYS) += clk-mt8188-venc.o
obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
-obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o \
+ clk-mt8189-vlpckgen.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-vlpckgen.c b/drivers/clk/mediatek/clk-mt8189-vlpckgen.c
new file mode 100644
index 000000000000..8eda5331972e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-vlpckgen.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static DEFINE_SPINLOCK(mt8189_vlpclk_lock);
+
+static const char * const vlp_26m_oscd10_parents[] = {
+ "clk26m",
+ "osc_d10"
+};
+
+static const char * const vlp_vadsp_vowpll_parents[] = {
+ "clk26m",
+ "vowpll"
+};
+
+static const char * const vlp_sspm_ulposc_parents[] = {
+ "ulposc",
+ "univpll_d5_d2",
+ "osc_d10"
+};
+
+static const char * const vlp_aud_adc_parents[] = {
+ "clk26m",
+ "vowpll",
+ "aud_adc_ext",
+ "osc_d10"
+};
+
+static const char * const vlp_scp_iic_spi_parents[] = {
+ "clk26m",
+ "mainpll_d5_d4",
+ "mainpll_d7_d2",
+ "osc_d10"
+};
+
+static const char * const vlp_vadsp_uarthub_b_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "univpll_d6_d4",
+ "univpll_d6_d2"
+};
+
+static const char * const vlp_axi_kp_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d2",
+ "mainpll_d7_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_sspm_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "mainpll_d5_d2",
+ "ulposc",
+ "mainpll_d6"
+};
+
+static const char * const vlp_pwm_vlp_parents[] = {
+ "clk26m",
+ "osc_d4",
+ "clk32k",
+ "osc_d10",
+ "mainpll_d4_d8"
+};
+
+static const char * const vlp_pwrap_ulposc_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d7",
+ "osc_d8",
+ "osc_d16",
+ "mainpll_d7_d8"
+};
+
+static const char * const vlp_vadsp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d10",
+ "osc_d2",
+ "ulposc",
+ "mainpll_d4_d2"
+};
+
+static const char * const vlp_scp_parents[] = {
+ "clk26m",
+ "univpll_d4",
+ "univpll_d3",
+ "mainpll_d3",
+ "univpll_d6",
+ "apll1",
+ "mainpll_d4",
+ "mainpll_d6",
+ "mainpll_d7",
+ "osc_d10"
+};
+
+static const char * const vlp_spmi_p_parents[] = {
+ "clk26m",
+ "f26m_d2",
+ "osc_d8",
+ "osc_d10",
+ "osc_d16",
+ "osc_d7",
+ "clk32k",
+ "mainpll_d7_d8",
+ "mainpll_d6_d8",
+ "mainpll_d5_d8"
+};
+
+static const char * const vlp_camtg_parents[] = {
+ "clk26m",
+ "univpll_192m_d8",
+ "univpll_d6_d8",
+ "univpll_192m_d4",
+ "osc_d16",
+ "osc_d20",
+ "osc_d10",
+ "univpll_d6_d16",
+ "tvdpll1_d16",
+ "f26m_d2",
+ "univpll_192m_d10",
+ "univpll_192m_d16",
+ "univpll_192m_d32"
+};
+
+static const struct mtk_mux vlp_ck_muxes[] = {
+ /* VLP_CLK_CFG_0 */
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_SCP_SEL, "vlp_scp_sel",
+ vlp_scp_parents, 0x008, 0x00c, 0x010,
+ 0, 4, 7, 0x04, 0),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_PWRAP_ULPOSC_SEL, "vlp_pwrap_ulposc_sel",
+ vlp_pwrap_ulposc_parents, 0x008, 0x00c, 0x010,
+ 8, 3, 0x04, 1),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SPMI_P_MST_SEL, "vlp_spmi_p_sel",
+ vlp_spmi_p_parents, 0x008, 0x00c, 0x010,
+ 16, 4, 0x04, 2),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_DVFSRC_SEL, "vlp_dvfsrc_sel",
+ vlp_26m_oscd10_parents, 0x008, 0x00c, 0x010,
+ 24, 1, 0x04, 3),
+ /* VLP_CLK_CFG_1 */
+ MUX_CLR_SET_UPD(CLK_VLP_CK_PWM_VLP_SEL, "vlp_pwm_vlp_sel",
+ vlp_pwm_vlp_parents, 0x014, 0x018, 0x01c,
+ 0, 3, 0x04, 4),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_AXI_VLP_SEL, "vlp_axi_vlp_sel",
+ vlp_axi_kp_parents, 0x014, 0x018, 0x01c,
+ 8, 3, 0x04, 5),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SYSTIMER_26M_SEL, "vlp_timer_26m_sel",
+ vlp_26m_oscd10_parents, 0x014, 0x018, 0x01c,
+ 16, 1, 0x04, 6),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SSPM_SEL, "vlp_sspm_sel",
+ vlp_sspm_parents, 0x014, 0x018, 0x01c,
+ 24, 3, 0x04, 7),
+ /* VLP_CLK_CFG_2 */
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SSPM_F26M_SEL, "vlp_sspm_f26m_sel",
+ vlp_26m_oscd10_parents, 0x020, 0x024, 0x028,
+ 0, 1, 0x04, 8),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SRCK_SEL, "vlp_srck_sel",
+ vlp_26m_oscd10_parents, 0x020, 0x024, 0x028,
+ 8, 1, 0x04, 9),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SCP_SPI_SEL, "vlp_scp_spi_sel",
+ vlp_scp_iic_spi_parents, 0x020, 0x024, 0x028,
+ 16, 2, 0x04, 10),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SCP_IIC_SEL, "vlp_scp_iic_sel",
+ vlp_scp_iic_spi_parents, 0x020, 0x024, 0x028,
+ 24, 2, 0x04, 11),
+ /* VLP_CLK_CFG_3 */
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SCP_SPI_HIGH_SPD_SEL,
+ "vlp_scp_spi_hs_sel",
+ vlp_scp_iic_spi_parents, 0x02c, 0x030, 0x034,
+ 0, 2, 0x04, 12),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SCP_IIC_HIGH_SPD_SEL,
+ "vlp_scp_iic_hs_sel",
+ vlp_scp_iic_spi_parents, 0x02c, 0x030, 0x034,
+ 8, 2, 0x04, 13),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_SSPM_ULPOSC_SEL, "vlp_sspm_ulposc_sel",
+ vlp_sspm_ulposc_parents, 0x02c, 0x030, 0x034,
+ 16, 2, 0x04, 14),
+ MUX_CLR_SET_UPD(CLK_VLP_CK_APXGPT_26M_SEL, "vlp_apxgpt_26m_sel",
+ vlp_26m_oscd10_parents, 0x02c, 0x030, 0x034,
+ 24, 1, 0x04, 15),
+ /* VLP_CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_VADSP_SEL, "vlp_vadsp_sel",
+ vlp_vadsp_parents, 0x038, 0x03c, 0x040,
+ 0, 3, 7, 0x04, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_VADSP_VOWPLL_SEL,
+ "vlp_vadsp_vowpll_sel",
+ vlp_vadsp_vowpll_parents, 0x038, 0x03c, 0x040,
+ 8, 1, 15, 0x04, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_VADSP_UARTHUB_BCLK_SEL,
+ "vlp_vadsp_uarthub_b_sel",
+ vlp_vadsp_uarthub_b_parents,
+ 0x038, 0x03c, 0x040, 16, 2, 23, 0x04, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_CAMTG0_SEL, "vlp_camtg0_sel",
+ vlp_camtg_parents, 0x038, 0x03c, 0x040,
+ 24, 4, 31, 0x04, 19),
+ /* VLP_CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_CAMTG1_SEL, "vlp_camtg1_sel",
+ vlp_camtg_parents, 0x044, 0x048, 0x04c,
+ 0, 4, 7, 0x04, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_CAMTG2_SEL, "vlp_camtg2_sel",
+ vlp_camtg_parents, 0x044, 0x048, 0x04c,
+ 8, 4, 15, 0x04, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_AUD_ADC_SEL, "vlp_aud_adc_sel",
+ vlp_aud_adc_parents, 0x044, 0x048, 0x04c,
+ 16, 2, 23, 0x04, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_VLP_CK_KP_IRQ_GEN_SEL, "vlp_kp_irq_sel",
+ vlp_axi_kp_parents, 0x044, 0x048, 0x04c,
+ 24, 3, 31, 0x04, 23),
+};
+
+static const struct mtk_gate_regs vlp_ck_cg_regs = {
+ .set_ofs = 0x1f4,
+ .clr_ofs = 0x1f8,
+ .sta_ofs = 0x1f0,
+};
+
+#define GATE_VLP_CK_FLAGS(_id, _name, _parent, _shift, _flag) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vlp_ck_cg_regs, \
+ .shift = _shift, \
+ .flags = _flag, \
+ .ops = &mtk_clk_gate_ops_setclr_inv, \
+ }
+
+#define GATE_VLP_CK(_id, _name, _parent, _shift) \
+ GATE_VLP_CK_FLAGS(_id, _name, _parent, _shift, 0)
+
+static const struct mtk_gate vlp_ck_clks[] = {
+ GATE_VLP_CK(CLK_VLP_CK_VADSYS_VLP_26M_EN, "vlp_vadsys_vlp_26m", "clk26m", 1),
+ GATE_VLP_CK_FLAGS(CLK_VLP_CK_FMIPI_CSI_UP26M_CK_EN, "vlp_fmipi_csi_up26m",
+ "fmipi_csi_up26m", 11, CLK_IS_CRITICAL),
+};
+
+static const struct mtk_clk_desc vlpck_desc = {
+ .mux_clks = vlp_ck_muxes,
+ .num_mux_clks = ARRAY_SIZE(vlp_ck_muxes),
+ .clks = vlp_ck_clks,
+ .num_clks = ARRAY_SIZE(vlp_ck_clks),
+ .clk_lock = &mt8189_vlpclk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt8189_vlpck[] = {
+ { .compatible = "mediatek,mt8189-vlpckgen", .data = &vlpck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_vlpck);
+
+static struct platform_driver clk_mt8189_vlpck_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-vlpck",
+ .of_match_table = of_match_clk_mt8189_vlpck,
+ },
+};
+module_platform_driver(clk_mt8189_vlpck_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 vlpckgen clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 10/18] clk: mediatek: Add MT8189 topckgen clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 topckgen clock controller, which provides
muxes and dividers for clock selection in other IP blocks.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Makefile | 2 +-
drivers/clk/mediatek/clk-mt8189-topckgen.c | 1025 ++++++++++++++++++++++++++++
2 files changed, 1026 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 66577ccb9b93..9d3d2983bfb2 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -123,7 +123,7 @@ obj-$(CONFIG_COMMON_CLK_MT8188_VDOSYS) += clk-mt8188-vdo0.o clk-mt8188-vdo1.o
obj-$(CONFIG_COMMON_CLK_MT8188_VENCSYS) += clk-mt8188-venc.o
obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
-obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o
+obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o clk-mt8189-topckgen.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-topckgen.c b/drivers/clk/mediatek/clk-mt8189-topckgen.c
new file mode 100644
index 000000000000..2473b617e9d6
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-topckgen.c
@@ -0,0 +1,1025 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+static DEFINE_SPINLOCK(mt8189_clk_lock);
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll", 1, 16),
+ FACTOR(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll", 43, 1375),
+ FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll", 1, 10),
+ FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll", 1, 20),
+ FACTOR(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll", 1, 40),
+ FACTOR(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6),
+ FACTOR(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll", 1, 12),
+ FACTOR(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll", 1, 24),
+ FACTOR(CLK_TOP_MAINPLL_D6_D8, "mainpll_d6_d8", "mainpll", 1, 48),
+ FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
+ FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll", 1, 14),
+ FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll", 1, 28),
+ FACTOR(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll", 1, 56),
+ FACTOR(CLK_TOP_MAINPLL_D9, "mainpll_d9", "mainpll", 1, 9),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll", 1, 16),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll", 1, 32),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll", 1, 10),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll", 1, 20),
+ FACTOR(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll", 1, 12),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll", 1, 24),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll", 1, 48),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll", 1, 96),
+ FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
+ FACTOR(CLK_TOP_UNIVPLL_D7_D2, "univpll_d7_d2", "univpll", 1, 14),
+ FACTOR(CLK_TOP_UNIVPLL_D7_D3, "univpll_d7_d3", "univpll", 1, 21),
+ FACTOR(CLK_TOP_LVDSTX_DG_CTS, "lvdstx_dg_cts", "univpll", 1, 21),
+ FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D2, "univpll_192m_d2", "univpll", 1, 26),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll", 1, 52),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll", 1, 104),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D10, "univpll_192m_d10", "univpll", 1, 130),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll", 1, 208),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll", 1, 416),
+ FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1", 1, 2),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1", 1, 8),
+ FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1", 1, 3),
+ FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2", 1, 2),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4),
+ FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2", 1, 8),
+ FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2", 1, 3),
+ FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D4_D2, "mmpll_d4_d2", "mmpll", 1, 8),
+ FACTOR(CLK_TOP_MMPLL_D4_D4, "mmpll_d4_d4", "mmpll", 1, 16),
+ FACTOR(CLK_TOP_VPLL_DPIX, "vpll_dpix", "mmpll", 1, 16),
+ FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5),
+ FACTOR(CLK_TOP_MMPLL_D5_D2, "mmpll_d5_d2", "mmpll", 1, 10),
+ FACTOR(CLK_TOP_MMPLL_D5_D4, "mmpll_d5_d4", "mmpll", 1, 20),
+ FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll", 1, 6),
+ FACTOR(CLK_TOP_MMPLL_D6_D2, "mmpll_d6_d2", "mmpll", 1, 12),
+ FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7),
+ FACTOR(CLK_TOP_MMPLL_D9, "mmpll_d9", "mmpll", 1, 9),
+ FACTOR(CLK_TOP_TVDPLL1_D2, "tvdpll1_d2", "tvdpll1", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL1_D4, "tvdpll1_d4", "tvdpll1", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL1_D8, "tvdpll1_d8", "tvdpll1", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL1_D16, "tvdpll1_d16", "tvdpll1", 92, 1473),
+ FACTOR(CLK_TOP_TVDPLL2_D2, "tvdpll2_d2", "tvdpll2", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL2_D4, "tvdpll2_d4", "tvdpll2", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL2_D8, "tvdpll2_d8", "tvdpll2", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL2_D16, "tvdpll2_d16", "tvdpll2", 92, 1473),
+ FACTOR(CLK_TOP_ETHPLL_D2, "ethpll_d2", "ethpll", 1, 2),
+ FACTOR(CLK_TOP_ETHPLL_D8, "ethpll_d8", "ethpll", 1, 8),
+ FACTOR(CLK_TOP_ETHPLL_D10, "ethpll_d10", "ethpll", 1, 10),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_UFSPLL_D2, "ufspll_d2", "ufspll", 1, 2),
+ FACTOR(CLK_TOP_F26M_CK_D2, "f26m_d2", "clk26m", 1, 2),
+ FACTOR(CLK_TOP_OSC_D2, "osc_d2", "ulposc", 1, 2),
+ FACTOR(CLK_TOP_OSC_D4, "osc_d4", "ulposc", 1, 4),
+ FACTOR(CLK_TOP_OSC_D8, "osc_d8", "ulposc", 1, 8),
+ FACTOR(CLK_TOP_OSC_D16, "osc_d16", "ulposc", 61, 973),
+ FACTOR(CLK_TOP_OSC_D3, "osc_d3", "ulposc", 1, 3),
+ FACTOR(CLK_TOP_OSC_D7, "osc_d7", "ulposc", 1, 7),
+ FACTOR(CLK_TOP_OSC_D10, "osc_d10", "ulposc", 1, 10),
+ FACTOR(CLK_TOP_OSC_D20, "osc_d20", "ulposc", 1, 20),
+};
+
+static const char * const ap2conn_host_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4"
+};
+
+static const char * const apll_m_parents[] = {
+ "aud_1_sel",
+ "aud_2_sel"
+};
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "apll1"
+};
+
+static const char * const aud_2_parents[] = {
+ "clk26m",
+ "apll2"
+};
+
+static const char * const mfg_sel_mfgpll_parents[] = {
+ "mfg_ref_sel",
+ "mfgpll"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "univpll_d4_d8"
+};
+
+static const char * const snps_eth_250m_parents[] = {
+ "clk26m",
+ "ethpll_d2"
+};
+
+static const char * const snps_eth_50m_rmii_parents[] = {
+ "clk26m",
+ "ethpll_d10"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll_d6_d8"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d4"
+};
+
+static const char * const msdc5hclk_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d6_d2"
+};
+
+static const char * const pcie_mac_tl_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "univpll_d5_d4"
+};
+
+static const char * const pll_dpix_parents[] = {
+ "clk26m",
+ "vpll_dpix",
+ "mmpll_d4_d4"
+};
+
+static const char * const usb_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4"
+};
+
+static const char * const vdstx_dg_cts_parents[] = {
+ "clk26m",
+ "lvdstx_dg_cts",
+ "univpll_d7_d3"
+};
+
+static const char * const audio_h_parents[] = {
+ "clk26m",
+ "univpll_d7_d2",
+ "apll1",
+ "apll2"
+};
+
+static const char * const aud_engen1_parents[] = {
+ "clk26m",
+ "apll1_d2",
+ "apll1_d4",
+ "apll1_d8"
+};
+
+static const char * const aud_engen2_parents[] = {
+ "clk26m",
+ "apll2_d2",
+ "apll2_d4",
+ "apll2_d8"
+};
+
+static const char * const axi_peri_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2",
+ "osc_d4"
+};
+
+static const char * const axi_u_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "mainpll_d7_d4",
+ "osc_d8"
+};
+
+static const char * const camtm_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "univpll_d6_d2",
+ "univpll_d6_d4"
+};
+
+static const char * const dsi_occ_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d5_d2",
+ "univpll_d4_d2"
+};
+
+static const char * const dxcc_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2"
+};
+
+static const char * const i2c_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "univpll_d5_d4",
+ "mainpll_d4_d4"
+};
+
+static const char * const mcupm_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2"
+};
+
+static const char * const mfg_ref_parents[] = {
+ "clk26m",
+ "mainpll_d6_d2",
+ "mainpll_d6",
+ "mainpll_d5_d2"
+};
+
+static const char * const msdc30_h_parents[] = {
+ "clk26m",
+ "msdcpll_d2",
+ "mainpll_d4_d4",
+ "mainpll_d6_d4"
+};
+
+static const char * const msdc_macro_p_parents[] = {
+ "clk26m",
+ "msdcpll",
+ "mmpll_d5_d4",
+ "univpll_d4_d2"
+};
+
+static const char * const snps_eth_62p4m_ptp_parents[] = {
+ "clk26m",
+ "ethpll_d8",
+ "apll1_d3",
+ "apll2_d3"
+};
+
+static const char * const ufs_mbist_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "ufspll_d2"
+};
+
+static const char * const aes_msdcfde_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d4_d4",
+ "msdcpll"
+};
+
+static const char * const bus_aximem_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6"
+};
+
+static const char * const dp_parents[] = {
+ "clk26m",
+ "tvdpll1_d16",
+ "tvdpll1_d8",
+ "tvdpll1_d4",
+ "tvdpll1_d2"
+};
+
+static const char * const msdc30_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d6_d2",
+ "mainpll_d7_d2",
+ "msdcpll_d2"
+};
+
+static const char * const ecc_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "univpll_d6",
+ "mainpll_d4",
+ "univpll_d4"
+};
+
+static const char * const emi_n_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d9",
+ "mainpll_d6",
+ "mainpll_d5",
+ "emipll"
+};
+
+static const char * const sr_pka_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const char * const aes_ufsfde_parents[] = {
+ "clk26m",
+ "mainpll_d4",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d4_d4",
+ "univpll_d4_d2",
+ "univpll_d6"
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2",
+ "mainpll_d4_d2",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2",
+ "osc_d4"
+};
+
+static const char * const disp_pwm_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "osc_d2",
+ "osc_d4",
+ "osc_d16",
+ "univpll_d5_d4",
+ "mainpll_d4_d4"
+};
+
+static const char * const edp_parents[] = {
+ "clk26m",
+ "tvdpll2_d16",
+ "tvdpll2_d8",
+ "tvdpll2_d4",
+ "tvdpll2_d2"
+};
+
+static const char * const gcpu_parents[] = {
+ "clk26m",
+ "mainpll_d6",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "univpll_d5_d2",
+ "univpll_d5_d4",
+ "univpll_d6"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll",
+ "msdcpll_d2",
+ "mainpll_d6_d2",
+ "mainpll_d4_d4",
+ "mainpll_d6",
+ "univpll_d4_d4"
+};
+
+static const char * const ufs_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "mainpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2",
+ "univpll_d6_d2",
+ "msdcpll_d2"
+};
+
+static const char * const dsp_parents[] = {
+ "clk26m",
+ "osc_d4",
+ "osc_d3",
+ "osc_d2",
+ "univpll_d7_d2",
+ "univpll_d6_d2",
+ "mainpll_d6",
+ "univpll_d5"
+};
+
+static const char * const mem_sub_peri_u_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4"
+};
+
+static const char * const seninf_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "mmpll_d7",
+ "univpll_d6",
+ "univpll_d5"
+};
+
+static const char * const sflash_parents[] = {
+ "clk26m",
+ "mainpll_d7_d8",
+ "univpll_d6_d8",
+ "mainpll_d7_d4",
+ "mainpll_d6_d4",
+ "univpll_d6_d4",
+ "univpll_d7_d3",
+ "univpll_d5_d4"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_192m",
+ "mainpll_d6_d2",
+ "univpll_d4_d4",
+ "mainpll_d4_d4",
+ "univpll_d5_d4",
+ "univpll_d6_d4"
+};
+
+static const char * const img1_parents[] = {
+ "clk26m",
+ "univpll_d4",
+ "mmpll_d5",
+ "mmpll_d6",
+ "univpll_d6",
+ "mmpll_d7",
+ "mmpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "mmpll_d6_d2",
+ "mmpll_d5_d2"
+};
+
+static const char * const ipe_parents[] = {
+ "clk26m",
+ "univpll_d4",
+ "mainpll_d4",
+ "mmpll_d6",
+ "univpll_d6",
+ "mainpll_d6",
+ "mmpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "mmpll_d6_d2",
+ "mmpll_d5_d2"
+};
+
+static const char * const mem_sub_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "mainpll_d6_d2",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mmpll_d7",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4",
+ "univpll_d4"
+};
+
+static const char * const cam_parents[] = {
+ "clk26m",
+ "mainpll_d4",
+ "mmpll_d4",
+ "univpll_d4",
+ "univpll_d5",
+ "mmpll_d7",
+ "mmpll_d6",
+ "univpll_d6",
+ "univpll_d4_d2",
+ "mmpll_d9",
+ "mainpll_d4_d2",
+ "osc_d2"
+};
+
+static const char * const mmsys_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2",
+ "univpll_d5_d2",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d6",
+ "univpll_d6",
+ "mmpll_d6",
+ "tvdpll1",
+ "tvdpll2",
+ "univpll_d4",
+ "mmpll_d4"
+};
+
+static const char * const mminfra_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "mainpll_d4_d2",
+ "mmpll_d4_d2",
+ "mainpll_d6",
+ "mmpll_d7",
+ "univpll_d6",
+ "mainpll_d5",
+ "mmpll_d6",
+ "univpll_d5",
+ "mainpll_d4",
+ "univpll_d4",
+ "mmpll_d4",
+ "emipll"
+};
+
+static const char * const vdec_parents[] = {
+ "clk26m",
+ "univpll_192m_d2",
+ "univpll_d5_d4",
+ "mainpll_d5",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "univpll_d7",
+ "mmpll_d7",
+ "mmpll_d6",
+ "univpll_d6",
+ "mainpll_d4",
+ "univpll_d4",
+ "mmpll_d5_d2"
+};
+
+static const char * const venc_parents[] = {
+ "clk26m",
+ "mmpll_d4_d2",
+ "mainpll_d6",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "univpll_d6",
+ "mmpll_d6",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2",
+ "mmpll_d9",
+ "mmpll_d4",
+ "mainpll_d4",
+ "univpll_d4",
+ "univpll_d5",
+ "univpll_d5_d2",
+ "mainpll_d5"
+};
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_CLR_SET_UPD(CLK_TOP_AXI_SEL, "axi_sel",
+ axi_parents, 0x010, 0x014, 0x018, 0, 3, 0x04, 0),
+ MUX_CLR_SET_UPD(CLK_TOP_AXI_PERI_SEL, "axi_peri_sel",
+ axi_peri_parents, 0x010, 0x014, 0x018,
+ 8, 2, 0x04, 1),
+ MUX_CLR_SET_UPD(CLK_TOP_AXI_U_SEL, "axi_u_sel",
+ axi_u_parents, 0x010, 0x014, 0x018,
+ 16, 2, 0x04, 2),
+ MUX_CLR_SET_UPD(CLK_TOP_BUS_AXIMEM_SEL, "bus_aximem_sel",
+ bus_aximem_parents, 0x010, 0x014, 0x018,
+ 24, 3, 0x04, 3),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP0_SEL, "disp0_sel",
+ mmsys_parents, 0x020, 0x024, 0x028,
+ 0, 4, 7, 0x04, 4),
+ MUX_CLR_SET_UPD(CLK_TOP_MMINFRA_SEL, "mminfra_sel",
+ mminfra_parents, 0x020, 0x024, 0x028,
+ 8, 4, 0x04, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel",
+ uart_parents, 0x020, 0x024, 0x028,
+ 16, 1, 23, 0x04, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI0_SEL, "spi0_sel",
+ spi_parents, 0x020, 0x024, 0x028,
+ 24, 3, 31, 0x04, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI1_SEL, "spi1_sel",
+ spi_parents, 0x030, 0x034, 0x038,
+ 0, 3, 7, 0x04, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI2_SEL, "spi2_sel",
+ spi_parents, 0x030, 0x034, 0x038,
+ 8, 3, 15, 0x04, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI3_SEL, "spi3_sel",
+ spi_parents, 0x030, 0x034, 0x038,
+ 16, 3, 23, 0x04, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI4_SEL, "spi4_sel",
+ spi_parents, 0x030, 0x034, 0x038,
+ 24, 3, 31, 0x04, 11),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI5_SEL, "spi5_sel",
+ spi_parents, 0x040, 0x044, 0x048,
+ 0, 3, 7, 0x04, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC_MACRO_0P_SEL, "msdc_macro_0p_sel",
+ msdc_macro_p_parents, 0x040, 0x044, 0x048,
+ 8, 2, 15, 0x04, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc5hclk_sel",
+ msdc5hclk_parents, 0x040, 0x044, 0x048,
+ 16, 2, 23, 0x04, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
+ msdc50_0_parents, 0x040, 0x044, 0x048,
+ 24, 3, 31, 0x04, 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AES_MSDCFDE_SEL, "aes_msdcfde_sel",
+ aes_msdcfde_parents, 0x050, 0x054, 0x058,
+ 0, 3, 7, 0x04, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC_MACRO_1P_SEL, "msdc_macro_1p_sel",
+ msdc_macro_p_parents, 0x050, 0x054, 0x058,
+ 8, 2, 15, 0x04, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
+ msdc30_parents, 0x050, 0x054, 0x058,
+ 16, 3, 23, 0x04, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_HCLK_SEL, "msdc30_1_h_sel",
+ msdc30_h_parents, 0x050, 0x054, 0x058,
+ 24, 2, 31, 0x04, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC_MACRO_2P_SEL, "msdc_macro_2p_sel",
+ msdc_macro_p_parents, 0x060, 0x064, 0x068,
+ 0, 2, 7, 0x04, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel",
+ msdc30_parents, 0x060, 0x064, 0x068,
+ 8, 3, 15, 0x04, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_2_HCLK_SEL, "msdc30_2_h_sel",
+ msdc30_h_parents, 0x060, 0x064, 0x068,
+ 16, 2, 23, 0x04, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel",
+ aud_intbus_parents, 0x060, 0x064, 0x068,
+ 24, 2, 31, 0x04, 23),
+ /* CLK_CFG_6 */
+ MUX_CLR_SET_UPD(CLK_TOP_ATB_SEL, "atb_sel",
+ atb_parents, 0x070, 0x074, 0x078, 0, 2, 0x04, 24),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM_SEL, "disp_pwm_sel",
+ disp_pwm_parents, 0x070, 0x074, 0x078,
+ 8, 3, 15, 0x04, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_P0_SEL, "usb_p0_sel",
+ usb_parents, 0x070, 0x074, 0x078,
+ 16, 2, 23, 0x04, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P0_SEL, "ssusb_xhci_p0_sel",
+ usb_parents, 0x070, 0x074, 0x078,
+ 24, 2, 31, 0x04, 27),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_P1_SEL, "usb_p1_sel",
+ usb_parents, 0x080, 0x084, 0x088,
+ 0, 2, 7, 0x04, 28),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P1_SEL, "ssusb_xhci_p1_sel",
+ usb_parents, 0x080, 0x084, 0x088,
+ 8, 2, 15, 0x04, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_P2_SEL, "usb_p2_sel",
+ usb_parents, 0x080, 0x084, 0x088,
+ 16, 2, 23, 0x04, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P2_SEL, "ssusb_xhci_p2_sel",
+ usb_parents, 0x080, 0x084, 0x088,
+ 24, 2, 31, 0x08, 0),
+ /* CLK_CFG_8 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_P3_SEL, "usb_p3_sel",
+ usb_parents, 0x090, 0x094, 0x098,
+ 0, 2, 7, 0x08, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P3_SEL, "ssusb_xhci_p3_sel",
+ usb_parents, 0x090, 0x094, 0x098,
+ 8, 2, 15, 0x08, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_P4_SEL, "usb_p4_sel",
+ usb_parents, 0x090, 0x094, 0x098,
+ 16, 2, 23, 0x08, 3),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P4_SEL, "ssusb_xhci_p4_sel",
+ usb_parents, 0x090, 0x094, 0x098,
+ 24, 2, 31, 0x08, 4),
+ /* CLK_CFG_9 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel",
+ i2c_parents, 0x0a0, 0x0a4, 0x0a8,
+ 0, 2, 7, 0x08, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF_SEL, "seninf_sel",
+ seninf_parents, 0x0a0, 0x0a4, 0x0a8,
+ 8, 3, 15, 0x08, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF1_SEL, "seninf1_sel",
+ seninf_parents, 0x0a0, 0x0a4, 0x0a8,
+ 16, 3, 23, 0x08, 7),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_ENGEN1_SEL, "aud_engen1_sel",
+ aud_engen1_parents, 0x0a0, 0x0a4, 0x0a8,
+ 24, 2, 31, 0x08, 8),
+ /* CLK_CFG_10 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_ENGEN2_SEL, "aud_engen2_sel",
+ aud_engen2_parents, 0x0b0, 0x0b4, 0x0b8,
+ 0, 2, 7, 0x08, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AES_UFSFDE_SEL, "aes_ufsfde_sel",
+ aes_ufsfde_parents, 0x0b0, 0x0b4, 0x0b8,
+ 8, 3, 15, 0x08, 10),
+ MUX_CLR_SET_UPD(CLK_TOP_U_SEL, "ufs_sel",
+ ufs_parents, 0x0b0, 0x0b4, 0x0b8,
+ 16, 3, 0x08, 11),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_U_MBIST_SEL, "ufs_mbist_sel",
+ ufs_mbist_parents, 0x0b0, 0x0b4, 0x0b8,
+ 24, 2, 31, 0x08, 12),
+ /* CLK_CFG_11 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_1_SEL, "aud_1_sel",
+ aud_1_parents, 0x0c0, 0x0c4, 0x0c8,
+ 0, 1, 7, 0x08, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_2_SEL, "aud_2_sel",
+ aud_2_parents, 0x0c0, 0x0c4, 0x0c8,
+ 8, 1, 15, 0x08, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VENC_SEL, "venc_sel",
+ venc_parents, 0x0c0, 0x0c4, 0x0c8,
+ 16, 4, 23, 0x08, 15),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VDEC_SEL, "vdec_sel",
+ vdec_parents, 0x0c0, 0x0c4, 0x0c8,
+ 24, 4, 31, 0x08, 16),
+ /* CLK_CFG_12 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel",
+ pwm_parents, 0x0d0, 0x0d4, 0x0d8,
+ 0, 1, 7, 0x08, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_H_SEL, "audio_h_sel",
+ audio_h_parents, 0x0d0, 0x0d4, 0x0d8,
+ 8, 2, 15, 0x08, 18),
+ MUX_CLR_SET_UPD(CLK_TOP_MCUPM_SEL, "mcupm_sel",
+ mcupm_parents, 0x0d0, 0x0d4, 0x0d8,
+ 16, 2, 0x08, 19),
+ MUX_CLR_SET_UPD(CLK_TOP_MEM_SUB_SEL, "mem_sub_sel",
+ mem_sub_parents, 0x0d0, 0x0d4, 0x0d8,
+ 24, 4, 0x08, 20),
+ /* CLK_CFG_13 */
+ MUX_CLR_SET_UPD(CLK_TOP_MEM_SUB_PERI_SEL, "mem_sub_peri_sel",
+ mem_sub_peri_u_parents, 0x0e0, 0x0e4, 0x0e8,
+ 0, 3, 0x08, 21),
+ MUX_CLR_SET_UPD(CLK_TOP_MEM_SUB_U_SEL, "mem_sub_u_sel",
+ mem_sub_peri_u_parents, 0x0e0, 0x0e4, 0x0e8,
+ 8, 3, 0x08, 22),
+ MUX_CLR_SET_UPD(CLK_TOP_EMI_N_SEL, "emi_n_sel",
+ emi_n_parents, 0x0e0, 0x0e4, 0x0e8,
+ 16, 3, 0x08, 23),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSI_OCC_SEL, "dsi_occ_sel",
+ dsi_occ_parents, 0x0e0, 0x0e4, 0x0e8,
+ 24, 2, 31, 0x08, 24),
+ /* CLK_CFG_14 */
+ MUX_CLR_SET_UPD(CLK_TOP_AP2CONN_HOST_SEL, "ap2conn_host_sel",
+ ap2conn_host_parents, 0x0f0, 0x0f4, 0x0f8,
+ 0, 1, 0x08, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IMG1_SEL, "img1_sel",
+ img1_parents, 0x0f0, 0x0f4, 0x0f8,
+ 8, 4, 15, 0x08, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IPE_SEL, "ipe_sel",
+ ipe_parents, 0x0f0, 0x0f4, 0x0f8,
+ 16, 4, 23, 0x08, 27),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAM_SEL, "cam_sel",
+ cam_parents, 0x0f0, 0x0f4, 0x0f8,
+ 24, 4, 31, 0x08, 28),
+ /* CLK_CFG_15 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTM_SEL, "camtm_sel",
+ camtm_parents, 0x100, 0x104, 0x108,
+ 0, 2, 7, 0x08, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP_SEL, "dsp_sel",
+ dsp_parents, 0x100, 0x104, 0x108,
+ 8, 3, 15, 0x08, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SR_PKA_SEL, "sr_pka_sel",
+ sr_pka_parents, 0x100, 0x104, 0x108,
+ 16, 3, 23, 0x0c, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DXCC_SEL, "dxcc_sel",
+ dxcc_parents, 0x100, 0x104, 0x108,
+ 24, 2, 31, 0x0c, 1),
+ /* CLK_CFG_16 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel",
+ mfg_ref_parents, 0x110, 0x114, 0x118,
+ 0, 2, 7, 0x0c, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MDP0_SEL, "mdp0_sel",
+ mmsys_parents, 0x110, 0x114, 0x118,
+ 8, 4, 15, 0x0c, 3),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DP_SEL, "dp_sel",
+ dp_parents, 0x110, 0x114, 0x118,
+ 16, 3, 23, 0x0c, 4),
+ MUX_CLR_SET_UPD(CLK_TOP_EDP_SEL, "edp_sel",
+ edp_parents, 0x110, 0x114, 0x118,
+ 24, 3, 0x0c, 5),
+ /* CLK_CFG_17 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_EDP_FAVT_SEL, "edp_favt_sel",
+ edp_parents, 0x180, 0x184, 0x188,
+ 0, 3, 7, 0x0c, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_250M_SEL, "snps_eth_250m_sel",
+ snps_eth_250m_parents, 0x180, 0x184, 0x188,
+ 8, 1, 15, 0x0c, 7),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_62P4M_PTP_SEL,
+ "snps_eth_62p4m_ptp_sel",
+ snps_eth_62p4m_ptp_parents,
+ 0x180, 0x184, 0x188, 16, 2, 23, 0x0c, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_50M_RMII_SEL,
+ "snps_eth_50m_rmii_sel",
+ snps_eth_50m_rmii_parents,
+ 0x180, 0x184, 0x188, 24, 1, 31, 0x0c, 9),
+ /* CLK_CFG_18 */
+ MUX_CLR_SET_UPD(CLK_TOP_SFLASH_SEL, "sflash_sel",
+ sflash_parents, 0x190, 0x194, 0x198,
+ 0, 3, 0x0c, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_GCPU_SEL, "gcpu_sel",
+ gcpu_parents, 0x190, 0x194, 0x198,
+ 8, 3, 15, 0x0c, 11),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MAC_TL_SEL, "pcie_mac_tl_sel",
+ pcie_mac_tl_parents, 0x190, 0x194, 0x198,
+ 16, 2, 23, 0x0c, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VDSTX_DG_CTS_SEL, "vdstx_dg_cts_sel",
+ vdstx_dg_cts_parents, 0x190, 0x194, 0x198,
+ 24, 2, 31, 0x0c, 13),
+ /* CLK_CFG_19 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PLL_DPIX_SEL, "pll_dpix_sel",
+ pll_dpix_parents, 0x240, 0x244, 0x248,
+ 0, 2, 7, 0x0c, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ECC_SEL, "ecc_sel",
+ ecc_parents, 0x240, 0x244, 0x248,
+ 8, 3, 15, 0x0c, 15),
+ /* CLK_MISC_CFG_3 */
+ GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MFG_SEL_MFGPLL, "mfg_sel_mfgpll",
+ mfg_sel_mfgpll_parents,
+ 0x510, 0x514, 0x0518, 16, 1, 0, -1, -1,
+ CLK_SET_RATE_PARENT,
+ mtk_mux_clr_set_upd_ops)
+};
+
+static const struct mtk_composite top_composites[] = {
+ /* CLK_AUDDIV_0 */
+ MUX(CLK_TOP_APLL_I2SIN0_MCK_SEL, "apll_i2sin0_m_sel",
+ apll_m_parents, 0x0320, 16, 1),
+ MUX(CLK_TOP_APLL_I2SIN1_MCK_SEL, "apll_i2sin1_m_sel",
+ apll_m_parents, 0x0320, 17, 1),
+ MUX(CLK_TOP_APLL_I2SIN2_MCK_SEL, "apll_i2sin2_m_sel",
+ apll_m_parents, 0x0320, 18, 1),
+ MUX(CLK_TOP_APLL_I2SIN3_MCK_SEL, "apll_i2sin3_m_sel",
+ apll_m_parents, 0x0320, 19, 1),
+ MUX(CLK_TOP_APLL_I2SIN4_MCK_SEL, "apll_i2sin4_m_sel",
+ apll_m_parents, 0x0320, 20, 1),
+ MUX(CLK_TOP_APLL_I2SIN6_MCK_SEL, "apll_i2sin6_m_sel",
+ apll_m_parents, 0x0320, 21, 1),
+ MUX(CLK_TOP_APLL_I2SOUT0_MCK_SEL, "apll_i2sout0_m_sel",
+ apll_m_parents, 0x0320, 22, 1),
+ MUX(CLK_TOP_APLL_I2SOUT1_MCK_SEL, "apll_i2sout1_m_sel",
+ apll_m_parents, 0x0320, 23, 1),
+ MUX(CLK_TOP_APLL_I2SOUT2_MCK_SEL, "apll_i2sout2_m_sel",
+ apll_m_parents, 0x0320, 24, 1),
+ MUX(CLK_TOP_APLL_I2SOUT3_MCK_SEL, "apll_i2sout3_m_sel",
+ apll_m_parents, 0x0320, 25, 1),
+ MUX(CLK_TOP_APLL_I2SOUT4_MCK_SEL, "apll_i2sout4_m_sel",
+ apll_m_parents, 0x0320, 26, 1),
+ MUX(CLK_TOP_APLL_I2SOUT6_MCK_SEL, "apll_i2sout6_m_sel",
+ apll_m_parents, 0x0320, 27, 1),
+ MUX(CLK_TOP_APLL_FMI2S_MCK_SEL, "apll_fmi2s_m_sel",
+ apll_m_parents, 0x0320, 28, 1),
+ MUX(CLK_TOP_APLL_TDMOUT_MCK_SEL, "apll_tdmout_m_sel",
+ apll_m_parents, 0x0320, 29, 1),
+ /* CLK_AUDDIV_2 */
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_I2SIN0, "apll12_div_i2sin0",
+ "apll_i2sin0_m_sel", 0x0320, 0, 0x0328, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_I2SIN1, "apll12_div_i2sin1",
+ "apll_i2sin1_m_sel", 0x0320, 1, 0x0328, 8, 8),
+ /* CLK_AUDDIV_3 */
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_I2SOUT0, "apll12_div_i2sout0",
+ "apll_i2sout0_m_sel", 0x0320, 6, 0x0334, 8, 16),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_I2SOUT1, "apll12_div_i2sout1",
+ "apll_i2sout1_m_sel", 0x0320, 7, 0x0334, 8, 24),
+ /* CLK_AUDDIV_5 */
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_FMI2S, "apll12_div_fmi2s",
+ "apll_fmi2s_m_sel", 0x0320, 12, 0x033c, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_TDMOUT_M, "apll12_div_tdmout_m",
+ "apll_tdmout_m_sel", 0x0320, 13, 0x033c, 8, 8),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV_TDMOUT_B, "apll12_div_tdmout_b",
+ "apll12_div_tdmout_m", 0x0320, 14, 0x033c, 8, 16),
+};
+
+static const struct mtk_gate_regs top_cg_regs = {
+ .set_ofs = 0x514,
+ .clr_ofs = 0x518,
+ .sta_ofs = 0x510,
+};
+
+#define GATE_TOP_FLAGS(_id, _name, _parent, _shift, _flag) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &top_cg_regs, \
+ .shift = _shift, \
+ .flags = _flag, \
+ .ops = &mtk_clk_gate_ops_setclr_inv, \
+ }
+
+#define GATE_TOP(_id, _name, _parent, _shift) \
+ GATE_TOP_FLAGS(_id, _name, _parent, _shift, 0)
+
+static const struct mtk_gate top_clks[] = {
+ GATE_TOP_FLAGS(CLK_TOP_FMCNT_P0_EN, "fmcnt_p0_en", "univpll_192m_d4", 0, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_FMCNT_P1_EN, "fmcnt_p1_en", "univpll_192m_d4", 1, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_FMCNT_P2_EN, "fmcnt_p2_en", "univpll_192m_d4", 2, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_FMCNT_P3_EN, "fmcnt_p3_en", "univpll_192m_d4", 3, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_FMCNT_P4_EN, "fmcnt_p4_en", "univpll_192m_d4", 4, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_USB_F26M_CK_EN, "ssusb_f26m", "clk26m", 5, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_SSPXTP_F26M_CK_EN, "sspxtp_f26m", "clk26m", 6, CLK_IS_CRITICAL),
+ GATE_TOP(CLK_TOP_USB2_PHY_RF_P0_EN, "usb2_phy_rf_p0_en", "ssusb_f26m", 7),
+ GATE_TOP(CLK_TOP_USB2_PHY_RF_P1_EN, "usb2_phy_rf_p1_en", "ssusb_f26m", 10),
+ GATE_TOP(CLK_TOP_USB2_PHY_RF_P2_EN, "usb2_phy_rf_p2_en", "ssusb_f26m", 11),
+ GATE_TOP(CLK_TOP_USB2_PHY_RF_P3_EN, "usb2_phy_rf_p3_en", "ssusb_f26m", 12),
+ GATE_TOP(CLK_TOP_USB2_PHY_RF_P4_EN, "usb2_phy_rf_p4_en", "ssusb_f26m", 13),
+ GATE_TOP_FLAGS(CLK_TOP_USB2_26M_CK_P0_EN, "usb2_26m_p0_en", "ssusb_f26m", 14, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_USB2_26M_CK_P1_EN, "usb2_26m_p1_en", "ssusb_f26m", 15, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_USB2_26M_CK_P2_EN, "usb2_26m_p2_en", "ssusb_f26m", 18, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_USB2_26M_CK_P3_EN, "usb2_26m_p3_en", "ssusb_f26m", 19, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_USB2_26M_CK_P4_EN, "usb2_26m_p4_en", "ssusb_f26m", 20, CLK_IS_CRITICAL),
+ GATE_TOP(CLK_TOP_F26M_CK_EN, "pcie_f26m", "sspxtp_f26m", 21),
+ GATE_TOP_FLAGS(CLK_TOP_AP2CON_EN, "ap2con", "clk26m", 24, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_EINT_N_EN, "eint_n", "clk26m", 25, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_TOPCKGEN_FMIPI_CSI_UP26M_CK_EN,
+ "fmipi_csi_up26m", "osc_d10", 26, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_EINT_E_EN, "eint_e", "clk26m", 28, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_EINT_W_EN, "eint_w", "clk26m", 30, CLK_IS_CRITICAL),
+ GATE_TOP_FLAGS(CLK_TOP_EINT_S_EN, "eint_s", "clk26m", 31, CLK_IS_CRITICAL),
+};
+
+/* Register mux notifier for MFG mux */
+static int clk_mt8189_reg_mfg_mux_notifier(struct device *dev,
+ struct clk *clk)
+{
+ struct mtk_mux_nb *mfg_mux_nb;
+
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
+ return -ENOMEM;
+
+ mfg_mux_nb->ops = &mtk_mux_clr_set_upd_ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to CLK_TOP_MFG_REF_SEL */
+
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
+}
+
+static const struct mtk_clk_desc topck_desc = {
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_composites,
+ .num_composite_clks = ARRAY_SIZE(top_composites),
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .clk_notifier_func = clk_mt8189_reg_mfg_mux_notifier,
+ .mfg_clk_idx = CLK_TOP_MFG_SEL_MFGPLL,
+ .clk_lock = &mt8189_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt8189_topck[] = {
+ { .compatible = "mediatek,mt8189-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_topck);
+
+static struct platform_driver clk_mt8189_topck_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8189-topck",
+ .of_match_table = of_match_clk_mt8189_topck,
+ },
+};
+module_platform_driver(clk_mt8189_topck_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 topckgen clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH v3 09/18] clk: mediatek: Add MT8189 apmixedsys clock support
From: Louis-Alexis Eyraud @ 2026-07-20 10:04 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, Chen-Yu Tsai
Cc: kernel, linux-clk, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, netdev, Irving-CH Lin, Louis-Alexis Eyraud
In-Reply-To: <20260720-mt8189-clocks-system-base-v3-0-8e6d99ab3ad2@collabora.com>
Add support for the MT8189 apmixedsys clock controller, which provides
PLLs generated from SoC 26m.
Co-developed-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Signed-off-by: Irving-CH Lin <irving-ch.lin@mediatek.com>
Co-developed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
drivers/clk/mediatek/Kconfig | 13 ++
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-mt8189-apmixedsys.c | 196 +++++++++++++++++++++++++++
3 files changed, 210 insertions(+)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 2c09fd729bab..f67dfb6cd019 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -815,6 +815,19 @@ config COMMON_CLK_MT8188_WPESYS
help
This driver supports MediaTek MT8188 Warp Engine clocks.
+config COMMON_CLK_MT8189
+ tristate "Clock driver for MediaTek MT8189"
+ depends on ARM64 || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
+ default ARCH_MEDIATEK
+ help
+ Enable this option to support the clock management for MediaTek MT8189 SoC. This
+ includes handling of all primary clock functions and features specific to the MT8189
+ platform. Enabling this driver ensures that the system's clock functionality aligns
+ with the MediaTek MT8189 hardware capabilities, providing efficient management of
+ clock speeds and power consumption.
+
config COMMON_CLK_MT8192
tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index d8736a060dbd..66577ccb9b93 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -123,6 +123,7 @@ obj-$(CONFIG_COMMON_CLK_MT8188_VDOSYS) += clk-mt8188-vdo0.o clk-mt8188-vdo1.o
obj-$(CONFIG_COMMON_CLK_MT8188_VENCSYS) += clk-mt8188-venc.o
obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
+obj-$(CONFIG_COMMON_CLK_MT8189) += clk-mt8189-apmixedsys.o
obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8189-apmixedsys.c b/drivers/clk/mediatek/clk-mt8189-apmixedsys.c
new file mode 100644
index 000000000000..0657a50c30d9
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8189-apmixedsys.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 MediaTek Inc.
+ * Qiqi Wang <qiqi.wang@mediatek.com>
+ * Irving-CH Lin <irving-ch.lin@mediatek.com>
+ * Copyright (C) 2026 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk-fhctl.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#include <dt-bindings/clock/mediatek,mt8189-clk.h>
+
+#define MT8189_PLL_FMAX (3800UL * MHZ)
+#define MT8189_PLL_FMIN (1500UL * MHZ)
+#define MT8189_PLLEN_OFS 0x70
+#define MT8189_INTEGER_BITS 8
+
+#define PLL_SETCLR(_id, _name, _reg, _en_setclr_bit, \
+ _rstb_setclr_bit, _flags, _pd_reg, \
+ _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _pcwbits) { \
+ .id = _id, \
+ .name = _name, \
+ .en_reg = MT8189_PLLEN_OFS, \
+ .reg = _reg, \
+ .pll_en_bit = _en_setclr_bit, \
+ .rst_bar_mask = BIT(_rstb_setclr_bit), \
+ .flags = _flags, \
+ .fmax = MT8189_PLL_FMAX, \
+ .fmin = MT8189_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8189_INTEGER_BITS, \
+ }
+
+static const struct mtk_pll_data apmixed_plls[] = {
+ PLL_SETCLR(CLK_APMIXED_ARMPLL_LL, "armpll-ll", 0x204, 18,
+ 0, PLL_AO, 0x208, 24, 0, 0, 0, 0x208, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_ARMPLL_BL, "armpll-bl", 0x214, 17,
+ 0, PLL_AO, 0x218, 24, 0, 0, 0, 0x218, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_CCIPLL, "ccipll", 0x224, 16,
+ 0, PLL_AO, 0x228, 24, 0, 0, 0, 0x228, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_MAINPLL, "mainpll", 0x304, 15,
+ 23, HAVE_RST_BAR | PLL_AO,
+ 0x308, 24, 0, 0, 0, 0x308, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_UNIVPLL, "univpll", 0x314, 14,
+ 23, HAVE_RST_BAR, 0x318, 24, 0, 0, 0, 0x318, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_MMPLL, "mmpll", 0x324, 13,
+ 23, HAVE_RST_BAR, 0x328, 24, 0, 0, 0, 0x328, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_MFGPLL, "mfgpll", 0x504, 7,
+ 0, 0, 0x508, 24, 0, 0, 0, 0x508, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_APLL1, "apll1", 0x404, 11,
+ 0, 0, 0x408, 24, 0x040, 0x00c, 0, 0x40c, 0, 32),
+ PLL_SETCLR(CLK_APMIXED_APLL2, "apll2", 0x418, 10,
+ 0, 0, 0x41c, 24, 0x044, 0x00c, 1, 0x420, 0, 32),
+ PLL_SETCLR(CLK_APMIXED_EMIPLL, "emipll", 0x334, 12,
+ 0, PLL_AO, 0x338, 24, 0, 0, 0, 0x338, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_APUPLL2, "apupll2", 0x614, 2,
+ 0, 0, 0x618, 24, 0, 0, 0, 0x618, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_APUPLL, "apupll", 0x604, 3,
+ 0, 0, 0x608, 24, 0, 0, 0, 0x608, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_TVDPLL1, "tvdpll1", 0x42c, 9,
+ 0, 0, 0x430, 24, 0, 0, 0, 0x430, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_TVDPLL2, "tvdpll2", 0x43c, 8,
+ 0, 0, 0x440, 24, 0, 0, 0, 0x440, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_ETHPLL, "ethpll", 0x514, 6,
+ 0, 0, 0x518, 24, 0, 0, 0, 0x518, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_MSDCPLL, "msdcpll", 0x524, 5,
+ 0, 0, 0x528, 24, 0, 0, 0, 0x528, 0, 22),
+ PLL_SETCLR(CLK_APMIXED_UFSPLL, "ufspll", 0x534, 4,
+ 0, 0, 0x538, 24, 0, 0, 0, 0x538, 0, 22),
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V2, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ARMPLL_LL, 0, 0x003C),
+ FH(CLK_APMIXED_ARMPLL_BL, 1, 0x0050),
+ FH(CLK_APMIXED_CCIPLL, 2, 0x0064),
+ FH(CLK_APMIXED_MAINPLL, 3, 0x0078),
+ FH(CLK_APMIXED_MMPLL, 4, 0x008C),
+ FH(CLK_APMIXED_MFGPLL, 5, 0x00A0),
+ FH(CLK_APMIXED_EMIPLL, 6, 0x00B4),
+ FH(CLK_APMIXED_TVDPLL1, 7, 0x00C8),
+ FH(CLK_APMIXED_TVDPLL2, 8, 0x00DC),
+ FH(CLK_APMIXED_MSDCPLL, 9, 0x00F0),
+ FH(CLK_APMIXED_UFSPLL, 10, 0x0104),
+ FH(CLK_APMIXED_APUPLL, 11, 0x0118),
+ FH(CLK_APMIXED_APUPLL2, 12, 0x012c),
+};
+
+static const struct of_device_id of_match_clk_mt8189_apmixed[] = {
+ { .compatible = "mediatek,mt8189-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8189_apmixed);
+
+static int clk_mt8189_apmixed_probe(struct platform_device *pdev)
+{
+ int r;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ const u8 *fhctl_node = "mediatek,mt8189-fhctl";
+
+ clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
+ if (!clk_data)
+ return -ENOMEM;
+
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+
+ r = mtk_clk_register_pllfhs(&pdev->dev, apmixed_plls, ARRAY_SIZE(apmixed_plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
+ if (r)
+ goto free_apmixed_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return 0;
+
+unregister_plls:
+ mtk_clk_unregister_pllfhs(apmixed_plls, ARRAY_SIZE(apmixed_plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+free_apmixed_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static void clk_mt8189_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_pllfhs(apmixed_plls, ARRAY_SIZE(apmixed_plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8189_apmixed_drv = {
+ .probe = clk_mt8189_apmixed_probe,
+ .remove = clk_mt8189_apmixed_remove,
+ .driver = {
+ .name = "clk-mt8189-apmixed",
+ .of_match_table = of_match_clk_mt8189_apmixed,
+ },
+};
+module_platform_driver(clk_mt8189_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8189 apmixed clocks driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox