2 * Rockchip Generic power domain support.
4 * Copyright (c) 2015 ROCKCHIP, Co. Ltd.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
12 #include <linux/err.h>
13 #include <linux/pm_clock.h>
14 #include <linux/pm_domain.h>
15 #include <linux/of_address.h>
16 #include <linux/of_platform.h>
17 #include <linux/clk.h>
18 #include <linux/regmap.h>
19 #include <linux/mfd/syscon.h>
20 #include <dt-bindings/power/rk3288-power.h>
22 struct rockchip_domain_info {
30 struct rockchip_pmu_info {
37 u32 core_pwrcnt_offset;
38 u32 gpu_pwrcnt_offset;
40 unsigned int core_power_transition_time;
41 unsigned int gpu_power_transition_time;
44 const struct rockchip_domain_info *domain_info;
47 struct rockchip_pm_domain {
48 struct generic_pm_domain genpd;
49 const struct rockchip_domain_info *info;
50 struct rockchip_pmu *pmu;
57 struct regmap *regmap;
58 const struct rockchip_pmu_info *info;
59 struct mutex mutex; /* mutex lock for pmu */
60 struct genpd_onecell_data genpd_data;
61 struct generic_pm_domain *domains[];
64 #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
66 #define DOMAIN(pwr, status, req, idle, ack) \
68 .pwr_mask = BIT(pwr), \
69 .status_mask = BIT(status), \
70 .req_mask = BIT(req), \
71 .idle_mask = BIT(idle), \
72 .ack_mask = BIT(ack), \
75 #define DOMAIN_RK3288(pwr, status, req) \
76 DOMAIN(pwr, status, req, req, (req) + 16)
78 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
80 struct rockchip_pmu *pmu = pd->pmu;
81 const struct rockchip_domain_info *pd_info = pd->info;
84 regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
85 return (val & pd_info->idle_mask) == pd_info->idle_mask;
88 static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
91 const struct rockchip_domain_info *pd_info = pd->info;
92 struct rockchip_pmu *pmu = pd->pmu;
95 regmap_update_bits(pmu->regmap, pmu->info->req_offset,
96 pd_info->req_mask, idle ? -1U : 0);
101 regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
102 } while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));
104 while (rockchip_pmu_domain_is_idle(pd) != idle)
110 static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
112 struct rockchip_pmu *pmu = pd->pmu;
115 regmap_read(pmu->regmap, pmu->info->status_offset, &val);
117 /* 1'b0: power on, 1'b1: power off */
118 return !(val & pd->info->status_mask);
121 static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
124 struct rockchip_pmu *pmu = pd->pmu;
126 regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
127 pd->info->pwr_mask, on ? 0 : -1U);
131 while (rockchip_pmu_domain_is_on(pd) != on)
135 static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
139 mutex_lock(&pd->pmu->mutex);
141 if (rockchip_pmu_domain_is_on(pd) != power_on) {
142 for (i = 0; i < pd->num_clks; i++)
143 clk_enable(pd->clks[i]);
146 /* FIXME: add code to save AXI_QOS */
148 /* if powering down, idle request to NIU first */
149 rockchip_pmu_set_idle_request(pd, true);
152 rockchip_do_pmu_set_power_domain(pd, power_on);
155 /* if powering up, leave idle mode */
156 rockchip_pmu_set_idle_request(pd, false);
158 /* FIXME: add code to restore AXI_QOS */
161 for (i = pd->num_clks - 1; i >= 0; i--)
162 clk_disable(pd->clks[i]);
165 mutex_unlock(&pd->pmu->mutex);
169 static int rockchip_pd_power_on(struct generic_pm_domain *domain)
171 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
173 return rockchip_pd_power(pd, true);
176 static int rockchip_pd_power_off(struct generic_pm_domain *domain)
178 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
180 return rockchip_pd_power(pd, false);
183 static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd,
190 dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
192 error = pm_clk_create(dev);
194 dev_err(dev, "pm_clk_create failed %d\n", error);
199 while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
200 dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk);
201 error = pm_clk_add_clk(dev, clk);
203 dev_err(dev, "pm_clk_add_clk failed %d\n", error);
213 static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
216 dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name);
221 static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
222 struct device_node *node)
224 const struct rockchip_domain_info *pd_info;
225 struct rockchip_pm_domain *pd;
232 error = of_property_read_u32(node, "reg", &id);
235 "%s: failed to retrieve domain id (reg): %d\n",
240 if (id >= pmu->info->num_domains) {
241 dev_err(pmu->dev, "%s: invalid domain id %d\n",
246 pd_info = &pmu->info->domain_info[id];
248 dev_err(pmu->dev, "%s: undefined domain id %d\n",
253 clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
254 pd = devm_kzalloc(pmu->dev,
255 sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
263 for (i = 0; i < clk_cnt; i++) {
264 clk = of_clk_get(node, i);
266 error = PTR_ERR(clk);
268 "%s: failed to get clk at index %d: %d\n",
269 node->name, i, error);
273 error = clk_prepare(clk);
276 "%s: failed to prepare clk %pC (index %d): %d\n",
277 node->name, clk, i, error);
282 pd->clks[pd->num_clks++] = clk;
284 dev_dbg(pmu->dev, "added clock '%pC' to domain '%s'\n",
288 error = rockchip_pd_power(pd, true);
291 "failed to power on domain '%s': %d\n",
296 pd->genpd.name = node->name;
297 pd->genpd.power_off = rockchip_pd_power_off;
298 pd->genpd.power_on = rockchip_pd_power_on;
299 pd->genpd.attach_dev = rockchip_pd_attach_dev;
300 pd->genpd.detach_dev = rockchip_pd_detach_dev;
301 pd->genpd.flags = GENPD_FLAG_PM_CLK;
302 pm_genpd_init(&pd->genpd, NULL, false);
304 pmu->genpd_data.domains[id] = &pd->genpd;
309 clk_unprepare(pd->clks[i]);
310 clk_put(pd->clks[i]);
315 static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
319 for (i = 0; i < pd->num_clks; i++) {
320 clk_unprepare(pd->clks[i]);
321 clk_put(pd->clks[i]);
324 /* protect the zeroing of pm->num_clks */
325 mutex_lock(&pd->pmu->mutex);
327 mutex_unlock(&pd->pmu->mutex);
329 /* devm will free our memory */
332 static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
334 struct generic_pm_domain *genpd;
335 struct rockchip_pm_domain *pd;
338 for (i = 0; i < pmu->genpd_data.num_domains; i++) {
339 genpd = pmu->genpd_data.domains[i];
341 pd = to_rockchip_pd(genpd);
342 rockchip_pm_remove_one_domain(pd);
346 /* devm will free our memory */
349 static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu,
350 u32 domain_reg_offset,
353 /* First configure domain power down transition count ... */
354 regmap_write(pmu->regmap, domain_reg_offset, count);
355 /* ... and then power up count. */
356 regmap_write(pmu->regmap, domain_reg_offset + 4, count);
359 static int rockchip_pm_domain_probe(struct platform_device *pdev)
361 struct device *dev = &pdev->dev;
362 struct device_node *np = dev->of_node;
363 struct device_node *node;
364 struct device *parent;
365 struct rockchip_pmu *pmu;
366 const struct of_device_id *match;
367 const struct rockchip_pmu_info *pmu_info;
371 dev_err(dev, "device tree node not found\n");
375 match = of_match_device(dev->driver->of_match_table, dev);
376 if (!match || !match->data) {
377 dev_err(dev, "missing pmu data\n");
381 pmu_info = match->data;
383 pmu = devm_kzalloc(dev,
385 pmu_info->num_domains * sizeof(pmu->domains[0]),
390 pmu->dev = &pdev->dev;
391 mutex_init(&pmu->mutex);
393 pmu->info = pmu_info;
395 pmu->genpd_data.domains = pmu->domains;
396 pmu->genpd_data.num_domains = pmu_info->num_domains;
398 parent = dev->parent;
400 dev_err(dev, "no parent for syscon devices\n");
404 pmu->regmap = syscon_node_to_regmap(parent->of_node);
407 * Configure power up and down transition delays for CORE
410 rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset,
411 pmu_info->core_power_transition_time);
412 rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset,
413 pmu_info->gpu_power_transition_time);
417 for_each_available_child_of_node(np, node) {
418 error = rockchip_pm_add_one_domain(pmu, node);
420 dev_err(dev, "failed to handle node %s: %d\n",
428 dev_dbg(dev, "no power domains defined\n");
432 of_genpd_add_provider_onecell(np, &pmu->genpd_data);
437 rockchip_pm_domain_cleanup(pmu);
441 static const struct rockchip_domain_info rk3288_pm_domains[] = {
442 [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4),
443 [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9),
444 [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3),
445 [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2),
448 static const struct rockchip_pmu_info rk3288_pmu = {
450 .status_offset = 0x0c,
455 .core_pwrcnt_offset = 0x34,
456 .gpu_pwrcnt_offset = 0x3c,
458 .core_power_transition_time = 24, /* 1us */
459 .gpu_power_transition_time = 24, /* 1us */
461 .num_domains = ARRAY_SIZE(rk3288_pm_domains),
462 .domain_info = rk3288_pm_domains,
465 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
467 .compatible = "rockchip,rk3288-power-controller",
468 .data = (void *)&rk3288_pmu,
473 static struct platform_driver rockchip_pm_domain_driver = {
474 .probe = rockchip_pm_domain_probe,
476 .name = "rockchip-pm-domain",
477 .of_match_table = rockchip_pm_domain_dt_match,
479 * We can't forcibly eject devices form power domain,
480 * so we can't really remove power domains once they
483 .suppress_bind_attrs = true,
487 static int __init rockchip_pm_domain_drv_register(void)
489 return platform_driver_register(&rockchip_pm_domain_driver);
491 postcore_initcall(rockchip_pm_domain_drv_register);