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
8 #include <linux/of_platform.h>
9 #include <linux/platform_device.h>
10 #include <linux/pm_domain.h>
11 #include <linux/pm_runtime.h>
14 #include "clk-exynos5-subcmu.h"
16 static struct samsung_clk_provider
*ctx
;
17 static const struct exynos5_subcmu_info
**cmu
;
20 static void exynos5_subcmu_clk_save(void __iomem
*base
,
21 struct exynos5_subcmu_reg_dump
*rd
,
22 unsigned int num_regs
)
24 for (; num_regs
> 0; --num_regs
, ++rd
) {
25 rd
->save
= readl(base
+ rd
->offset
);
26 writel((rd
->save
& ~rd
->mask
) | rd
->value
, base
+ rd
->offset
);
31 static void exynos5_subcmu_clk_restore(void __iomem
*base
,
32 struct exynos5_subcmu_reg_dump
*rd
,
33 unsigned int num_regs
)
35 for (; num_regs
> 0; --num_regs
, ++rd
)
36 writel((readl(base
+ rd
->offset
) & ~rd
->mask
) | rd
->save
,
40 static void exynos5_subcmu_defer_gate(struct samsung_clk_provider
*ctx
,
41 const struct samsung_gate_clock
*list
, int nr_clk
)
44 samsung_clk_add_lookup(ctx
, ERR_PTR(-EPROBE_DEFER
), list
++->id
);
48 * Pass the needed clock provider context and register sub-CMU clocks
50 * NOTE: This function has to be called from the main, OF_CLK_DECLARE-
51 * initialized clock provider driver. This happens very early during boot
52 * process. Then this driver, during core_initcall registers two platform
53 * drivers: one which binds to the same device-tree node as OF_CLK_DECLARE
54 * driver and second, for handling its per-domain child-devices. Those
55 * platform drivers are bound to their devices a bit later in arch_initcall,
56 * when OF-core populates all device-tree nodes.
58 void exynos5_subcmus_init(struct samsung_clk_provider
*_ctx
, int _nr_cmus
,
59 const struct exynos5_subcmu_info
**_cmu
)
65 for (; _nr_cmus
--; _cmu
++) {
66 exynos5_subcmu_defer_gate(ctx
, (*_cmu
)->gate_clks
,
67 (*_cmu
)->nr_gate_clks
);
68 exynos5_subcmu_clk_save(ctx
->reg_base
, (*_cmu
)->suspend_regs
,
69 (*_cmu
)->nr_suspend_regs
);
73 static int __maybe_unused
exynos5_subcmu_suspend(struct device
*dev
)
75 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
78 spin_lock_irqsave(&ctx
->lock
, flags
);
79 exynos5_subcmu_clk_save(ctx
->reg_base
, info
->suspend_regs
,
80 info
->nr_suspend_regs
);
81 spin_unlock_irqrestore(&ctx
->lock
, flags
);
86 static int __maybe_unused
exynos5_subcmu_resume(struct device
*dev
)
88 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
91 spin_lock_irqsave(&ctx
->lock
, flags
);
92 exynos5_subcmu_clk_restore(ctx
->reg_base
, info
->suspend_regs
,
93 info
->nr_suspend_regs
);
94 spin_unlock_irqrestore(&ctx
->lock
, flags
);
99 static int __init
exynos5_subcmu_probe(struct platform_device
*pdev
)
101 struct device
*dev
= &pdev
->dev
;
102 struct exynos5_subcmu_info
*info
= dev_get_drvdata(dev
);
104 pm_runtime_set_suspended(dev
);
105 pm_runtime_enable(dev
);
109 samsung_clk_register_div(ctx
, info
->div_clks
, info
->nr_div_clks
);
110 samsung_clk_register_gate(ctx
, info
->gate_clks
, info
->nr_gate_clks
);
113 pm_runtime_put_sync(dev
);
118 static const struct dev_pm_ops exynos5_subcmu_pm_ops
= {
119 SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend
,
120 exynos5_subcmu_resume
, NULL
)
121 SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
122 pm_runtime_force_resume
)
125 static struct platform_driver exynos5_subcmu_driver __refdata
= {
127 .name
= "exynos5-subcmu",
128 .suppress_bind_attrs
= true,
129 .pm
= &exynos5_subcmu_pm_ops
,
131 .probe
= exynos5_subcmu_probe
,
134 static int __init
exynos5_clk_register_subcmu(struct device
*parent
,
135 const struct exynos5_subcmu_info
*info
,
136 struct device_node
*pd_node
)
138 struct of_phandle_args genpdspec
= { .np
= pd_node
};
139 struct platform_device
*pdev
;
142 pdev
= platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO
);
146 pdev
->dev
.parent
= parent
;
147 platform_set_drvdata(pdev
, (void *)info
);
148 of_genpd_add_device(&genpdspec
, &pdev
->dev
);
149 ret
= platform_device_add(pdev
);
151 platform_device_put(pdev
);
156 static int __init
exynos5_clk_probe(struct platform_device
*pdev
)
158 struct device_node
*np
;
162 for_each_compatible_node(np
, NULL
, "samsung,exynos4210-pd") {
163 if (of_property_read_string(np
, "label", &name
) < 0)
165 for (i
= 0; i
< nr_cmus
; i
++)
166 if (strcmp(cmu
[i
]->pd_name
, name
) == 0)
167 exynos5_clk_register_subcmu(&pdev
->dev
,
173 static const struct of_device_id exynos5_clk_of_match
[] = {
174 { .compatible
= "samsung,exynos5250-clock", },
175 { .compatible
= "samsung,exynos5420-clock", },
176 { .compatible
= "samsung,exynos5800-clock", },
180 static struct platform_driver exynos5_clk_driver __refdata
= {
182 .name
= "exynos5-clock",
183 .of_match_table
= exynos5_clk_of_match
,
184 .suppress_bind_attrs
= true,
186 .probe
= exynos5_clk_probe
,
189 static int __init
exynos5_clk_drv_init(void)
191 platform_driver_register(&exynos5_clk_driver
);
192 platform_driver_register(&exynos5_subcmu_driver
);
195 core_initcall(exynos5_clk_drv_init
);