Skip to content

Commit f05dc20

Browse files
Sam Protsenkokrzk
authored andcommitted
clk: samsung: exynos5433: Extract PM support to common ARM64 layer
Exynos5433 clock driver implements PM support internally, which might be also useful for other Exynos clock drivers. Extract all PM related code from clk-exynos5433 to common ARM64 functions. Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> Link: https://lore.kernel.org/r/20230307002423.24454-4-semen.protsenko@linaro.org Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
1 parent 454e8d2 commit f05dc20

File tree

3 files changed

+180
-156
lines changed

3 files changed

+180
-156
lines changed

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

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
*/
1111
#include <linux/clk.h>
1212
#include <linux/of_address.h>
13+
#include <linux/of_device.h>
14+
#include <linux/pm_runtime.h>
15+
#include <linux/slab.h>
1316

1417
#include "clk-exynos-arm64.h"
1518

@@ -21,6 +24,19 @@
2124
#define GATE_OFF_START 0x2000
2225
#define GATE_OFF_END 0x2fff
2326

27+
struct exynos_arm64_cmu_data {
28+
struct samsung_clk_reg_dump *clk_save;
29+
unsigned int nr_clk_save;
30+
const struct samsung_clk_reg_dump *clk_suspend;
31+
unsigned int nr_clk_suspend;
32+
33+
struct clk *clk;
34+
struct clk **pclks;
35+
int nr_pclks;
36+
37+
struct samsung_clk_provider *ctx;
38+
};
39+
2440
/**
2541
* exynos_arm64_init_clocks - Set clocks initial configuration
2642
* @np: CMU device tree node with "reg" property (CMU addr)
@@ -76,17 +92,63 @@ static int __init exynos_arm64_enable_bus_clk(struct device *dev,
7692
if (!cmu->clk_name)
7793
return 0;
7894

79-
if (dev)
95+
if (dev) {
96+
struct exynos_arm64_cmu_data *data;
97+
8098
parent_clk = clk_get(dev, cmu->clk_name);
81-
else
99+
data = dev_get_drvdata(dev);
100+
if (data)
101+
data->clk = parent_clk;
102+
} else {
82103
parent_clk = of_clk_get_by_name(np, cmu->clk_name);
104+
}
83105

84106
if (IS_ERR(parent_clk))
85107
return PTR_ERR(parent_clk);
86108

87109
return clk_prepare_enable(parent_clk);
88110
}
89111

112+
static int __init exynos_arm64_cmu_prepare_pm(struct device *dev,
113+
const struct samsung_cmu_info *cmu)
114+
{
115+
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
116+
int i;
117+
118+
data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs,
119+
cmu->nr_clk_regs);
120+
if (!data->clk_save)
121+
return -ENOMEM;
122+
123+
data->nr_clk_save = cmu->nr_clk_regs;
124+
data->clk_suspend = cmu->suspend_regs;
125+
data->nr_clk_suspend = cmu->nr_suspend_regs;
126+
data->nr_pclks = of_clk_get_parent_count(dev->of_node);
127+
if (!data->nr_pclks)
128+
return 0;
129+
130+
data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks,
131+
GFP_KERNEL);
132+
if (!data->pclks) {
133+
kfree(data->clk_save);
134+
return -ENOMEM;
135+
}
136+
137+
for (i = 0; i < data->nr_pclks; i++) {
138+
struct clk *clk = of_clk_get(dev->of_node, i);
139+
140+
if (IS_ERR(clk)) {
141+
kfree(data->clk_save);
142+
while (--i >= 0)
143+
clk_put(data->pclks[i]);
144+
return PTR_ERR(clk);
145+
}
146+
data->pclks[i] = clk;
147+
}
148+
149+
return 0;
150+
}
151+
90152
/**
91153
* exynos_arm64_register_cmu - Register specified Exynos CMU domain
92154
* @dev: Device object; may be NULL if this function is not being
@@ -117,3 +179,113 @@ void __init exynos_arm64_register_cmu(struct device *dev,
117179
exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs);
118180
samsung_cmu_register_one(np, cmu);
119181
}
182+
183+
/**
184+
* exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support
185+
*
186+
* @pdev: Platform device object
187+
* @set_manual: If true, set gate clocks to manual mode
188+
*
189+
* It's a version of exynos_arm64_register_cmu() with PM support. Should be
190+
* called from probe function of platform driver.
191+
*
192+
* Return: 0 on success, or negative error code on error.
193+
*/
194+
int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
195+
bool set_manual)
196+
{
197+
const struct samsung_cmu_info *cmu;
198+
struct device *dev = &pdev->dev;
199+
struct device_node *np = dev->of_node;
200+
struct exynos_arm64_cmu_data *data;
201+
void __iomem *reg_base;
202+
int ret;
203+
204+
cmu = of_device_get_match_data(dev);
205+
206+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
207+
if (!data)
208+
return -ENOMEM;
209+
210+
platform_set_drvdata(pdev, data);
211+
212+
ret = exynos_arm64_cmu_prepare_pm(dev, cmu);
213+
if (ret)
214+
return ret;
215+
216+
/*
217+
* Try to boot even if the parent clock enablement fails, as it might be
218+
* already enabled by bootloader.
219+
*/
220+
ret = exynos_arm64_enable_bus_clk(dev, NULL, cmu);
221+
if (ret)
222+
dev_err(dev, "%s: could not enable bus clock %s; err = %d\n",
223+
__func__, cmu->clk_name, ret);
224+
225+
if (set_manual)
226+
exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs);
227+
228+
reg_base = devm_platform_ioremap_resource(pdev, 0);
229+
if (IS_ERR(reg_base))
230+
return PTR_ERR(reg_base);
231+
232+
data->ctx = samsung_clk_init(dev, reg_base, cmu->nr_clk_ids);
233+
234+
/*
235+
* Enable runtime PM here to allow the clock core using runtime PM
236+
* for the registered clocks. Additionally, we increase the runtime
237+
* PM usage count before registering the clocks, to prevent the
238+
* clock core from runtime suspending the device.
239+
*/
240+
pm_runtime_get_noresume(dev);
241+
pm_runtime_set_active(dev);
242+
pm_runtime_enable(dev);
243+
244+
samsung_cmu_register_clocks(data->ctx, cmu);
245+
samsung_clk_of_add_provider(dev->of_node, data->ctx);
246+
pm_runtime_put_sync(dev);
247+
248+
return 0;
249+
}
250+
251+
int exynos_arm64_cmu_suspend(struct device *dev)
252+
{
253+
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
254+
int i;
255+
256+
samsung_clk_save(data->ctx->reg_base, data->clk_save,
257+
data->nr_clk_save);
258+
259+
for (i = 0; i < data->nr_pclks; i++)
260+
clk_prepare_enable(data->pclks[i]);
261+
262+
/* For suspend some registers have to be set to certain values */
263+
samsung_clk_restore(data->ctx->reg_base, data->clk_suspend,
264+
data->nr_clk_suspend);
265+
266+
for (i = 0; i < data->nr_pclks; i++)
267+
clk_disable_unprepare(data->pclks[i]);
268+
269+
clk_disable_unprepare(data->clk);
270+
271+
return 0;
272+
}
273+
274+
int exynos_arm64_cmu_resume(struct device *dev)
275+
{
276+
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
277+
int i;
278+
279+
clk_prepare_enable(data->clk);
280+
281+
for (i = 0; i < data->nr_pclks; i++)
282+
clk_prepare_enable(data->pclks[i]);
283+
284+
samsung_clk_restore(data->ctx->reg_base, data->clk_save,
285+
data->nr_clk_save);
286+
287+
for (i = 0; i < data->nr_pclks; i++)
288+
clk_disable_unprepare(data->pclks[i]);
289+
290+
return 0;
291+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@
1616

1717
void exynos_arm64_register_cmu(struct device *dev,
1818
struct device_node *np, const struct samsung_cmu_info *cmu);
19+
int exynos_arm64_register_cmu_pm(struct platform_device *pdev, bool set_manual);
20+
int exynos_arm64_cmu_suspend(struct device *dev);
21+
int exynos_arm64_cmu_resume(struct device *dev);
1922

2023
#endif /* __CLK_EXYNOS_ARM64_H */

0 commit comments

Comments
 (0)