Skip to content

Commit 298fac4

Browse files
petegriffinkrzk
authored andcommitted
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 <peter.griffin@linaro.org> Link: https://patch.msgid.link/20251222-automatic-clocks-v7-3-fec86fa89874@linaro.org Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
1 parent 2e8e9a2 commit 298fac4

9 files changed

Lines changed: 302 additions & 43 deletions

File tree

drivers/clk/samsung/clk-exynos-arm64.c

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
#define GATE_MANUAL BIT(20)
2525
#define GATE_ENABLE_HWACG BIT(28)
2626

27+
/* Option register bits */
28+
#define OPT_EN_MEM_PWR_GATING BIT(24)
29+
#define OPT_EN_AUTO_GATING BIT(28)
30+
#define OPT_EN_PWR_MANAGEMENT BIT(29)
31+
#define OPT_EN_LAYER2_CTRL BIT(30)
32+
#define OPT_EN_DBG BIT(31)
33+
34+
#define CMU_OPT_GLOBAL_EN_AUTO_GATING (OPT_EN_DBG | OPT_EN_LAYER2_CTRL | \
35+
OPT_EN_PWR_MANAGEMENT | OPT_EN_AUTO_GATING | OPT_EN_MEM_PWR_GATING)
36+
2737
/* PLL_CONx_PLL register offsets range */
2838
#define PLL_CON_OFF_START 0x100
2939
#define PLL_CON_OFF_END 0x600
@@ -37,6 +47,8 @@ struct exynos_arm64_cmu_data {
3747
unsigned int nr_clk_save;
3848
const struct samsung_clk_reg_dump *clk_suspend;
3949
unsigned int nr_clk_suspend;
50+
struct samsung_clk_reg_dump *clk_sysreg_save;
51+
unsigned int nr_clk_sysreg;
4052

4153
struct clk *clk;
4254
struct clk **pclks;
@@ -76,19 +88,41 @@ static void __init exynos_arm64_init_clocks(struct device_node *np,
7688
const unsigned long *reg_offs = cmu->clk_regs;
7789
size_t reg_offs_len = cmu->nr_clk_regs;
7890
void __iomem *reg_base;
91+
bool init_auto;
7992
size_t i;
8093

8194
reg_base = of_iomap(np, 0);
8295
if (!reg_base)
8396
panic("%s: failed to map registers\n", __func__);
8497

98+
/* ensure compatibility with older DTs */
99+
if (cmu->auto_clock_gate && samsung_is_auto_capable(np))
100+
init_auto = true;
101+
else
102+
init_auto = false;
103+
104+
if (cmu->option_offset && init_auto) {
105+
/*
106+
* Enable the global automatic mode for the entire CMU.
107+
* This overrides the individual HWACG bits in each of the
108+
* individual gate, mux and qch registers.
109+
*/
110+
writel(CMU_OPT_GLOBAL_EN_AUTO_GATING,
111+
reg_base + cmu->option_offset);
112+
}
113+
85114
for (i = 0; i < reg_offs_len; ++i) {
86115
void __iomem *reg = reg_base + reg_offs[i];
87116
u32 val;
88117

89118
if (cmu->manual_plls && is_pll_con1_reg(reg_offs[i])) {
90119
writel(PLL_CON1_MANUAL, reg);
91-
} else if (is_gate_reg(reg_offs[i])) {
120+
} else if (is_gate_reg(reg_offs[i]) && !init_auto) {
121+
/*
122+
* Setting GATE_MANUAL bit (which is described in TRM as
123+
* reserved!) overrides the global CMU automatic mode
124+
* option.
125+
*/
92126
val = readl(reg);
93127
val |= GATE_MANUAL;
94128
val &= ~GATE_ENABLE_HWACG;
@@ -210,16 +244,16 @@ void __init exynos_arm64_register_cmu(struct device *dev,
210244
/**
211245
* exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support
212246
*
213-
* @pdev: Platform device object
214-
* @set_manual: If true, set gate clocks to manual mode
247+
* @pdev: Platform device object
248+
* @init_clk_regs: If true, initialize CMU registers
215249
*
216250
* It's a version of exynos_arm64_register_cmu() with PM support. Should be
217251
* called from probe function of platform driver.
218252
*
219253
* Return: 0 on success, or negative error code on error.
220254
*/
221255
int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
222-
bool set_manual)
256+
bool init_clk_regs)
223257
{
224258
const struct samsung_cmu_info *cmu;
225259
struct device *dev = &pdev->dev;
@@ -249,7 +283,7 @@ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
249283
dev_err(dev, "%s: could not enable bus clock %s; err = %d\n",
250284
__func__, cmu->clk_name, ret);
251285

252-
if (set_manual)
286+
if (init_clk_regs)
253287
exynos_arm64_init_clocks(np, cmu);
254288

255289
reg_base = devm_platform_ioremap_resource(pdev, 0);
@@ -268,8 +302,10 @@ int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
268302
pm_runtime_set_active(dev);
269303
pm_runtime_enable(dev);
270304

271-
samsung_cmu_register_clocks(data->ctx, cmu);
305+
samsung_cmu_register_clocks(data->ctx, cmu, np);
272306
samsung_clk_of_add_provider(dev->of_node, data->ctx);
307+
/* sysreg DT nodes reference a clock in this CMU */
308+
samsung_en_dyn_root_clk_gating(np, data->ctx, cmu);
273309
pm_runtime_put_sync(dev);
274310

275311
return 0;
@@ -280,14 +316,17 @@ int exynos_arm64_cmu_suspend(struct device *dev)
280316
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
281317
int i;
282318

283-
samsung_clk_save(data->ctx->reg_base, data->clk_save,
319+
samsung_clk_save(data->ctx->reg_base, NULL, data->clk_save,
284320
data->nr_clk_save);
285321

322+
samsung_clk_save(NULL, data->ctx->sysreg, data->clk_sysreg_save,
323+
data->nr_clk_sysreg);
324+
286325
for (i = 0; i < data->nr_pclks; i++)
287326
clk_prepare_enable(data->pclks[i]);
288327

289328
/* For suspend some registers have to be set to certain values */
290-
samsung_clk_restore(data->ctx->reg_base, data->clk_suspend,
329+
samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_suspend,
291330
data->nr_clk_suspend);
292331

293332
for (i = 0; i < data->nr_pclks; i++)
@@ -308,9 +347,14 @@ int exynos_arm64_cmu_resume(struct device *dev)
308347
for (i = 0; i < data->nr_pclks; i++)
309348
clk_prepare_enable(data->pclks[i]);
310349

311-
samsung_clk_restore(data->ctx->reg_base, data->clk_save,
350+
samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_save,
312351
data->nr_clk_save);
313352

353+
if (data->ctx->sysreg)
354+
samsung_clk_restore(NULL, data->ctx->sysreg,
355+
data->clk_sysreg_save,
356+
data->nr_clk_sysreg);
357+
314358
for (i = 0; i < data->nr_pclks; i++)
315359
clk_disable_unprepare(data->pclks[i]);
316360

drivers/clk/samsung/clk-exynos4.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,12 +1361,12 @@ static void __init exynos4_clk_init(struct device_node *np,
13611361
ARRAY_SIZE(exynos4x12_plls));
13621362
}
13631363

1364-
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4);
1364+
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4, np);
13651365

13661366
if (exynos4_soc == EXYNOS4210) {
1367-
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4210);
1367+
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4210, np);
13681368
} else {
1369-
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4x12);
1369+
samsung_cmu_register_clocks(ctx, &cmu_info_exynos4x12, np);
13701370
if (soc == EXYNOS4412)
13711371
samsung_clk_register_cpu(ctx, exynos4412_cpu_clks,
13721372
ARRAY_SIZE(exynos4412_cpu_clks));
@@ -1378,15 +1378,15 @@ static void __init exynos4_clk_init(struct device_node *np,
13781378
if (soc == EXYNOS4212 || soc == EXYNOS4412)
13791379
exynos4x12_core_down_clock();
13801380

1381-
samsung_clk_extended_sleep_init(reg_base,
1381+
samsung_clk_extended_sleep_init(reg_base, NULL,
13821382
exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
13831383
src_mask_suspend, ARRAY_SIZE(src_mask_suspend));
13841384
if (exynos4_soc == EXYNOS4210)
1385-
samsung_clk_extended_sleep_init(reg_base,
1385+
samsung_clk_extended_sleep_init(reg_base, NULL,
13861386
exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save),
13871387
src_mask_suspend_e4210, ARRAY_SIZE(src_mask_suspend_e4210));
13881388
else
1389-
samsung_clk_sleep_init(reg_base, exynos4x12_clk_save,
1389+
samsung_clk_sleep_init(reg_base, NULL, exynos4x12_clk_save,
13901390
ARRAY_SIZE(exynos4x12_clk_save));
13911391

13921392
samsung_clk_of_add_provider(np, ctx);

drivers/clk/samsung/clk-exynos4412-isp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ static int __maybe_unused exynos4x12_isp_clk_suspend(struct device *dev)
9494
{
9595
struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
9696

97-
samsung_clk_save(ctx->reg_base, exynos4x12_save_isp,
97+
samsung_clk_save(ctx->reg_base, NULL, exynos4x12_save_isp,
9898
ARRAY_SIZE(exynos4x12_clk_isp_save));
9999
return 0;
100100
}
@@ -103,7 +103,7 @@ static int __maybe_unused exynos4x12_isp_clk_resume(struct device *dev)
103103
{
104104
struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
105105

106-
samsung_clk_restore(ctx->reg_base, exynos4x12_save_isp,
106+
samsung_clk_restore(ctx->reg_base, NULL, exynos4x12_save_isp,
107107
ARRAY_SIZE(exynos4x12_clk_isp_save));
108108
return 0;
109109
}

drivers/clk/samsung/clk-exynos5250.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
854854
PWR_CTRL2_CORE2_UP_RATIO | PWR_CTRL2_CORE1_UP_RATIO);
855855
__raw_writel(tmp, reg_base + PWR_CTRL2);
856856

857-
samsung_clk_sleep_init(reg_base, exynos5250_clk_regs,
857+
samsung_clk_sleep_init(reg_base, NULL, exynos5250_clk_regs,
858858
ARRAY_SIZE(exynos5250_clk_regs));
859859
exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5250_subcmus),
860860
exynos5250_subcmus);

drivers/clk/samsung/clk-exynos5420.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,12 +1649,12 @@ static void __init exynos5x_clk_init(struct device_node *np,
16491649
ARRAY_SIZE(exynos5800_cpu_clks));
16501650
}
16511651

1652-
samsung_clk_extended_sleep_init(reg_base,
1652+
samsung_clk_extended_sleep_init(reg_base, NULL,
16531653
exynos5x_clk_regs, ARRAY_SIZE(exynos5x_clk_regs),
16541654
exynos5420_set_clksrc, ARRAY_SIZE(exynos5420_set_clksrc));
16551655

16561656
if (soc == EXYNOS5800) {
1657-
samsung_clk_sleep_init(reg_base, exynos5800_clk_regs,
1657+
samsung_clk_sleep_init(reg_base, NULL, exynos5800_clk_regs,
16581658
ARRAY_SIZE(exynos5800_clk_regs));
16591659

16601660
exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5800_subcmus),

drivers/clk/samsung/clk-s3c64xx.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,10 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
449449
samsung_clk_register_alias(ctx, s3c64xx_clock_aliases,
450450
ARRAY_SIZE(s3c64xx_clock_aliases));
451451

452-
samsung_clk_sleep_init(reg_base, s3c64xx_clk_regs,
452+
samsung_clk_sleep_init(reg_base, NULL, s3c64xx_clk_regs,
453453
ARRAY_SIZE(s3c64xx_clk_regs));
454454
if (!is_s3c6400)
455-
samsung_clk_sleep_init(reg_base, s3c6410_clk_regs,
455+
samsung_clk_sleep_init(reg_base, NULL, s3c6410_clk_regs,
456456
ARRAY_SIZE(s3c6410_clk_regs));
457457

458458
samsung_clk_of_add_provider(np, ctx);

drivers/clk/samsung/clk-s5pv210.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ static void __init __s5pv210_clk_init(struct device_node *np,
782782
samsung_clk_register_alias(ctx, s5pv210_aliases,
783783
ARRAY_SIZE(s5pv210_aliases));
784784

785-
samsung_clk_sleep_init(reg_base, s5pv210_clk_regs,
785+
samsung_clk_sleep_init(reg_base, NULL, s5pv210_clk_regs,
786786
ARRAY_SIZE(s5pv210_clk_regs));
787787

788788
samsung_clk_of_add_provider(np, ctx);

0 commit comments

Comments
 (0)