From db1cc4902f2f51977e427f796ea8daf49ba93c69 Mon Sep 17 00:00:00 2001 From: Raghav Sharma Date: Wed, 19 Nov 2025 17:17:42 +0530 Subject: [PATCH 01/11] dt-bindings: clock: exynosautov920: add MFD clock definitions Add device tree clock binding definitions for CMU_MFD Signed-off-by: Raghav Sharma Reviewed-by: Alim Akhtar Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20251119114744.1914416-2-raghav.s@samsung.com Signed-off-by: Krzysztof Kozlowski --- .../clock/samsung,exynosautov920-clock.yaml | 19 +++++++++++++++++++ .../clock/samsung,exynosautov920.h | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml index 5bf905f88a1a..1318720193b3 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml @@ -40,6 +40,7 @@ properties: - samsung,exynosautov920-cmu-hsi2 - samsung,exynosautov920-cmu-m2m - samsung,exynosautov920-cmu-mfc + - samsung,exynosautov920-cmu-mfd - samsung,exynosautov920-cmu-misc - samsung,exynosautov920-cmu-peric0 - samsung,exynosautov920-cmu-peric1 @@ -268,6 +269,24 @@ allOf: - const: mfc - const: wfd + - if: + properties: + compatible: + contains: + const: samsung,exynosautov920-cmu-mfd + + then: + properties: + clocks: + items: + - description: External reference clock (38.4 MHz) + - description: CMU_MFD NOC clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: noc + required: - compatible - "#clock-cells" diff --git a/include/dt-bindings/clock/samsung,exynosautov920.h b/include/dt-bindings/clock/samsung,exynosautov920.h index 970d05167fc6..06dec27a8c77 100644 --- a/include/dt-bindings/clock/samsung,exynosautov920.h +++ b/include/dt-bindings/clock/samsung,exynosautov920.h @@ -305,4 +305,8 @@ #define CLK_MOUT_MFC_WFD_USER 2 #define CLK_DOUT_MFC_NOCP 3 +/* CMU_MFD */ +#define CLK_MOUT_MFD_NOC_USER 1 +#define CLK_DOUT_MFD_NOCP 2 + #endif /* _DT_BINDINGS_CLOCK_EXYNOSAUTOV920_H */ From efa45bcc73e1a30705eed28933e341d36a08bb84 Mon Sep 17 00:00:00 2001 From: Raghav Sharma Date: Wed, 19 Nov 2025 17:17:43 +0530 Subject: [PATCH 02/11] clk: samsung: exynosautov920: add clock support Add support for CMU_MFD which provides clocks to MFD block, and register the required compatible and cmu_info for the same. Signed-off-by: Raghav Sharma Reviewed-by: Alim Akhtar Link: https://patch.msgid.link/20251119114744.1914416-3-raghav.s@samsung.com Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynosautov920.c | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/clk/samsung/clk-exynosautov920.c b/drivers/clk/samsung/clk-exynosautov920.c index b90b73c3518f..d0617c7fff3a 100644 --- a/drivers/clk/samsung/clk-exynosautov920.c +++ b/drivers/clk/samsung/clk-exynosautov920.c @@ -29,6 +29,7 @@ #define CLKS_NR_HSI2 (CLK_DOUT_HSI2_ETHERNET_PTP + 1) #define CLKS_NR_M2M (CLK_DOUT_M2M_NOCP + 1) #define CLKS_NR_MFC (CLK_DOUT_MFC_NOCP + 1) +#define CLKS_NR_MFD (CLK_DOUT_MFD_NOCP + 1) /* ---- CMU_TOP ------------------------------------------------------------ */ @@ -1905,6 +1906,42 @@ static const struct samsung_cmu_info mfc_cmu_info __initconst = { .clk_name = "noc", }; +/* ---- CMU_MFD --------------------------------------------------------- */ + +/* Register Offset definitions for CMU_MFD (0x19e00000) */ +#define PLL_CON0_MUX_CLKCMU_MFD_NOC_USER 0x600 +#define CLK_CON_DIV_DIV_CLK_MFD_NOCP 0x1800 + +static const unsigned long mfd_clk_regs[] __initconst = { + PLL_CON0_MUX_CLKCMU_MFD_NOC_USER, + CLK_CON_DIV_DIV_CLK_MFD_NOCP, +}; + +/* List of parent clocks for Muxes in CMU_MFD */ +PNAME(mout_clkcmu_mfd_noc_user_p) = { "oscclk", "dout_clkcmu_mfd_noc" }; + +static const struct samsung_mux_clock mfd_mux_clks[] __initconst = { + MUX(CLK_MOUT_MFD_NOC_USER, "mout_clkcmu_mfd_noc_user", + mout_clkcmu_mfd_noc_user_p, PLL_CON0_MUX_CLKCMU_MFD_NOC_USER, 4, 1), +}; + +static const struct samsung_div_clock mfd_div_clks[] __initconst = { + DIV(CLK_DOUT_MFD_NOCP, "dout_mfd_nocp", + "mout_clkcmu_mfd_noc_user", CLK_CON_DIV_DIV_CLK_MFD_NOCP, + 0, 3), +}; + +static const struct samsung_cmu_info mfd_cmu_info __initconst = { + .mux_clks = mfd_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mfd_mux_clks), + .div_clks = mfd_div_clks, + .nr_div_clks = ARRAY_SIZE(mfd_div_clks), + .nr_clk_ids = CLKS_NR_MFD, + .clk_regs = mfd_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mfd_clk_regs), + .clk_name = "noc", +}; + static int __init exynosautov920_cmu_probe(struct platform_device *pdev) { const struct samsung_cmu_info *info; @@ -1941,6 +1978,9 @@ static const struct of_device_id exynosautov920_cmu_of_match[] = { }, { .compatible = "samsung,exynosautov920-cmu-mfc", .data = &mfc_cmu_info, + }, { + .compatible = "samsung,exynosautov920-cmu-mfd", + .data = &mfd_cmu_info, }, { } }; From 2e8e9a2492edbbef54de6ab1c5bf1578ffef4c8c Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Mon, 22 Dec 2025 10:22:12 +0000 Subject: [PATCH 03/11] dt-bindings: clock: google,gs101-clock: add samsung,sysreg property as required MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each CMU (with the exception of cmu_top) has a corresponding sysreg bank that contains the BUSCOMPONENT_DRCG_EN and optional MEMCLK registers. The BUSCOMPONENT_DRCG_EN register enables dynamic root clock gating of bus components and MEMCLK gates the sram clock. Now the clock driver supports automatic clock mode, to fully enable dynamic root clock gating it is required to configure these registers. Update the bindings documentation so that all CMUs (with the exception of gs101-cmu-top) have samsung,sysreg as a required property. Note this is NOT an ABI break, as if the property isn't specified the clock driver will fallback to the current behaviour of not initializing the registers. The system still boots, but bus components won't benefit from dynamic root clock gating and dynamic power will be higher (which has been the case until now anyway). Additionally update the DT example to included the correct CMU size as registers in that region are used for automatic clock mode. Acked-by: Rob Herring (Arm) Reviewed-by: André Draszik Reviewed-by: Rob Herring (Arm) Signed-off-by: Peter Griffin Link: https://patch.msgid.link/20251222-automatic-clocks-v7-1-fec86fa89874@linaro.org Signed-off-by: Krzysztof Kozlowski --- .../bindings/clock/google,gs101-clock.yaml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml index 31e106ef913d..09e679c1a9de 100644 --- a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml +++ b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml @@ -52,6 +52,11 @@ properties: reg: maxItems: 1 + samsung,sysreg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to system registers interface. + required: - compatible - "#clock-cells" @@ -166,6 +171,18 @@ allOf: - const: bus - const: ip + - if: + properties: + compatible: + contains: + const: google,gs101-cmu-top + then: + properties: + samsung,sysreg: false + else: + required: + - samsung,sysreg + additionalProperties: false examples: @@ -175,7 +192,7 @@ examples: cmu_top: clock-controller@1e080000 { compatible = "google,gs101-cmu-top"; - reg = <0x1e080000 0x8000>; + reg = <0x1e080000 0x10000>; #clock-cells = <1>; clocks = <&ext_24_5m>; clock-names = "oscclk"; From 298fac4f4b96448ce7ef8da525a74fd3dba0035d Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Mon, 22 Dec 2025 10:22:14 +0000 Subject: [PATCH 04/11] clk: samsung: Implement automatic clock gating mode for CMUs Update exynos_arm64_init_clocks() so that it enables the automatic clock mode bits in the CMU option register if the auto_clock_gate flag and option_offset fields are set for the CMU. To ensure compatibility with older DTs (that specified an incorrect CMU reg size), detect this and fallback to manual clock gate mode as the auto clock mode feature depends on registers in this area. The CMU option register bits are global and effect every clock component in the CMU, as such clearing the GATE_ENABLE_HWACG bit and setting GATE_MANUAL bit on every gate register is only required if auto_clock_gate is false. Additionally if auto_clock_gate is enabled the dynamic root clock gating and memclk registers will be configured in the corresponding CMUs sysreg bank. These registers are exposed via syscon, so the register samsung_clk_save/restore paths are updated to also take a regmap. As many gates for various Samsung SoCs are already exposed in the Samsung clock drivers a new samsung_auto_clk_gate_ops is implemented. This uses some CMU debug registers to report whether clocks are enabled or disabled when operating in automatic mode. This allows /sys/kernel/debug/clk/clk_summary to still dump the entire clock tree and correctly report the status of each clock in the system. Signed-off-by: Peter Griffin Link: https://patch.msgid.link/20251222-automatic-clocks-v7-3-fec86fa89874@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynos-arm64.c | 62 ++++++- drivers/clk/samsung/clk-exynos4.c | 12 +- drivers/clk/samsung/clk-exynos4412-isp.c | 4 +- drivers/clk/samsung/clk-exynos5250.c | 2 +- drivers/clk/samsung/clk-exynos5420.c | 4 +- drivers/clk/samsung/clk-s3c64xx.c | 4 +- drivers/clk/samsung/clk-s5pv210.c | 2 +- drivers/clk/samsung/clk.c | 200 +++++++++++++++++++++-- drivers/clk/samsung/clk.h | 55 ++++++- 9 files changed, 302 insertions(+), 43 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos-arm64.c b/drivers/clk/samsung/clk-exynos-arm64.c index bf7de21f329e..11e4d49f2390 100644 --- a/drivers/clk/samsung/clk-exynos-arm64.c +++ b/drivers/clk/samsung/clk-exynos-arm64.c @@ -24,6 +24,16 @@ #define GATE_MANUAL BIT(20) #define GATE_ENABLE_HWACG BIT(28) +/* Option register bits */ +#define OPT_EN_MEM_PWR_GATING BIT(24) +#define OPT_EN_AUTO_GATING BIT(28) +#define OPT_EN_PWR_MANAGEMENT BIT(29) +#define OPT_EN_LAYER2_CTRL BIT(30) +#define OPT_EN_DBG BIT(31) + +#define CMU_OPT_GLOBAL_EN_AUTO_GATING (OPT_EN_DBG | OPT_EN_LAYER2_CTRL | \ + OPT_EN_PWR_MANAGEMENT | OPT_EN_AUTO_GATING | OPT_EN_MEM_PWR_GATING) + /* PLL_CONx_PLL register offsets range */ #define PLL_CON_OFF_START 0x100 #define PLL_CON_OFF_END 0x600 @@ -37,6 +47,8 @@ struct exynos_arm64_cmu_data { unsigned int nr_clk_save; const struct samsung_clk_reg_dump *clk_suspend; unsigned int nr_clk_suspend; + struct samsung_clk_reg_dump *clk_sysreg_save; + unsigned int nr_clk_sysreg; struct clk *clk; struct clk **pclks; @@ -76,19 +88,41 @@ static void __init exynos_arm64_init_clocks(struct device_node *np, const unsigned long *reg_offs = cmu->clk_regs; size_t reg_offs_len = cmu->nr_clk_regs; void __iomem *reg_base; + bool init_auto; size_t i; reg_base = of_iomap(np, 0); if (!reg_base) panic("%s: failed to map registers\n", __func__); + /* ensure compatibility with older DTs */ + if (cmu->auto_clock_gate && samsung_is_auto_capable(np)) + init_auto = true; + else + init_auto = false; + + if (cmu->option_offset && init_auto) { + /* + * Enable the global automatic mode for the entire CMU. + * This overrides the individual HWACG bits in each of the + * individual gate, mux and qch registers. + */ + writel(CMU_OPT_GLOBAL_EN_AUTO_GATING, + reg_base + cmu->option_offset); + } + for (i = 0; i < reg_offs_len; ++i) { void __iomem *reg = reg_base + reg_offs[i]; u32 val; if (cmu->manual_plls && is_pll_con1_reg(reg_offs[i])) { writel(PLL_CON1_MANUAL, reg); - } else if (is_gate_reg(reg_offs[i])) { + } else if (is_gate_reg(reg_offs[i]) && !init_auto) { + /* + * Setting GATE_MANUAL bit (which is described in TRM as + * reserved!) overrides the global CMU automatic mode + * option. + */ val = readl(reg); val |= GATE_MANUAL; val &= ~GATE_ENABLE_HWACG; @@ -210,8 +244,8 @@ void __init exynos_arm64_register_cmu(struct device *dev, /** * exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support * - * @pdev: Platform device object - * @set_manual: If true, set gate clocks to manual mode + * @pdev: Platform device object + * @init_clk_regs: If true, initialize CMU registers * * It's a version of exynos_arm64_register_cmu() with PM support. Should be * called from probe function of platform driver. @@ -219,7 +253,7 @@ void __init exynos_arm64_register_cmu(struct device *dev, * Return: 0 on success, or negative error code on error. */ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, - bool set_manual) + bool init_clk_regs) { const struct samsung_cmu_info *cmu; struct device *dev = &pdev->dev; @@ -249,7 +283,7 @@ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, dev_err(dev, "%s: could not enable bus clock %s; err = %d\n", __func__, cmu->clk_name, ret); - if (set_manual) + if (init_clk_regs) exynos_arm64_init_clocks(np, cmu); reg_base = devm_platform_ioremap_resource(pdev, 0); @@ -268,8 +302,10 @@ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, pm_runtime_set_active(dev); pm_runtime_enable(dev); - samsung_cmu_register_clocks(data->ctx, cmu); + samsung_cmu_register_clocks(data->ctx, cmu, np); samsung_clk_of_add_provider(dev->of_node, data->ctx); + /* sysreg DT nodes reference a clock in this CMU */ + samsung_en_dyn_root_clk_gating(np, data->ctx, cmu); pm_runtime_put_sync(dev); return 0; @@ -280,14 +316,17 @@ int exynos_arm64_cmu_suspend(struct device *dev) struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); int i; - samsung_clk_save(data->ctx->reg_base, data->clk_save, + samsung_clk_save(data->ctx->reg_base, NULL, data->clk_save, data->nr_clk_save); + samsung_clk_save(NULL, data->ctx->sysreg, data->clk_sysreg_save, + data->nr_clk_sysreg); + for (i = 0; i < data->nr_pclks; i++) clk_prepare_enable(data->pclks[i]); /* For suspend some registers have to be set to certain values */ - samsung_clk_restore(data->ctx->reg_base, data->clk_suspend, + samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_suspend, data->nr_clk_suspend); for (i = 0; i < data->nr_pclks; i++) @@ -308,9 +347,14 @@ int exynos_arm64_cmu_resume(struct device *dev) for (i = 0; i < data->nr_pclks; i++) clk_prepare_enable(data->pclks[i]); - samsung_clk_restore(data->ctx->reg_base, data->clk_save, + samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_save, data->nr_clk_save); + if (data->ctx->sysreg) + samsung_clk_restore(NULL, data->ctx->sysreg, + data->clk_sysreg_save, + data->nr_clk_sysreg); + for (i = 0; i < data->nr_pclks; i++) clk_disable_unprepare(data->pclks[i]); diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index cc5c1644c41c..246bd28bac2d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1361,12 +1361,12 @@ static void __init exynos4_clk_init(struct device_node *np, ARRAY_SIZE(exynos4x12_plls)); } - samsung_cmu_register_clocks(ctx, &cmu_info_exynos4); + samsung_cmu_register_clocks(ctx, &cmu_info_exynos4, np); if (exynos4_soc == EXYNOS4210) { - samsung_cmu_register_clocks(ctx, &cmu_info_exynos4210); + samsung_cmu_register_clocks(ctx, &cmu_info_exynos4210, np); } else { - samsung_cmu_register_clocks(ctx, &cmu_info_exynos4x12); + samsung_cmu_register_clocks(ctx, &cmu_info_exynos4x12, np); if (soc == EXYNOS4412) samsung_clk_register_cpu(ctx, exynos4412_cpu_clks, ARRAY_SIZE(exynos4412_cpu_clks)); @@ -1378,15 +1378,15 @@ static void __init exynos4_clk_init(struct device_node *np, if (soc == EXYNOS4212 || soc == EXYNOS4412) exynos4x12_core_down_clock(); - samsung_clk_extended_sleep_init(reg_base, + samsung_clk_extended_sleep_init(reg_base, NULL, exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), src_mask_suspend, ARRAY_SIZE(src_mask_suspend)); if (exynos4_soc == EXYNOS4210) - samsung_clk_extended_sleep_init(reg_base, + samsung_clk_extended_sleep_init(reg_base, NULL, exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save), src_mask_suspend_e4210, ARRAY_SIZE(src_mask_suspend_e4210)); else - samsung_clk_sleep_init(reg_base, exynos4x12_clk_save, + samsung_clk_sleep_init(reg_base, NULL, exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save)); samsung_clk_of_add_provider(np, ctx); diff --git a/drivers/clk/samsung/clk-exynos4412-isp.c b/drivers/clk/samsung/clk-exynos4412-isp.c index fa915057e109..772bc18a1e68 100644 --- a/drivers/clk/samsung/clk-exynos4412-isp.c +++ b/drivers/clk/samsung/clk-exynos4412-isp.c @@ -94,7 +94,7 @@ static int __maybe_unused exynos4x12_isp_clk_suspend(struct device *dev) { struct samsung_clk_provider *ctx = dev_get_drvdata(dev); - samsung_clk_save(ctx->reg_base, exynos4x12_save_isp, + samsung_clk_save(ctx->reg_base, NULL, exynos4x12_save_isp, ARRAY_SIZE(exynos4x12_clk_isp_save)); return 0; } @@ -103,7 +103,7 @@ static int __maybe_unused exynos4x12_isp_clk_resume(struct device *dev) { struct samsung_clk_provider *ctx = dev_get_drvdata(dev); - samsung_clk_restore(ctx->reg_base, exynos4x12_save_isp, + samsung_clk_restore(ctx->reg_base, NULL, exynos4x12_save_isp, ARRAY_SIZE(exynos4x12_clk_isp_save)); return 0; } diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index e90d3a0848cb..f97f30b29be7 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -854,7 +854,7 @@ static void __init exynos5250_clk_init(struct device_node *np) PWR_CTRL2_CORE2_UP_RATIO | PWR_CTRL2_CORE1_UP_RATIO); __raw_writel(tmp, reg_base + PWR_CTRL2); - samsung_clk_sleep_init(reg_base, exynos5250_clk_regs, + samsung_clk_sleep_init(reg_base, NULL, exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs)); exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5250_subcmus), exynos5250_subcmus); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index a9df4e6db82f..1982e0751cee 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -1649,12 +1649,12 @@ static void __init exynos5x_clk_init(struct device_node *np, ARRAY_SIZE(exynos5800_cpu_clks)); } - samsung_clk_extended_sleep_init(reg_base, + samsung_clk_extended_sleep_init(reg_base, NULL, exynos5x_clk_regs, ARRAY_SIZE(exynos5x_clk_regs), exynos5420_set_clksrc, ARRAY_SIZE(exynos5420_set_clksrc)); if (soc == EXYNOS5800) { - samsung_clk_sleep_init(reg_base, exynos5800_clk_regs, + samsung_clk_sleep_init(reg_base, NULL, exynos5800_clk_regs, ARRAY_SIZE(exynos5800_clk_regs)); exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5800_subcmus), diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c index 397a057af5d1..5a2d5a5703ff 100644 --- a/drivers/clk/samsung/clk-s3c64xx.c +++ b/drivers/clk/samsung/clk-s3c64xx.c @@ -449,10 +449,10 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, samsung_clk_register_alias(ctx, s3c64xx_clock_aliases, ARRAY_SIZE(s3c64xx_clock_aliases)); - samsung_clk_sleep_init(reg_base, s3c64xx_clk_regs, + samsung_clk_sleep_init(reg_base, NULL, s3c64xx_clk_regs, ARRAY_SIZE(s3c64xx_clk_regs)); if (!is_s3c6400) - samsung_clk_sleep_init(reg_base, s3c6410_clk_regs, + samsung_clk_sleep_init(reg_base, NULL, s3c6410_clk_regs, ARRAY_SIZE(s3c6410_clk_regs)); samsung_clk_of_add_provider(np, ctx); diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c index 9a4217cc1908..4ee4f2b5efbc 100644 --- a/drivers/clk/samsung/clk-s5pv210.c +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -782,7 +782,7 @@ static void __init __s5pv210_clk_init(struct device_node *np, samsung_clk_register_alias(ctx, s5pv210_aliases, ARRAY_SIZE(s5pv210_aliases)); - samsung_clk_sleep_init(reg_base, s5pv210_clk_regs, + samsung_clk_sleep_init(reg_base, NULL, s5pv210_clk_regs, ARRAY_SIZE(s5pv210_clk_regs)); samsung_clk_of_add_provider(np, ctx); diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index c149ca6c2217..06ea5deef4ee 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include "clk.h" @@ -21,19 +23,29 @@ static LIST_HEAD(clock_reg_cache_list); void samsung_clk_save(void __iomem *base, + struct regmap *regmap, struct samsung_clk_reg_dump *rd, unsigned int num_regs) { - for (; num_regs > 0; --num_regs, ++rd) - rd->value = readl(base + rd->offset); + for (; num_regs > 0; --num_regs, ++rd) { + if (base) + rd->value = readl(base + rd->offset); + else if (regmap) + regmap_read(regmap, rd->offset, &rd->value); + } } void samsung_clk_restore(void __iomem *base, + struct regmap *regmap, const struct samsung_clk_reg_dump *rd, unsigned int num_regs) { - for (; num_regs > 0; --num_regs, ++rd) - writel(rd->value, base + rd->offset); + for (; num_regs > 0; --num_regs, ++rd) { + if (base) + writel(rd->value, base + rd->offset); + else if (regmap) + regmap_write(regmap, rd->offset, rd->value); + } } struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( @@ -227,6 +239,103 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, } } +/* + * Some older DT's have an incorrect CMU resource size which is incompatible + * with the auto clock mode feature. In such cases we switch back to manual + * clock gating mode. + */ +bool samsung_is_auto_capable(struct device_node *np) +{ + struct resource res; + resource_size_t size; + + if (of_address_to_resource(np, 0, &res)) + return false; + + size = resource_size(&res); + if (size != 0x10000) { + pr_warn("%pOF: incorrect res size for automatic clocks\n", np); + return false; + } + return true; +} + +#define ACG_MSK GENMASK(6, 4) +#define CLK_IDLE GENMASK(5, 4) +static int samsung_auto_clk_gate_is_en(struct clk_hw *hw) +{ + u32 reg; + struct clk_gate *gate = to_clk_gate(hw); + + reg = readl(gate->reg); + return ((reg & ACG_MSK) == CLK_IDLE) ? 0 : 1; +} + +/* enable and disable are nops in automatic clock mode */ +static int samsung_auto_clk_gate_en(struct clk_hw *hw) +{ + return 0; +} + +static void samsung_auto_clk_gate_dis(struct clk_hw *hw) +{ +} + +static const struct clk_ops samsung_auto_clk_gate_ops = { + .enable = samsung_auto_clk_gate_en, + .disable = samsung_auto_clk_gate_dis, + .is_enabled = samsung_auto_clk_gate_is_en, +}; + +struct clk_hw *samsung_register_auto_gate(struct device *dev, + struct device_node *np, const char *name, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct clk_gate *gate; + struct clk_hw *hw; + struct clk_init_data init = {}; + int ret = -EINVAL; + + /* allocate the gate */ + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &samsung_auto_clk_gate_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.parent_hws = parent_hw ? &parent_hw : NULL; + init.parent_data = parent_data; + if (parent_name || parent_hw || parent_data) + init.num_parents = 1; + else + init.num_parents = 0; + + /* struct clk_gate assignments */ + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = clk_gate_flags; + gate->lock = lock; + gate->hw.init = &init; + + hw = &gate->hw; + if (dev || !np) + ret = clk_hw_register(dev, hw); + else if (np) + ret = of_clk_hw_register(np, hw); + if (ret) { + kfree(gate); + hw = ERR_PTR(ret); + } + + return hw; +} + /* register a list of gate clocks */ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, const struct samsung_gate_clock *list, @@ -234,14 +343,24 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, { struct clk_hw *clk_hw; unsigned int idx; + void __iomem *reg_offs; for (idx = 0; idx < nr_clk; idx++, list++) { - clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name, - list->flags, ctx->reg_base + list->offset, + reg_offs = ctx->reg_base + list->offset; + + if (ctx->auto_clock_gate && ctx->gate_dbg_offset) + clk_hw = samsung_register_auto_gate(ctx->dev, NULL, + list->name, list->parent_name, NULL, NULL, + list->flags, reg_offs + ctx->gate_dbg_offset, list->bit_idx, list->gate_flags, &ctx->lock); + else + clk_hw = clk_hw_register_gate(ctx->dev, list->name, + list->parent_name, list->flags, + ctx->reg_base + list->offset, list->bit_idx, + list->gate_flags, &ctx->lock); if (IS_ERR(clk_hw)) { - pr_err("%s: failed to register clock %s\n", __func__, - list->name); + pr_err("%s: failed to register clock %s: %ld\n", __func__, + list->name, PTR_ERR(clk_hw)); continue; } @@ -276,10 +395,11 @@ static int samsung_clk_suspend(void *data) struct samsung_clock_reg_cache *reg_cache; list_for_each_entry(reg_cache, &clock_reg_cache_list, node) { - samsung_clk_save(reg_cache->reg_base, reg_cache->rdump, - reg_cache->rd_num); - samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend, - reg_cache->rsuspend_num); + samsung_clk_save(reg_cache->reg_base, reg_cache->sysreg, + reg_cache->rdump, reg_cache->rd_num); + samsung_clk_restore(reg_cache->reg_base, reg_cache->sysreg, + reg_cache->rsuspend, + reg_cache->rsuspend_num); } return 0; } @@ -289,8 +409,8 @@ static void samsung_clk_resume(void *data) struct samsung_clock_reg_cache *reg_cache; list_for_each_entry(reg_cache, &clock_reg_cache_list, node) - samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump, - reg_cache->rd_num); + samsung_clk_restore(reg_cache->reg_base, reg_cache->sysreg, + reg_cache->rdump, reg_cache->rd_num); } static const struct syscore_ops samsung_clk_syscore_ops = { @@ -303,6 +423,7 @@ static struct syscore samsung_clk_syscore = { }; void samsung_clk_extended_sleep_init(void __iomem *reg_base, + struct regmap *sysreg, const unsigned long *rdump, unsigned long nr_rdump, const struct samsung_clk_reg_dump *rsuspend, @@ -323,6 +444,7 @@ void samsung_clk_extended_sleep_init(void __iomem *reg_base, register_syscore(&samsung_clk_syscore); reg_cache->reg_base = reg_base; + reg_cache->sysreg = sysreg; reg_cache->rd_num = nr_rdump; reg_cache->rsuspend = rsuspend; reg_cache->rsuspend_num = nr_rsuspend; @@ -334,10 +456,20 @@ void samsung_clk_extended_sleep_init(void __iomem *reg_base, * samsung_cmu_register_clocks() - Register all clocks provided in CMU object * @ctx: Clock provider object * @cmu: CMU object with clocks to register + * @np: CMU device tree node */ void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, - const struct samsung_cmu_info *cmu) + const struct samsung_cmu_info *cmu, + struct device_node *np) { + if (samsung_is_auto_capable(np) && cmu->auto_clock_gate) + ctx->auto_clock_gate = cmu->auto_clock_gate; + + ctx->gate_dbg_offset = cmu->gate_dbg_offset; + ctx->option_offset = cmu->option_offset; + ctx->drcg_offset = cmu->drcg_offset; + ctx->memclk_offset = cmu->memclk_offset; + if (cmu->pll_clks) samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks); if (cmu->mux_clks) @@ -357,6 +489,37 @@ void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks); } +/* Each bit enable/disables DRCG of a bus component */ +#define DRCG_EN_MSK GENMASK(31, 0) +#define MEMCLK_EN BIT(0) + +/* Enable Dynamic Root Clock Gating (DRCG) of bus components */ +void samsung_en_dyn_root_clk_gating(struct device_node *np, + struct samsung_clk_provider *ctx, + const struct samsung_cmu_info *cmu) +{ + if (!ctx->auto_clock_gate) + return; + + ctx->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg"); + if (IS_ERR(ctx->sysreg)) { + pr_warn("%pOF: Unable to get CMU sysreg\n", np); + ctx->sysreg = NULL; + } else { + /* Enable DRCG for all bus components */ + regmap_write(ctx->sysreg, ctx->drcg_offset, DRCG_EN_MSK); + /* Enable memclk gate (not present on all sysreg) */ + if (ctx->memclk_offset) + regmap_write_bits(ctx->sysreg, ctx->memclk_offset, + MEMCLK_EN, 0x0); + + samsung_clk_extended_sleep_init(NULL, ctx->sysreg, + cmu->sysreg_clk_regs, + cmu->nr_sysreg_clk_regs, + NULL, 0); + } +} + /* * Common function which registers plls, muxes, dividers and gates * for each CMU. It also add CMU register list to register cache. @@ -375,14 +538,17 @@ struct samsung_clk_provider * __init samsung_cmu_register_one( } ctx = samsung_clk_init(NULL, reg_base, cmu->nr_clk_ids); - samsung_cmu_register_clocks(ctx, cmu); + samsung_cmu_register_clocks(ctx, cmu, np); if (cmu->clk_regs) - samsung_clk_extended_sleep_init(reg_base, + samsung_clk_extended_sleep_init(reg_base, NULL, cmu->clk_regs, cmu->nr_clk_regs, cmu->suspend_regs, cmu->nr_suspend_regs); samsung_clk_of_add_provider(np, ctx); + /* sysreg DT nodes reference a clock in this CMU */ + samsung_en_dyn_root_clk_gating(np, ctx, cmu); + return ctx; } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 18660c1ac6f0..a56aa3be54d8 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -12,6 +12,7 @@ #include #include +#include #include "clk-pll.h" #include "clk-cpu.h" @@ -19,13 +20,25 @@ * struct samsung_clk_provider - information about clock provider * @reg_base: virtual address for the register base * @dev: clock provider device needed for runtime PM + * @sysreg: syscon regmap for clock-provider sysreg controller * @lock: maintains exclusion between callbacks for a given clock-provider + * @auto_clock_gate: enable auto clk mode for all clocks in clock-provider + * @gate_dbg_offset: gate debug reg offset. Used for all gates in auto clk mode + * @option_offset: option reg offset. Enables auto mode for clock-provider + * @drcg_offset: dynamic root clk gate enable register offset in sysreg + * @memclk_offset: memclk enable register offset in sysreg * @clk_data: holds clock related data like clk_hw* and number of clocks */ struct samsung_clk_provider { void __iomem *reg_base; struct device *dev; + struct regmap *sysreg; spinlock_t lock; + bool auto_clock_gate; + u32 gate_dbg_offset; + u32 option_offset; + u32 drcg_offset; + u32 memclk_offset; /* clk_data must be the last entry due to variable length 'hws' array */ struct clk_hw_onecell_data clk_data; }; @@ -310,6 +323,7 @@ struct samsung_cpu_clock { struct samsung_clock_reg_cache { struct list_head node; void __iomem *reg_base; + struct regmap *sysreg; struct samsung_clk_reg_dump *rdump; unsigned int rd_num; const struct samsung_clk_reg_dump *rsuspend; @@ -338,7 +352,14 @@ struct samsung_clock_reg_cache { * @suspend_regs: list of clock registers to set before suspend * @nr_suspend_regs: count of clock registers in @suspend_regs * @clk_name: name of the parent clock needed for CMU register access + * @sysreg_clk_regs: list of sysreg clock registers + * @nr_sysreg_clk_regs: count of clock registers in @sysreg_clk_regs * @manual_plls: Enable manual control for PLL clocks + * @auto_clock_gate: enable auto clock mode for all components in CMU + * @gate_dbg_offset: gate debug reg offset. Used by all gates in auto clk mode + * @option_offset: option reg offset. Enables auto clk mode for entire CMU + * @drcg_offset: dynamic root clk gate enable register offset in sysreg + * @memclk_offset: memclk enable register offset in sysreg */ struct samsung_cmu_info { const struct samsung_pll_clock *pll_clks; @@ -364,8 +385,16 @@ struct samsung_cmu_info { unsigned int nr_suspend_regs; const char *clk_name; + const unsigned long *sysreg_clk_regs; + unsigned int nr_sysreg_clk_regs; + /* ARM64 Exynos CMUs */ bool manual_plls; + bool auto_clock_gate; + u32 gate_dbg_offset; + u32 option_offset; + u32 drcg_offset; + u32 memclk_offset; }; struct samsung_clk_provider *samsung_clk_init(struct device *dev, @@ -408,35 +437,55 @@ void samsung_clk_register_cpu(struct samsung_clk_provider *ctx, const struct samsung_cpu_clock *list, unsigned int nr_clk); void samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, - const struct samsung_cmu_info *cmu); + const struct samsung_cmu_info *cmu, + struct device_node *np); struct samsung_clk_provider *samsung_cmu_register_one( struct device_node *, const struct samsung_cmu_info *); #ifdef CONFIG_PM_SLEEP void samsung_clk_extended_sleep_init(void __iomem *reg_base, + struct regmap *sysreg, const unsigned long *rdump, unsigned long nr_rdump, const struct samsung_clk_reg_dump *rsuspend, unsigned long nr_rsuspend); #else static inline void samsung_clk_extended_sleep_init(void __iomem *reg_base, + struct regmap *sysreg, const unsigned long *rdump, unsigned long nr_rdump, const struct samsung_clk_reg_dump *rsuspend, unsigned long nr_rsuspend) {} #endif -#define samsung_clk_sleep_init(reg_base, rdump, nr_rdump) \ - samsung_clk_extended_sleep_init(reg_base, rdump, nr_rdump, NULL, 0) +#define samsung_clk_sleep_init(reg_base, sysreg, rdump, nr_rdump) \ + samsung_clk_extended_sleep_init(reg_base, sysreg, rdump, nr_rdump, \ + NULL, 0) void samsung_clk_save(void __iomem *base, + struct regmap *regmap, struct samsung_clk_reg_dump *rd, unsigned int num_regs); void samsung_clk_restore(void __iomem *base, + struct regmap *regmap, const struct samsung_clk_reg_dump *rd, unsigned int num_regs); struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( const unsigned long *rdump, unsigned long nr_rdump); +void samsung_en_dyn_root_clk_gating(struct device_node *np, + struct samsung_clk_provider *ctx, + const struct samsung_cmu_info *cmu); + +struct clk_hw *samsung_register_auto_gate(struct device *dev, + struct device_node *np, const char *name, + const char *parent_name, const struct clk_hw *parent_hw, + const struct clk_parent_data *parent_data, + unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + +bool samsung_is_auto_capable(struct device_node *np); + #endif /* __SAMSUNG_CLK_H */ From eaf9206bf3d68f792ecd5e6f488eab1fb5afe900 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Mon, 22 Dec 2025 10:22:15 +0000 Subject: [PATCH 05/11] clk: samsung: gs101: Enable auto_clock_gate mode for each gs101 CMU Enable auto clock mode, and define the additional fields which are used when this mode is enabled. /sys/kernel/debug/clk/clk_summary now reports approximately 308 running clocks and 298 disabled clocks. Prior to this commit 586 clocks were running and 17 disabled. Signed-off-by: Peter Griffin Link: https://patch.msgid.link/20251222-automatic-clocks-v7-4-fec86fa89874@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-gs101.c | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/clk/samsung/clk-gs101.c b/drivers/clk/samsung/clk-gs101.c index 70b26db9b95a..8551289b46eb 100644 --- a/drivers/clk/samsung/clk-gs101.c +++ b/drivers/clk/samsung/clk-gs101.c @@ -26,6 +26,10 @@ #define CLKS_NR_PERIC0 (CLK_GOUT_PERIC0_SYSREG_PERIC0_PCLK + 1) #define CLKS_NR_PERIC1 (CLK_GOUT_PERIC1_SYSREG_PERIC1_PCLK + 1) +#define GS101_GATE_DBG_OFFSET 0x4000 +#define GS101_DRCG_EN_OFFSET 0x104 +#define GS101_MEMCLK_OFFSET 0x108 + /* ---- CMU_TOP ------------------------------------------------------------- */ /* Register Offset definitions for CMU_TOP (0x1e080000) */ @@ -1433,6 +1437,9 @@ static const struct samsung_cmu_info top_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_TOP, .clk_regs = cmu_top_clk_regs, .nr_clk_regs = ARRAY_SIZE(cmu_top_clk_regs), + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = CMU_CMU_TOP_CONTROLLER_OPTION, }; static void __init gs101_cmu_top_init(struct device_node *np) @@ -1900,6 +1907,11 @@ static const struct samsung_gate_clock apm_gate_clks[] __initconst = { CLK_CON_GAT_GOUT_BLK_APM_UID_XIU_DP_APM_IPCLKPORT_ACLK, 21, CLK_IS_CRITICAL, 0), }; +static const unsigned long dcrg_memclk_sysreg[] __initconst = { + GS101_DRCG_EN_OFFSET, + GS101_MEMCLK_OFFSET, +}; + static const struct samsung_cmu_info apm_cmu_info __initconst = { .mux_clks = apm_mux_clks, .nr_mux_clks = ARRAY_SIZE(apm_mux_clks), @@ -1912,6 +1924,12 @@ static const struct samsung_cmu_info apm_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_APM, .clk_regs = apm_clk_regs, .nr_clk_regs = ARRAY_SIZE(apm_clk_regs), + .sysreg_clk_regs = dcrg_memclk_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .drcg_offset = GS101_DRCG_EN_OFFSET, + .memclk_offset = GS101_MEMCLK_OFFSET, }; /* ---- CMU_HSI0 ------------------------------------------------------------ */ @@ -2375,7 +2393,14 @@ static const struct samsung_cmu_info hsi0_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_HSI0, .clk_regs = hsi0_clk_regs, .nr_clk_regs = ARRAY_SIZE(hsi0_clk_regs), + .sysreg_clk_regs = dcrg_memclk_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = HSI0_CMU_HSI0_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, + .memclk_offset = GS101_MEMCLK_OFFSET, }; /* ---- CMU_HSI2 ------------------------------------------------------------ */ @@ -2863,7 +2888,14 @@ static const struct samsung_cmu_info hsi2_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_HSI2, .clk_regs = cmu_hsi2_clk_regs, .nr_clk_regs = ARRAY_SIZE(cmu_hsi2_clk_regs), + .sysreg_clk_regs = dcrg_memclk_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = HSI2_CMU_HSI2_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, + .memclk_offset = GS101_MEMCLK_OFFSET, }; /* ---- CMU_MISC ------------------------------------------------------------ */ @@ -3423,7 +3455,14 @@ static const struct samsung_cmu_info misc_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_MISC, .clk_regs = misc_clk_regs, .nr_clk_regs = ARRAY_SIZE(misc_clk_regs), + .sysreg_clk_regs = dcrg_memclk_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = MISC_CMU_MISC_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, + .memclk_offset = GS101_MEMCLK_OFFSET, }; static void __init gs101_cmu_misc_init(struct device_node *np) @@ -4010,6 +4049,10 @@ static const struct samsung_gate_clock peric0_gate_clks[] __initconst = { 21, 0, 0), }; +static const unsigned long dcrg_sysreg[] __initconst = { + GS101_DRCG_EN_OFFSET, +}; + static const struct samsung_cmu_info peric0_cmu_info __initconst = { .mux_clks = peric0_mux_clks, .nr_mux_clks = ARRAY_SIZE(peric0_mux_clks), @@ -4020,7 +4063,13 @@ static const struct samsung_cmu_info peric0_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_PERIC0, .clk_regs = peric0_clk_regs, .nr_clk_regs = ARRAY_SIZE(peric0_clk_regs), + .sysreg_clk_regs = dcrg_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_sysreg), .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = PERIC0_CMU_PERIC0_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, }; /* ---- CMU_PERIC1 ---------------------------------------------------------- */ @@ -4368,7 +4417,13 @@ static const struct samsung_cmu_info peric1_cmu_info __initconst = { .nr_clk_ids = CLKS_NR_PERIC1, .clk_regs = peric1_clk_regs, .nr_clk_regs = ARRAY_SIZE(peric1_clk_regs), + .sysreg_clk_regs = dcrg_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_sysreg), .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = PERIC1_CMU_PERIC1_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, }; /* ---- platform_driver ----------------------------------------------------- */ From aafaa4d875fb8ff525c514e69eb0440c69425c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Draszik?= Date: Fri, 9 Jan 2026 17:27:23 +0000 Subject: [PATCH 06/11] clk: samsung: avoid warning message on legacy Exynos (auto clock gating) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently print a warning message that the IO memory size is not compatible with automatic clock gating for many Exynos-based boards, including legacy ones, even if not requested to enable automatic clock gating in the first place. Change the test in question to avoid that warning. Fixes: 298fac4f4b96 ("clk: samsung: Implement automatic clock gating mode for CMUs") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/8b2c412d-3e1e-4be0-a9d5-ef67f6f0d409@samsung.com/ Suggested-by: Marek Szyprowski Signed-off-by: André Draszik Reviewed-by: Peter Griffin Link: https://patch.msgid.link/20260109-clk-samsung-autoclk-updates-v1-1-2394dcf242a9@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 06ea5deef4ee..417ec1786b5e 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -462,7 +462,7 @@ void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, const struct samsung_cmu_info *cmu, struct device_node *np) { - if (samsung_is_auto_capable(np) && cmu->auto_clock_gate) + if (cmu->auto_clock_gate && samsung_is_auto_capable(np)) ctx->auto_clock_gate = cmu->auto_clock_gate; ctx->gate_dbg_offset = cmu->gate_dbg_offset; From c9d24fe079027f143992a4a1c0be74c4f4455f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Draszik?= Date: Fri, 9 Jan 2026 17:27:24 +0000 Subject: [PATCH 07/11] clk: samsung: fix sysreg save/restore when PM is enabled for CMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, sysreg registers of a CMU that has PM and automatic clock gating enabled are not saved / restored during runtime PM (RPM) or s2idle. During normal suspend, they are accessed too late, after the CMU (and potentially power domain) have been shut down, causing an SError. The reason is that these registers are registered to be saved/restored via a syscore suspend handler which doesn't run during RPM or s2idle. During normal suspend, this handler runs after the CMU has been shut down. This registration happens as part of samsung_clk_extended_sleep_init() via samsung_en_dyn_root_clk_gating(). When PM is enabled for a CMU, registers must be saved/restored via exynos_arm64_cmu_suspend() / exynos_arm64_cmu_resume() respectively instead. These use their own data structures and are unrelated to anything that samsung_clk_extended_sleep_init() does. Calling it unconditionally from samsung_en_dyn_root_clk_gating() therefore isn't useful. Update the code to prepare sysreg save / restore in a similar way to how it handles other clock registers in the PM case already. exynos_arm64_cmu_suspend() / exynos_arm64_cmu_resume() already handle sysreg save/restore, just the setup was incorrect. Fixes: 298fac4f4b96 ("clk: samsung: Implement automatic clock gating mode for CMUs") Signed-off-by: André Draszik Reviewed-by: Peter Griffin Link: https://patch.msgid.link/20260109-clk-samsung-autoclk-updates-v1-2-2394dcf242a9@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynos-arm64.c | 32 +++++++++++++++++++++----- drivers/clk/samsung/clk.c | 19 ++++++++++----- drivers/clk/samsung/clk.h | 3 ++- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos-arm64.c b/drivers/clk/samsung/clk-exynos-arm64.c index 11e4d49f2390..35d4de233cc1 100644 --- a/drivers/clk/samsung/clk-exynos-arm64.c +++ b/drivers/clk/samsung/clk-exynos-arm64.c @@ -174,7 +174,7 @@ static int __init exynos_arm64_cmu_prepare_pm(struct device *dev, const struct samsung_cmu_info *cmu) { struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); - int i; + int i, ret; data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs, cmu->nr_clk_regs); @@ -182,8 +182,22 @@ static int __init exynos_arm64_cmu_prepare_pm(struct device *dev, return -ENOMEM; data->nr_clk_save = cmu->nr_clk_regs; + + if (cmu->nr_sysreg_clk_regs) { + data->clk_sysreg_save = + samsung_clk_alloc_reg_dump(cmu->sysreg_clk_regs, + cmu->nr_sysreg_clk_regs); + if (!data->clk_sysreg_save) { + ret = -ENOMEM; + goto free_clk_save; + } + + data->nr_clk_sysreg = cmu->nr_sysreg_clk_regs; + } + data->clk_suspend = cmu->suspend_regs; data->nr_clk_suspend = cmu->nr_suspend_regs; + data->nr_pclks = of_clk_get_parent_count(dev->of_node); if (!data->nr_pclks) return 0; @@ -191,23 +205,29 @@ static int __init exynos_arm64_cmu_prepare_pm(struct device *dev, data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks, GFP_KERNEL); if (!data->pclks) { - kfree(data->clk_save); - return -ENOMEM; + ret = -ENOMEM; + goto free_sysreg_save; } for (i = 0; i < data->nr_pclks; i++) { struct clk *clk = of_clk_get(dev->of_node, i); if (IS_ERR(clk)) { - kfree(data->clk_save); while (--i >= 0) clk_put(data->pclks[i]); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto free_sysreg_save; } data->pclks[i] = clk; } return 0; + +free_sysreg_save: + kfree(data->clk_sysreg_save); +free_clk_save: + kfree(data->clk_save); + return ret; } /** @@ -305,7 +325,7 @@ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, samsung_cmu_register_clocks(data->ctx, cmu, np); samsung_clk_of_add_provider(dev->of_node, data->ctx); /* sysreg DT nodes reference a clock in this CMU */ - samsung_en_dyn_root_clk_gating(np, data->ctx, cmu); + samsung_en_dyn_root_clk_gating(np, data->ctx, cmu, true); pm_runtime_put_sync(dev); return 0; diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 417ec1786b5e..9f68f079fd55 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -496,7 +496,8 @@ void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx, /* Enable Dynamic Root Clock Gating (DRCG) of bus components */ void samsung_en_dyn_root_clk_gating(struct device_node *np, struct samsung_clk_provider *ctx, - const struct samsung_cmu_info *cmu) + const struct samsung_cmu_info *cmu, + bool cmu_has_pm) { if (!ctx->auto_clock_gate) return; @@ -513,10 +514,16 @@ void samsung_en_dyn_root_clk_gating(struct device_node *np, regmap_write_bits(ctx->sysreg, ctx->memclk_offset, MEMCLK_EN, 0x0); - samsung_clk_extended_sleep_init(NULL, ctx->sysreg, - cmu->sysreg_clk_regs, - cmu->nr_sysreg_clk_regs, - NULL, 0); + if (!cmu_has_pm) + /* + * When a CMU has PM support, clocks are saved/restored + * via its PM handlers, so only register them with the + * syscore suspend / resume paths if PM is not in use. + */ + samsung_clk_extended_sleep_init(NULL, ctx->sysreg, + cmu->sysreg_clk_regs, + cmu->nr_sysreg_clk_regs, + NULL, 0); } } @@ -548,7 +555,7 @@ struct samsung_clk_provider * __init samsung_cmu_register_one( samsung_clk_of_add_provider(np, ctx); /* sysreg DT nodes reference a clock in this CMU */ - samsung_en_dyn_root_clk_gating(np, ctx, cmu); + samsung_en_dyn_root_clk_gating(np, ctx, cmu, false); return ctx; } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index a56aa3be54d8..b1192ca03db5 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -476,7 +476,8 @@ struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( void samsung_en_dyn_root_clk_gating(struct device_node *np, struct samsung_clk_provider *ctx, - const struct samsung_cmu_info *cmu); + const struct samsung_cmu_info *cmu, + bool cmu_has_pm); struct clk_hw *samsung_register_auto_gate(struct device *dev, struct device_node *np, const char *name, From 3a2741fa31385348c5b501bdcbab6f6d7b4628ac Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 13 Jan 2026 10:58:58 +0000 Subject: [PATCH 08/11] dt-bindings: clock: google,gs101-clock: fix alphanumeric ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure children of cmu_top have alphanumeric ordering. Top is special as it feeds all the other children CMUs. This ordering then matches the clk-gs101.c file. Reviewed-by: André Draszik Signed-off-by: Peter Griffin Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260113-dpu-clocks-v3-1-cb85424f2c72@linaro.org Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/clock/google,gs101-clock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml index 31e106ef913d..82639863b1a4 100644 --- a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml +++ b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml @@ -29,9 +29,9 @@ properties: enum: - google,gs101-cmu-top - google,gs101-cmu-apm - - google,gs101-cmu-misc - google,gs101-cmu-hsi0 - google,gs101-cmu-hsi2 + - google,gs101-cmu-misc - google,gs101-cmu-peric0 - google,gs101-cmu-peric1 From 52300cd894b3167d6206e266e341a089e87124b9 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 13 Jan 2026 10:58:59 +0000 Subject: [PATCH 09/11] dt-bindings: clock: google,gs101-clock: Add DPU clock management unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dt schema documentation and clock IDs for the Display Process Unit (DPU) clock management unit (CMU). This CMU feeds IPs such as image scaler, enhancer and compressor. Signed-off-by: Peter Griffin Reviewed-by: Rob Herring (Arm) Reviewed-by: André Draszik Link: https://patch.msgid.link/20260113-dpu-clocks-v3-2-cb85424f2c72@linaro.org Signed-off-by: Krzysztof Kozlowski --- .../bindings/clock/google,gs101-clock.yaml | 19 ++++++++++ include/dt-bindings/clock/google,gs101.h | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml index 82639863b1a4..6193c87511fa 100644 --- a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml +++ b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml @@ -29,6 +29,7 @@ properties: enum: - google,gs101-cmu-top - google,gs101-cmu-apm + - google,gs101-cmu-dpu - google,gs101-cmu-hsi0 - google,gs101-cmu-hsi2 - google,gs101-cmu-misc @@ -77,6 +78,24 @@ allOf: items: - const: oscclk + - if: + properties: + compatible: + contains: + const: google,gs101-cmu-dpu + + then: + properties: + clocks: + items: + - description: External reference clock (24.576 MHz) + - description: DPU bus clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: bus + - if: properties: compatible: diff --git a/include/dt-bindings/clock/google,gs101.h b/include/dt-bindings/clock/google,gs101.h index 442f9e9037dc..7a14dcb9f17b 100644 --- a/include/dt-bindings/clock/google,gs101.h +++ b/include/dt-bindings/clock/google,gs101.h @@ -313,6 +313,42 @@ #define CLK_APM_PLL_DIV4_APM 70 #define CLK_APM_PLL_DIV16_APM 71 +/* CMU_DPU */ +#define CLK_MOUT_DPU_BUS_USER 1 +#define CLK_DOUT_DPU_BUSP 2 +#define CLK_GOUT_DPU_PCLK 3 +#define CLK_GOUT_DPU_CLK_DPU_OSCCLK_CLK 4 +#define CLK_GOUT_DPU_AD_APB_DPU_DMA_PCLKM 5 +#define CLK_GOUT_DPU_DPUF_ACLK_DMA 6 +#define CLK_GOUT_DPU_DPUF_ACLK_DPP 7 +#define CLK_GOUT_DPU_D_TZPC_DPU_PCLK 8 +#define CLK_GOUT_DPU_GPC_DPU_PCLK 9 +#define CLK_GOUT_DPU_LHM_AXI_P_DPU_I_CLK 10 +#define CLK_GOUT_DPU_LHS_AXI_D0_DPU_I_CLK 11 +#define CLK_GOUT_DPU_LHS_AXI_D1_DPU_I_CLK 12 +#define CLK_GOUT_DPU_LHS_AXI_D2_DPU_I_CLK 13 +#define CLK_GOUT_DPU_PPMU_DPUD0_ACLK 14 +#define CLK_GOUT_DPU_PPMU_DPUD0_PCLK 15 +#define CLK_GOUT_DPU_PPMU_DPUD1_ACLK 16 +#define CLK_GOUT_DPU_PPMU_DPUD1_PCLK 17 +#define CLK_GOUT_DPU_PPMU_DPUD2_ACLK 18 +#define CLK_GOUT_DPU_PPMU_DPUD2_PCLK 19 +#define CLK_GOUT_DPU_CLK_DPU_BUSD_CLK 20 +#define CLK_GOUT_DPU_CLK_DPU_BUSP_CLK 21 +#define CLK_GOUT_DPU_SSMT_DPU0_ACLK 22 +#define CLK_GOUT_DPU_SSMT_DPU0_PCLK 23 +#define CLK_GOUT_DPU_SSMT_DPU1_ACLK 24 +#define CLK_GOUT_DPU_SSMT_DPU1_PCLK 25 +#define CLK_GOUT_DPU_SSMT_DPU2_ACLK 26 +#define CLK_GOUT_DPU_SSMT_DPU2_PCLK 27 +#define CLK_GOUT_DPU_SYSMMU_DPUD0_CLK_S1 28 +#define CLK_GOUT_DPU_SYSMMU_DPUD0_CLK_S2 29 +#define CLK_GOUT_DPU_SYSMMU_DPUD1_CLK_S1 30 +#define CLK_GOUT_DPU_SYSMMU_DPUD1_CLK_S2 31 +#define CLK_GOUT_DPU_SYSMMU_DPUD2_CLK_S1 32 +#define CLK_GOUT_DPU_SYSMMU_DPUD2_CLK_S2 33 +#define CLK_GOUT_DPU_SYSREG_DPU_PCLK 34 + /* CMU_HSI0 */ #define CLK_FOUT_USB_PLL 1 #define CLK_MOUT_PLL_USB 2 From 1d8fae6617b6ebcaf3ef3040ee84cdde25d5d10f Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 13 Jan 2026 10:59:00 +0000 Subject: [PATCH 10/11] dt-bindings: samsung: exynos-sysreg: add gs101 dpu compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dedicated compatibles for gs101 dpu sysreg controllers to the documentation. Reviewed-by: André Draszik Signed-off-by: Peter Griffin Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260113-dpu-clocks-v3-3-cb85424f2c72@linaro.org Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml index 5e1e155510b3..9c63dbcd4d77 100644 --- a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml @@ -15,6 +15,7 @@ properties: - items: - enum: - google,gs101-apm-sysreg + - google,gs101-dpu-sysreg - google,gs101-hsi0-sysreg - google,gs101-hsi2-sysreg - google,gs101-misc-sysreg @@ -92,6 +93,7 @@ allOf: compatible: contains: enum: + - google,gs101-dpu-sysreg - google,gs101-hsi0-sysreg - google,gs101-hsi2-sysreg - google,gs101-misc-sysreg From 4b18a3ef2230e38439c9c3a0e3fa4a2b5edcc5cd Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 13 Jan 2026 10:59:01 +0000 Subject: [PATCH 11/11] clk: samsung: gs101: add support for Display Process Unit (DPU) clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmu_dpu is the clock management unit used for the Display Process Unit block. It generates clocks for image scaler, compressor etc. Add support for the muxes, dividers and gates in cmu_dpu. Signed-off-by: Peter Griffin Reviewed-by: André Draszik Link: https://patch.msgid.link/20260113-dpu-clocks-v3-4-cb85424f2c72@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-gs101.c | 283 ++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) diff --git a/drivers/clk/samsung/clk-gs101.c b/drivers/clk/samsung/clk-gs101.c index 8551289b46eb..44a8ecd332fd 100644 --- a/drivers/clk/samsung/clk-gs101.c +++ b/drivers/clk/samsung/clk-gs101.c @@ -20,6 +20,7 @@ /* NOTE: Must be equal to the last clock ID increased by one */ #define CLKS_NR_TOP (CLK_GOUT_CMU_TPU_UART + 1) #define CLKS_NR_APM (CLK_APM_PLL_DIV16_APM + 1) +#define CLKS_NR_DPU (CLK_GOUT_DPU_SYSREG_DPU_PCLK + 1) #define CLKS_NR_HSI0 (CLK_GOUT_HSI0_XIU_P_HSI0_ACLK + 1) #define CLKS_NR_HSI2 (CLK_GOUT_HSI2_XIU_P_HSI2_ACLK + 1) #define CLKS_NR_MISC (CLK_GOUT_MISC_XIU_D_MISC_ACLK + 1) @@ -1932,6 +1933,285 @@ static const struct samsung_cmu_info apm_cmu_info __initconst = { .memclk_offset = GS101_MEMCLK_OFFSET, }; +/* ---- CMU_DPU ------------------------------------------------------------- */ + +/* Register Offset definitions for CMU_DPU (0x1c000000) */ +#define PLL_CON0_MUX_CLKCMU_DPU_BUS_USER 0x0600 +#define PLL_CON1_MUX_CLKCMU_DPU_BUS_USER 0x0604 +#define DPU_CMU_DPU_CONTROLLER_OPTION 0x0800 +#define CLKOUT_CON_BLK_DPU_CMU_DPU_CLKOUT0 0x0810 +#define CLK_CON_DIV_DIV_CLK_DPU_BUSP 0x1800 +#define CLK_CON_GAT_CLK_BLK_DPU_UID_DPU_CMU_DPU_IPCLKPORT_PCLK 0x2000 +#define CLK_CON_GAT_CLK_BLK_DPU_UID_RSTNSYNC_CLK_DPU_OSCCLK_IPCLKPORT_CLK 0x2004 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_AD_APB_DPU_DMA_IPCLKPORT_PCLKM 0x2008 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DMA 0x200c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DPP 0x2010 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_D_TZPC_DPU_IPCLKPORT_PCLK 0x2014 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_GPC_DPU_IPCLKPORT_PCLK 0x2018 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_LHM_AXI_P_DPU_IPCLKPORT_I_CLK 0x201c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D0_DPU_IPCLKPORT_I_CLK 0x2020 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D1_DPU_IPCLKPORT_I_CLK 0x2024 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D2_DPU_IPCLKPORT_I_CLK 0x2028 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_ACLK 0x202c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_PCLK 0x2030 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_ACLK 0x2034 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_PCLK 0x2038 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_ACLK 0x203c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_PCLK 0x2040 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSD_IPCLKPORT_CLK 0x2044 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSP_IPCLKPORT_CLK 0x2048 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_ACLK 0x204c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_PCLK 0x2050 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_ACLK 0x2054 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_PCLK 0x2058 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_ACLK 0x205c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_PCLK 0x2060 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S1 0x2064 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S2 0x2068 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S1 0x206c +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S2 0x2070 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S1 0x2074 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S2 0x2078 +#define CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSREG_DPU_IPCLKPORT_PCLK 0x207c +#define PCH_CON_LHM_AXI_P_DPU_PCH 0x3000 +#define PCH_CON_LHS_AXI_D0_DPU_PCH 0x3004 +#define PCH_CON_LHS_AXI_D1_DPU_PCH 0x3008 +#define PCH_CON_LHS_AXI_D2_DPU_PCH 0x300c +#define QCH_CON_DPUF_QCH_DPU_DMA 0x3010 +#define QCH_CON_DPUF_QCH_DPU_DPP 0x3014 +#define QCH_CON_DPU_CMU_DPU_QCH 0x301c +#define QCH_CON_D_TZPC_DPU_QCH 0x3020 +#define QCH_CON_GPC_DPU_QCH 0x3024 +#define QCH_CON_LHM_AXI_P_DPU_QCH 0x3028 +#define QCH_CON_LHS_AXI_D0_DPU_QCH 0x302c +#define QCH_CON_LHS_AXI_D1_DPU_QCH 0x3030 +#define QCH_CON_LHS_AXI_D2_DPU_QCH 0x3034 +#define QCH_CON_PPMU_DPUD0_QCH 0x3038 +#define QCH_CON_PPMU_DPUD1_QCH 0x303c +#define QCH_CON_PPMU_DPUD2_QCH 0x3040 +#define QCH_CON_SSMT_DPU0_QCH 0x3044 +#define QCH_CON_SSMT_DPU1_QCH 0x3048 +#define QCH_CON_SSMT_DPU2_QCH 0x304c +#define QCH_CON_SYSMMU_DPUD0_QCH_S1 0x3050 +#define QCH_CON_SYSMMU_DPUD0_QCH_S2 0x3054 +#define QCH_CON_SYSMMU_DPUD1_QCH_S1 0x3058 +#define QCH_CON_SYSMMU_DPUD1_QCH_S2 0x305c +#define QCH_CON_SYSMMU_DPUD2_QCH_S1 0x3060 +#define QCH_CON_SYSMMU_DPUD2_QCH_S2 0x3064 +#define QCH_CON_SYSREG_DPU_QCH 0x3068 +#define QUEUE_CTRL_REG_BLK_DPU_CMU_DPU 0x3c00 + +static const unsigned long dpu_clk_regs[] __initconst = { + PLL_CON0_MUX_CLKCMU_DPU_BUS_USER, + PLL_CON1_MUX_CLKCMU_DPU_BUS_USER, + DPU_CMU_DPU_CONTROLLER_OPTION, + CLKOUT_CON_BLK_DPU_CMU_DPU_CLKOUT0, + CLK_CON_DIV_DIV_CLK_DPU_BUSP, + CLK_CON_GAT_CLK_BLK_DPU_UID_DPU_CMU_DPU_IPCLKPORT_PCLK, + CLK_CON_GAT_CLK_BLK_DPU_UID_RSTNSYNC_CLK_DPU_OSCCLK_IPCLKPORT_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_AD_APB_DPU_DMA_IPCLKPORT_PCLKM, + CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DMA, + CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DPP, + CLK_CON_GAT_GOUT_BLK_DPU_UID_D_TZPC_DPU_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_GPC_DPU_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHM_AXI_P_DPU_IPCLKPORT_I_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D0_DPU_IPCLKPORT_I_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D1_DPU_IPCLKPORT_I_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D2_DPU_IPCLKPORT_I_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSD_IPCLKPORT_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSP_IPCLKPORT_CLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_ACLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_PCLK, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S1, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S2, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S1, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S2, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S1, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S2, + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSREG_DPU_IPCLKPORT_PCLK, + PCH_CON_LHM_AXI_P_DPU_PCH, + PCH_CON_LHS_AXI_D0_DPU_PCH, + PCH_CON_LHS_AXI_D1_DPU_PCH, + PCH_CON_LHS_AXI_D2_DPU_PCH, + QCH_CON_DPUF_QCH_DPU_DMA, + QCH_CON_DPUF_QCH_DPU_DPP, + QCH_CON_DPU_CMU_DPU_QCH, + QCH_CON_D_TZPC_DPU_QCH, + QCH_CON_GPC_DPU_QCH, + QCH_CON_LHM_AXI_P_DPU_QCH, + QCH_CON_LHS_AXI_D0_DPU_QCH, + QCH_CON_LHS_AXI_D1_DPU_QCH, + QCH_CON_LHS_AXI_D2_DPU_QCH, + QCH_CON_PPMU_DPUD0_QCH, + QCH_CON_PPMU_DPUD1_QCH, + QCH_CON_PPMU_DPUD2_QCH, + QCH_CON_SSMT_DPU0_QCH, + QCH_CON_SSMT_DPU1_QCH, + QCH_CON_SSMT_DPU2_QCH, + QCH_CON_SYSMMU_DPUD0_QCH_S1, + QCH_CON_SYSMMU_DPUD0_QCH_S2, + QCH_CON_SYSMMU_DPUD1_QCH_S1, + QCH_CON_SYSMMU_DPUD1_QCH_S2, + QCH_CON_SYSMMU_DPUD2_QCH_S1, + QCH_CON_SYSMMU_DPUD2_QCH_S2, + QCH_CON_SYSREG_DPU_QCH, + QUEUE_CTRL_REG_BLK_DPU_CMU_DPU, +}; + +/* List of parent clocks for Muxes in CMU_DPU */ +PNAME(mout_dpu_bus_user_p) = { "oscclk", "dout_cmu_dpu_bus" }; + +static const struct samsung_mux_clock dpu_mux_clks[] __initconst = { + MUX(CLK_MOUT_DPU_BUS_USER, "mout_dpu_bus_user", + mout_dpu_bus_user_p, PLL_CON0_MUX_CLKCMU_DPU_BUS_USER, 4, 1), +}; + +static const struct samsung_div_clock dpu_div_clks[] __initconst = { + DIV(CLK_DOUT_DPU_BUSP, "dout_dpu_busp", "mout_dpu_bus_user", + CLK_CON_DIV_DIV_CLK_DPU_BUSP, 0, 3), +}; + +static const struct samsung_gate_clock dpu_gate_clks[] __initconst = { + GATE(CLK_GOUT_DPU_PCLK, "gout_dpu_dpu_pclk", + "dout_dpu_busp", + CLK_CON_GAT_CLK_BLK_DPU_UID_DPU_CMU_DPU_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_CLK_DPU_OSCCLK_CLK, "gout_dpu_clk_dpu_oscclk_clk", + "oscclk", + CLK_CON_GAT_CLK_BLK_DPU_UID_RSTNSYNC_CLK_DPU_OSCCLK_IPCLKPORT_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_AD_APB_DPU_DMA_PCLKM, "gout_dpu_ad_apb_dpu_dma_pclkm", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_AD_APB_DPU_DMA_IPCLKPORT_PCLKM, + 21, 0, 0), + GATE(CLK_GOUT_DPU_DPUF_ACLK_DMA, "gout_dpu_dpuf_aclk_dma", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DMA, 21, 0, 0), + GATE(CLK_GOUT_DPU_DPUF_ACLK_DPP, "gout_dpu_dpuf_aclk_dpp", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_DPUF_IPCLKPORT_ACLK_DPP, 21, 0, 0), + GATE(CLK_GOUT_DPU_D_TZPC_DPU_PCLK, "gout_dpu_d_tzpc_dpu_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_D_TZPC_DPU_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_GPC_DPU_PCLK, "gout_dpu_gpc_dpu_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_GPC_DPU_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_LHM_AXI_P_DPU_I_CLK, "gout_dpu_lhm_axi_p_dpu_i_clk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHM_AXI_P_DPU_IPCLKPORT_I_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_LHS_AXI_D0_DPU_I_CLK, "gout_dpu_lhs_axi_d0_dpu_i_clk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D0_DPU_IPCLKPORT_I_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_LHS_AXI_D1_DPU_I_CLK, "gout_dpu_lhs_axi_d1_dpu_i_clk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D1_DPU_IPCLKPORT_I_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_LHS_AXI_D2_DPU_I_CLK, "gout_dpu_lhs_axi_d2_dpu_i_clk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_LHS_AXI_D2_DPU_IPCLKPORT_I_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD0_ACLK, "gout_dpu_ppmu_dpud0_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD0_PCLK, "gout_dpu_ppmu_dpud0_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD0_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD1_ACLK, "gout_dpu_ppmu_dpud1_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD1_PCLK, "gout_dpu_ppmu_dpud1_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD1_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD2_ACLK, "gout_dpu_ppmu_dpud2_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_PPMU_DPUD2_PCLK, "gout_dpu_ppmu_dpud2_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_PPMU_DPUD2_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_CLK_DPU_BUSD_CLK, "gout_dpu_clk_dpu_busd_clk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSD_IPCLKPORT_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_CLK_DPU_BUSP_CLK, "gout_dpu_clk_dpu_busp_clk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_RSTNSYNC_CLK_DPU_BUSP_IPCLKPORT_CLK, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU0_ACLK, "gout_dpu_ssmt_dpu0_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU0_PCLK, "gout_dpu_ssmt_dpu0_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU0_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU1_ACLK, "gout_dpu_ssmt_dpu1_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU1_PCLK, "gout_dpu_ssmt_dpu1_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU1_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU2_ACLK, "gout_dpu_ssmt_dpu2_aclk", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_ACLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SSMT_DPU2_PCLK, "gout_dpu_ssmt_dpu2_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SSMT_DPU2_IPCLKPORT_PCLK, 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD0_CLK_S1, "gout_dpu_sysmmu_dpud0_clk_s1", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S1, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD0_CLK_S2, "gout_dpu_sysmmu_dpud0_clk_s2", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD0_IPCLKPORT_CLK_S2, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD1_CLK_S1, "gout_dpu_sysmmu_dpud1_clk_s1", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S1, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD1_CLK_S2, "gout_dpu_sysmmu_dpud1_clk_s2", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD1_IPCLKPORT_CLK_S2, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD2_CLK_S1, "gout_dpu_sysmmu_dpud2_clk_s1", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S1, + 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSMMU_DPUD2_CLK_S2, "gout_dpu_sysmmu_dpud2_clk_s2", + "mout_dpu_bus_user", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSMMU_DPUD2_IPCLKPORT_CLK_S2, 21, 0, 0), + GATE(CLK_GOUT_DPU_SYSREG_DPU_PCLK, "gout_dpu_sysreg_dpu_pclk", + "dout_dpu_busp", + CLK_CON_GAT_GOUT_BLK_DPU_UID_SYSREG_DPU_IPCLKPORT_PCLK, 21, 0, 0), +}; + +static const struct samsung_cmu_info dpu_cmu_info __initconst = { + .mux_clks = dpu_mux_clks, + .nr_mux_clks = ARRAY_SIZE(dpu_mux_clks), + .div_clks = dpu_div_clks, + .nr_div_clks = ARRAY_SIZE(dpu_div_clks), + .gate_clks = dpu_gate_clks, + .nr_gate_clks = ARRAY_SIZE(dpu_gate_clks), + .nr_clk_ids = CLKS_NR_DPU, + .clk_regs = dpu_clk_regs, + .nr_clk_regs = ARRAY_SIZE(dpu_clk_regs), + .sysreg_clk_regs = dcrg_memclk_sysreg, + .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), + .clk_name = "bus", + .auto_clock_gate = true, + .gate_dbg_offset = GS101_GATE_DBG_OFFSET, + .option_offset = DPU_CMU_DPU_CONTROLLER_OPTION, + .drcg_offset = GS101_DRCG_EN_OFFSET, +}; + /* ---- CMU_HSI0 ------------------------------------------------------------ */ /* Register Offset definitions for CMU_HSI0 (0x11000000) */ @@ -4443,6 +4723,9 @@ static const struct of_device_id gs101_cmu_of_match[] = { { .compatible = "google,gs101-cmu-apm", .data = &apm_cmu_info, + }, { + .compatible = "google,gs101-cmu-dpu", + .data = &dpu_cmu_info, }, { .compatible = "google,gs101-cmu-hsi0", .data = &hsi0_cmu_info,