1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // Copyright (c) 2018 Samsung Electronics Co., Ltd.
4*4882a593Smuzhiyun // Author: Marek Szyprowski <m.szyprowski@samsung.com>
5*4882a593Smuzhiyun // Common Clock Framework support for Exynos5 power-domain dependent clocks
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/io.h>
8*4882a593Smuzhiyun #include <linux/of_platform.h>
9*4882a593Smuzhiyun #include <linux/platform_device.h>
10*4882a593Smuzhiyun #include <linux/pm_domain.h>
11*4882a593Smuzhiyun #include <linux/pm_runtime.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include "clk.h"
14*4882a593Smuzhiyun #include "clk-exynos5-subcmu.h"
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun static struct samsung_clk_provider *ctx;
17*4882a593Smuzhiyun static const struct exynos5_subcmu_info **cmu;
18*4882a593Smuzhiyun static int nr_cmus;
19*4882a593Smuzhiyun
exynos5_subcmu_clk_save(void __iomem * base,struct exynos5_subcmu_reg_dump * rd,unsigned int num_regs)20*4882a593Smuzhiyun static void exynos5_subcmu_clk_save(void __iomem *base,
21*4882a593Smuzhiyun struct exynos5_subcmu_reg_dump *rd,
22*4882a593Smuzhiyun unsigned int num_regs)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun for (; num_regs > 0; --num_regs, ++rd) {
25*4882a593Smuzhiyun rd->save = readl(base + rd->offset);
26*4882a593Smuzhiyun writel((rd->save & ~rd->mask) | rd->value, base + rd->offset);
27*4882a593Smuzhiyun rd->save &= rd->mask;
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun };
30*4882a593Smuzhiyun
exynos5_subcmu_clk_restore(void __iomem * base,struct exynos5_subcmu_reg_dump * rd,unsigned int num_regs)31*4882a593Smuzhiyun static void exynos5_subcmu_clk_restore(void __iomem *base,
32*4882a593Smuzhiyun struct exynos5_subcmu_reg_dump *rd,
33*4882a593Smuzhiyun unsigned int num_regs)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun for (; num_regs > 0; --num_regs, ++rd)
36*4882a593Smuzhiyun writel((readl(base + rd->offset) & ~rd->mask) | rd->save,
37*4882a593Smuzhiyun base + rd->offset);
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun
exynos5_subcmu_defer_gate(struct samsung_clk_provider * ctx,const struct samsung_gate_clock * list,int nr_clk)40*4882a593Smuzhiyun static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx,
41*4882a593Smuzhiyun const struct samsung_gate_clock *list, int nr_clk)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun while (nr_clk--)
44*4882a593Smuzhiyun samsung_clk_add_lookup(ctx, ERR_PTR(-EPROBE_DEFER), list++->id);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /*
48*4882a593Smuzhiyun * Pass the needed clock provider context and register sub-CMU clocks
49*4882a593Smuzhiyun *
50*4882a593Smuzhiyun * NOTE: This function has to be called from the main, OF_CLK_DECLARE-
51*4882a593Smuzhiyun * initialized clock provider driver. This happens very early during boot
52*4882a593Smuzhiyun * process. Then this driver, during core_initcall registers two platform
53*4882a593Smuzhiyun * drivers: one which binds to the same device-tree node as OF_CLK_DECLARE
54*4882a593Smuzhiyun * driver and second, for handling its per-domain child-devices. Those
55*4882a593Smuzhiyun * platform drivers are bound to their devices a bit later in arch_initcall,
56*4882a593Smuzhiyun * when OF-core populates all device-tree nodes.
57*4882a593Smuzhiyun */
exynos5_subcmus_init(struct samsung_clk_provider * _ctx,int _nr_cmus,const struct exynos5_subcmu_info ** _cmu)58*4882a593Smuzhiyun void exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus,
59*4882a593Smuzhiyun const struct exynos5_subcmu_info **_cmu)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun ctx = _ctx;
62*4882a593Smuzhiyun cmu = _cmu;
63*4882a593Smuzhiyun nr_cmus = _nr_cmus;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun for (; _nr_cmus--; _cmu++) {
66*4882a593Smuzhiyun exynos5_subcmu_defer_gate(ctx, (*_cmu)->gate_clks,
67*4882a593Smuzhiyun (*_cmu)->nr_gate_clks);
68*4882a593Smuzhiyun exynos5_subcmu_clk_save(ctx->reg_base, (*_cmu)->suspend_regs,
69*4882a593Smuzhiyun (*_cmu)->nr_suspend_regs);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
exynos5_subcmu_suspend(struct device * dev)73*4882a593Smuzhiyun static int __maybe_unused exynos5_subcmu_suspend(struct device *dev)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
76*4882a593Smuzhiyun unsigned long flags;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun spin_lock_irqsave(&ctx->lock, flags);
79*4882a593Smuzhiyun exynos5_subcmu_clk_save(ctx->reg_base, info->suspend_regs,
80*4882a593Smuzhiyun info->nr_suspend_regs);
81*4882a593Smuzhiyun spin_unlock_irqrestore(&ctx->lock, flags);
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun return 0;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
exynos5_subcmu_resume(struct device * dev)86*4882a593Smuzhiyun static int __maybe_unused exynos5_subcmu_resume(struct device *dev)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
89*4882a593Smuzhiyun unsigned long flags;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun spin_lock_irqsave(&ctx->lock, flags);
92*4882a593Smuzhiyun exynos5_subcmu_clk_restore(ctx->reg_base, info->suspend_regs,
93*4882a593Smuzhiyun info->nr_suspend_regs);
94*4882a593Smuzhiyun spin_unlock_irqrestore(&ctx->lock, flags);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
exynos5_subcmu_probe(struct platform_device * pdev)99*4882a593Smuzhiyun static int __init exynos5_subcmu_probe(struct platform_device *pdev)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun struct device *dev = &pdev->dev;
102*4882a593Smuzhiyun struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun pm_runtime_set_suspended(dev);
105*4882a593Smuzhiyun pm_runtime_enable(dev);
106*4882a593Smuzhiyun pm_runtime_get(dev);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun ctx->dev = dev;
109*4882a593Smuzhiyun samsung_clk_register_div(ctx, info->div_clks, info->nr_div_clks);
110*4882a593Smuzhiyun samsung_clk_register_gate(ctx, info->gate_clks, info->nr_gate_clks);
111*4882a593Smuzhiyun ctx->dev = NULL;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun pm_runtime_put_sync(dev);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun static const struct dev_pm_ops exynos5_subcmu_pm_ops = {
119*4882a593Smuzhiyun SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend,
120*4882a593Smuzhiyun exynos5_subcmu_resume, NULL)
121*4882a593Smuzhiyun SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
122*4882a593Smuzhiyun pm_runtime_force_resume)
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun static struct platform_driver exynos5_subcmu_driver __refdata = {
126*4882a593Smuzhiyun .driver = {
127*4882a593Smuzhiyun .name = "exynos5-subcmu",
128*4882a593Smuzhiyun .suppress_bind_attrs = true,
129*4882a593Smuzhiyun .pm = &exynos5_subcmu_pm_ops,
130*4882a593Smuzhiyun },
131*4882a593Smuzhiyun .probe = exynos5_subcmu_probe,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun
exynos5_clk_register_subcmu(struct device * parent,const struct exynos5_subcmu_info * info,struct device_node * pd_node)134*4882a593Smuzhiyun static int __init exynos5_clk_register_subcmu(struct device *parent,
135*4882a593Smuzhiyun const struct exynos5_subcmu_info *info,
136*4882a593Smuzhiyun struct device_node *pd_node)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun struct of_phandle_args genpdspec = { .np = pd_node };
139*4882a593Smuzhiyun struct platform_device *pdev;
140*4882a593Smuzhiyun int ret;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO);
143*4882a593Smuzhiyun if (!pdev)
144*4882a593Smuzhiyun return -ENOMEM;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun pdev->dev.parent = parent;
147*4882a593Smuzhiyun platform_set_drvdata(pdev, (void *)info);
148*4882a593Smuzhiyun of_genpd_add_device(&genpdspec, &pdev->dev);
149*4882a593Smuzhiyun ret = platform_device_add(pdev);
150*4882a593Smuzhiyun if (ret)
151*4882a593Smuzhiyun platform_device_put(pdev);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun return ret;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
exynos5_clk_probe(struct platform_device * pdev)156*4882a593Smuzhiyun static int __init exynos5_clk_probe(struct platform_device *pdev)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct device_node *np;
159*4882a593Smuzhiyun const char *name;
160*4882a593Smuzhiyun int i;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
163*4882a593Smuzhiyun if (of_property_read_string(np, "label", &name) < 0)
164*4882a593Smuzhiyun continue;
165*4882a593Smuzhiyun for (i = 0; i < nr_cmus; i++)
166*4882a593Smuzhiyun if (strcmp(cmu[i]->pd_name, name) == 0)
167*4882a593Smuzhiyun exynos5_clk_register_subcmu(&pdev->dev,
168*4882a593Smuzhiyun cmu[i], np);
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun static const struct of_device_id exynos5_clk_of_match[] = {
174*4882a593Smuzhiyun { .compatible = "samsung,exynos5250-clock", },
175*4882a593Smuzhiyun { .compatible = "samsung,exynos5420-clock", },
176*4882a593Smuzhiyun { .compatible = "samsung,exynos5800-clock", },
177*4882a593Smuzhiyun { },
178*4882a593Smuzhiyun };
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun static struct platform_driver exynos5_clk_driver __refdata = {
181*4882a593Smuzhiyun .driver = {
182*4882a593Smuzhiyun .name = "exynos5-clock",
183*4882a593Smuzhiyun .of_match_table = exynos5_clk_of_match,
184*4882a593Smuzhiyun .suppress_bind_attrs = true,
185*4882a593Smuzhiyun },
186*4882a593Smuzhiyun .probe = exynos5_clk_probe,
187*4882a593Smuzhiyun };
188*4882a593Smuzhiyun
exynos5_clk_drv_init(void)189*4882a593Smuzhiyun static int __init exynos5_clk_drv_init(void)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun platform_driver_register(&exynos5_clk_driver);
192*4882a593Smuzhiyun platform_driver_register(&exynos5_subcmu_driver);
193*4882a593Smuzhiyun return 0;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun core_initcall(exynos5_clk_drv_init);
196