1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 // Author: Marek Szyprowski <m.szyprowski@samsung.com>
5 // Common Clock Framework support for Exynos5 power-domain dependent clocks
7 #include <linux/of_platform.h>
8 #include <linux/platform_device.h>
9 #include <linux/pm_domain.h>
10 #include <linux/pm_runtime.h>
13 #include "clk-exynos5-subcmu.h"
15 static struct samsung_clk_provider
*ctx
;
16 static const struct exynos5_subcmu_info
*cmu
;
19 static void exynos5_subcmu_clk_save(void __iomem
*base
,
20 struct exynos5_subcmu_reg_dump
*rd
,
21 unsigned int num_regs
)
23 for (; num_regs
> 0; --num_regs
, ++rd
) {
24 rd
->save
= readl(base
+ rd
->offset
);
25 writel((rd
->save
& ~rd
->mask
) | rd
->value
, base
+ rd
->offset
);
30 static void exynos5_subcmu_clk_restore(void __iomem
*base
,
31 struct exynos5_subcmu_reg_dump
*rd
,
32 unsigned int num_regs
)
34 for (; num_regs
> 0; --num_regs
, ++rd
)
35 writel((readl(base
+ rd
->offset
) & ~rd
->mask
) | rd
->save
,
39 static void exynos5_subcmu_defer_gate(struct samsung_clk_provider
*ctx
,
40 const struct samsung_gate_clock
*list
, int nr_clk
)
43 samsung_clk_add_lookup(ctx
, ERR_PTR(-EPROBE_DEFER
), list
++->id
);
47 * Pass the needed clock provider context and register sub-CMU clocks
49 * NOTE: This function has to be called from the main, OF_CLK_DECLARE-
50 * initialized clock provider driver. This happens very early during boot
51 * process. Then this driver, during core_initcall registers two platform
52 * drivers: one which binds to the same device-tree node as OF_CLK_DECLARE
53 * driver and second, for handling its per-domain child-devices. Those
54 * platform drivers are bound to their devices a bit later in arch_initcall,
55 * when OF-core populates all device-tree nodes.
57 void exynos5_subcmus_init(struct samsung_clk_provider
*_ctx
, int _nr_cmus
,
58 const struct exynos5_subcmu_info
*_cmu
)
64 for (; _nr_cmus
--; _cmu
++) {
65 exynos5_subcmu_defer_gate(ctx
, _cmu
->gate_clks
,
67 exynos5_subcmu_clk_save(ctx
->reg_base
, _cmu
->suspend_regs
,
68 _cmu
->nr_suspend_regs
);
72 static int __maybe_unused
exynos5_subcmu_suspend(struct device
*dev
)
74 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
77 spin_lock_irqsave(&ctx
->lock
, flags
);
78 exynos5_subcmu_clk_save(ctx
->reg_base
, info
->suspend_regs
,
79 info
->nr_suspend_regs
);
80 spin_unlock_irqrestore(&ctx
->lock
, flags
);
85 static int __maybe_unused
exynos5_subcmu_resume(struct device
*dev
)
87 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
90 spin_lock_irqsave(&ctx
->lock
, flags
);
91 exynos5_subcmu_clk_restore(ctx
->reg_base
, info
->suspend_regs
,
92 info
->nr_suspend_regs
);
93 spin_unlock_irqrestore(&ctx
->lock
, flags
);
98 static int __init
exynos5_subcmu_probe(struct platform_device
*pdev
)
100 struct device
*dev
= &pdev
->dev
;
101 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
103 pm_runtime_set_suspended(dev
);
104 pm_runtime_enable(dev
);
108 samsung_clk_register_div(ctx
, info
->div_clks
, info
->nr_div_clks
);
109 samsung_clk_register_gate(ctx
, info
->gate_clks
, info
->nr_gate_clks
);
112 pm_runtime_put_sync(dev
);
117 static const struct dev_pm_ops exynos5_subcmu_pm_ops
= {
118 SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend
,
119 exynos5_subcmu_resume
, NULL
)
120 SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
121 pm_runtime_force_resume
)
124 static struct platform_driver exynos5_subcmu_driver __refdata
= {
126 .name
= "exynos5-subcmu",
127 .suppress_bind_attrs
= true,
128 .pm
= &exynos5_subcmu_pm_ops
,
130 .probe
= exynos5_subcmu_probe
,
133 static int __init
exynos5_clk_register_subcmu(struct device
*parent
,
134 const struct exynos5_subcmu_info
*info
,
135 struct device_node
*pd_node
)
137 struct of_phandle_args genpdspec
= { .np
= pd_node
};
138 struct platform_device
*pdev
;
140 pdev
= platform_device_alloc(info
->pd_name
, -1);
141 pdev
->dev
.parent
= parent
;
142 pdev
->driver_override
= "exynos5-subcmu";
143 platform_set_drvdata(pdev
, (void *)info
);
144 of_genpd_add_device(&genpdspec
, &pdev
->dev
);
145 platform_device_add(pdev
);
150 static int __init
exynos5_clk_probe(struct platform_device
*pdev
)
152 struct device_node
*np
;
156 for_each_compatible_node(np
, NULL
, "samsung,exynos4210-pd") {
157 if (of_property_read_string(np
, "label", &name
) < 0)
159 for (i
= 0; i
< nr_cmus
; i
++)
160 if (strcmp(cmu
[i
].pd_name
, name
) == 0)
161 exynos5_clk_register_subcmu(&pdev
->dev
,
167 static const struct of_device_id exynos5_clk_of_match
[] = {
168 { .compatible
= "samsung,exynos5250-clock", },
169 { .compatible
= "samsung,exynos5420-clock", },
170 { .compatible
= "samsung,exynos5800-clock", },
174 static struct platform_driver exynos5_clk_driver __refdata
= {
176 .name
= "exynos5-clock",
177 .of_match_table
= exynos5_clk_of_match
,
178 .suppress_bind_attrs
= true,
180 .probe
= exynos5_clk_probe
,
183 static int __init
exynos5_clk_drv_init(void)
185 platform_driver_register(&exynos5_clk_driver
);
186 platform_driver_register(&exynos5_subcmu_driver
);
189 core_initcall(exynos5_clk_drv_init
);