Commit 298fac4f authored by Peter Griffin's avatar Peter Griffin Committed by Krzysztof Kozlowski
Browse files

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: default avatarPeter Griffin <peter.griffin@linaro.org>
Link: https://patch.msgid.link/20251222-automatic-clocks-v7-3-fec86fa89874@linaro.org


Signed-off-by: default avatarKrzysztof Kozlowski <krzk@kernel.org>
parent 2e8e9a24
Loading
Loading
Loading
Loading
+53 −9
Original line number Diff line number Diff line
@@ -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;
@@ -211,7 +245,7 @@ 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
 * @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]);

+6 −6
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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),
Loading