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>
21 #include <dt-bindings/power/rk3366-power.h>
22 #include <dt-bindings/power/rk3368-power.h>
23 #include <dt-bindings/power/rk3399-power.h>
25 struct rockchip_domain_info {
33 struct rockchip_pmu_info {
40 u32 core_pwrcnt_offset;
41 u32 gpu_pwrcnt_offset;
43 unsigned int core_power_transition_time;
44 unsigned int gpu_power_transition_time;
47 const struct rockchip_domain_info *domain_info;
50 struct rockchip_pm_domain {
51 struct generic_pm_domain genpd;
52 const struct rockchip_domain_info *info;
53 struct rockchip_pmu *pmu;
60 struct regmap *regmap;
61 const struct rockchip_pmu_info *info;
62 struct mutex mutex; /* mutex lock for pmu */
63 struct genpd_onecell_data genpd_data;
64 struct generic_pm_domain *domains[];
67 #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
69 #define DOMAIN(pwr, status, req, idle, ack) \
71 .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \
72 .status_mask = (status >= 0) ? BIT(status) : 0, \
73 .req_mask = (req >= 0) ? BIT(req) : 0, \
74 .idle_mask = (idle >= 0) ? BIT(idle) : 0, \
75 .ack_mask = (ack >= 0) ? BIT(ack) : 0, \
78 #define DOMAIN_RK3288(pwr, status, req) \
79 DOMAIN(pwr, status, req, req, (req) + 16)
81 #define DOMAIN_RK3368(pwr, status, req) \
82 DOMAIN(pwr, status, req, (req) + 16, req)
84 #define DOMAIN_RK3399(pwr, status, req) \
85 DOMAIN(pwr, status, req, req, req)
87 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
89 struct rockchip_pmu *pmu = pd->pmu;
90 const struct rockchip_domain_info *pd_info = pd->info;
93 regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
94 return (val & pd_info->idle_mask) == pd_info->idle_mask;
97 static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
100 const struct rockchip_domain_info *pd_info = pd->info;
101 struct rockchip_pmu *pmu = pd->pmu;
104 if (pd_info->req_mask == 0)
107 regmap_update_bits(pmu->regmap, pmu->info->req_offset,
108 pd_info->req_mask, idle ? -1U : 0);
113 regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
114 } while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));
116 while (rockchip_pmu_domain_is_idle(pd) != idle)
122 static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
124 struct rockchip_pmu *pmu = pd->pmu;
127 /* check idle status for idle-only domains */
128 if (pd->info->status_mask == 0)
129 return !rockchip_pmu_domain_is_idle(pd);
131 regmap_read(pmu->regmap, pmu->info->status_offset, &val);
133 /* 1'b0: power on, 1'b1: power off */
134 return !(val & pd->info->status_mask);
137 static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
140 struct rockchip_pmu *pmu = pd->pmu;
142 if (pd->info->pwr_mask == 0)
145 regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
146 pd->info->pwr_mask, on ? 0 : -1U);
150 while (rockchip_pmu_domain_is_on(pd) != on)
154 static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
158 mutex_lock(&pd->pmu->mutex);
160 if (rockchip_pmu_domain_is_on(pd) != power_on) {
161 for (i = 0; i < pd->num_clks; i++)
162 clk_enable(pd->clks[i]);
165 /* FIXME: add code to save AXI_QOS */
167 /* if powering down, idle request to NIU first */
168 rockchip_pmu_set_idle_request(pd, true);
171 rockchip_do_pmu_set_power_domain(pd, power_on);
174 /* if powering up, leave idle mode */
175 rockchip_pmu_set_idle_request(pd, false);
177 /* FIXME: add code to restore AXI_QOS */
180 for (i = pd->num_clks - 1; i >= 0; i--)
181 clk_disable(pd->clks[i]);
184 mutex_unlock(&pd->pmu->mutex);
188 static int rockchip_pd_power_on(struct generic_pm_domain *domain)
190 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
192 return rockchip_pd_power(pd, true);
195 static int rockchip_pd_power_off(struct generic_pm_domain *domain)
197 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
199 return rockchip_pd_power(pd, false);
202 static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd,
209 dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
211 error = pm_clk_create(dev);
213 dev_err(dev, "pm_clk_create failed %d\n", error);
218 while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
219 dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk);
220 error = pm_clk_add_clk(dev, clk);
222 dev_err(dev, "pm_clk_add_clk failed %d\n", error);
232 static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
235 dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name);
240 static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
241 struct device_node *node)
243 const struct rockchip_domain_info *pd_info;
244 struct rockchip_pm_domain *pd;
251 error = of_property_read_u32(node, "reg", &id);
254 "%s: failed to retrieve domain id (reg): %d\n",
259 if (id >= pmu->info->num_domains) {
260 dev_err(pmu->dev, "%s: invalid domain id %d\n",
265 pd_info = &pmu->info->domain_info[id];
267 dev_err(pmu->dev, "%s: undefined domain id %d\n",
272 clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
273 pd = devm_kzalloc(pmu->dev,
274 sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
282 for (i = 0; i < clk_cnt; i++) {
283 clk = of_clk_get(node, i);
285 error = PTR_ERR(clk);
287 "%s: failed to get clk at index %d: %d\n",
288 node->name, i, error);
292 error = clk_prepare(clk);
295 "%s: failed to prepare clk %pC (index %d): %d\n",
296 node->name, clk, i, error);
301 pd->clks[pd->num_clks++] = clk;
303 dev_dbg(pmu->dev, "added clock '%pC' to domain '%s'\n",
307 error = rockchip_pd_power(pd, true);
310 "failed to power on domain '%s': %d\n",
315 pd->genpd.name = node->name;
316 pd->genpd.power_off = rockchip_pd_power_off;
317 pd->genpd.power_on = rockchip_pd_power_on;
318 pd->genpd.attach_dev = rockchip_pd_attach_dev;
319 pd->genpd.detach_dev = rockchip_pd_detach_dev;
320 pd->genpd.flags = GENPD_FLAG_PM_CLK;
321 pm_genpd_init(&pd->genpd, NULL, false);
323 pmu->genpd_data.domains[id] = &pd->genpd;
328 clk_unprepare(pd->clks[i]);
329 clk_put(pd->clks[i]);
334 static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
338 for (i = 0; i < pd->num_clks; i++) {
339 clk_unprepare(pd->clks[i]);
340 clk_put(pd->clks[i]);
343 /* protect the zeroing of pm->num_clks */
344 mutex_lock(&pd->pmu->mutex);
346 mutex_unlock(&pd->pmu->mutex);
348 /* devm will free our memory */
351 static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
353 struct generic_pm_domain *genpd;
354 struct rockchip_pm_domain *pd;
357 for (i = 0; i < pmu->genpd_data.num_domains; i++) {
358 genpd = pmu->genpd_data.domains[i];
360 pd = to_rockchip_pd(genpd);
361 rockchip_pm_remove_one_domain(pd);
365 /* devm will free our memory */
368 static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu,
369 u32 domain_reg_offset,
372 /* First configure domain power down transition count ... */
373 regmap_write(pmu->regmap, domain_reg_offset, count);
374 /* ... and then power up count. */
375 regmap_write(pmu->regmap, domain_reg_offset + 4, count);
378 static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu,
379 struct device_node *parent)
381 struct device_node *np;
382 struct generic_pm_domain *child_domain, *parent_domain;
385 for_each_child_of_node(parent, np) {
388 error = of_property_read_u32(parent, "reg", &idx);
391 "%s: failed to retrieve domain id (reg): %d\n",
392 parent->name, error);
395 parent_domain = pmu->genpd_data.domains[idx];
397 error = rockchip_pm_add_one_domain(pmu, np);
399 dev_err(pmu->dev, "failed to handle node %s: %d\n",
404 error = of_property_read_u32(np, "reg", &idx);
407 "%s: failed to retrieve domain id (reg): %d\n",
411 child_domain = pmu->genpd_data.domains[idx];
413 error = pm_genpd_add_subdomain(parent_domain, child_domain);
415 dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n",
416 parent_domain->name, child_domain->name, error);
419 dev_dbg(pmu->dev, "%s add subdomain: %s\n",
420 parent_domain->name, child_domain->name);
423 rockchip_pm_add_subdomain(pmu, np);
433 static int rockchip_pm_domain_probe(struct platform_device *pdev)
435 struct device *dev = &pdev->dev;
436 struct device_node *np = dev->of_node;
437 struct device_node *node;
438 struct device *parent;
439 struct rockchip_pmu *pmu;
440 const struct of_device_id *match;
441 const struct rockchip_pmu_info *pmu_info;
445 dev_err(dev, "device tree node not found\n");
449 match = of_match_device(dev->driver->of_match_table, dev);
450 if (!match || !match->data) {
451 dev_err(dev, "missing pmu data\n");
455 pmu_info = match->data;
457 pmu = devm_kzalloc(dev,
459 pmu_info->num_domains * sizeof(pmu->domains[0]),
464 pmu->dev = &pdev->dev;
465 mutex_init(&pmu->mutex);
467 pmu->info = pmu_info;
469 pmu->genpd_data.domains = pmu->domains;
470 pmu->genpd_data.num_domains = pmu_info->num_domains;
472 parent = dev->parent;
474 dev_err(dev, "no parent for syscon devices\n");
478 pmu->regmap = syscon_node_to_regmap(parent->of_node);
481 * Configure power up and down transition delays for CORE
484 rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset,
485 pmu_info->core_power_transition_time);
486 rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset,
487 pmu_info->gpu_power_transition_time);
491 for_each_available_child_of_node(np, node) {
492 error = rockchip_pm_add_one_domain(pmu, node);
494 dev_err(dev, "failed to handle node %s: %d\n",
499 error = rockchip_pm_add_subdomain(pmu, node);
501 dev_err(dev, "failed to handle subdomain node %s: %d\n",
509 dev_dbg(dev, "no power domains defined\n");
513 of_genpd_add_provider_onecell(np, &pmu->genpd_data);
518 rockchip_pm_domain_cleanup(pmu);
522 static const struct rockchip_domain_info rk3288_pm_domains[] = {
523 [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4),
524 [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9),
525 [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3),
526 [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2),
529 static const struct rockchip_domain_info rk3366_pm_domains[] = {
530 [RK3366_PD_PERI] = DOMAIN_RK3368(10, 10, 6),
531 [RK3366_PD_VIO] = DOMAIN_RK3368(14, 14, 8),
532 [RK3366_PD_VIDEO] = DOMAIN_RK3368(13, 13, 7),
533 [RK3366_PD_RKVDEC] = DOMAIN_RK3368(11, 11, 7),
534 [RK3366_PD_WIFIBT] = DOMAIN_RK3368(8, 8, 9),
535 [RK3366_PD_VPU] = DOMAIN_RK3368(12, 12, 7),
536 [RK3366_PD_GPU] = DOMAIN_RK3368(15, 15, 2),
539 static const struct rockchip_domain_info rk3368_pm_domains[] = {
540 [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6),
541 [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8),
542 [RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7),
543 [RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2),
544 [RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2),
547 static const struct rockchip_domain_info rk3399_pm_domains[] = {
548 [RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1),
549 [RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1),
550 [RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1),
551 [RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15),
552 [RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16),
553 [RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1),
554 [RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2),
555 [RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14),
556 [RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17),
557 [RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0),
558 [RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3),
559 [RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4),
560 [RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5),
561 [RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6),
562 [RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1),
563 [RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7),
564 [RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8),
565 [RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9),
566 [RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10),
567 [RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11),
568 [RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23),
569 [RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24),
570 [RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12),
571 [RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22),
572 [RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27),
573 [RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28),
574 [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29),
577 static const struct rockchip_pmu_info rk3288_pmu = {
579 .status_offset = 0x0c,
584 .core_pwrcnt_offset = 0x34,
585 .gpu_pwrcnt_offset = 0x3c,
587 .core_power_transition_time = 24, /* 1us */
588 .gpu_power_transition_time = 24, /* 1us */
590 .num_domains = ARRAY_SIZE(rk3288_pm_domains),
591 .domain_info = rk3288_pm_domains,
594 static const struct rockchip_pmu_info rk3366_pmu = {
596 .status_offset = 0x10,
601 .core_pwrcnt_offset = 0x48,
602 .gpu_pwrcnt_offset = 0x50,
604 .core_power_transition_time = 24,
605 .gpu_power_transition_time = 24,
607 .num_domains = ARRAY_SIZE(rk3366_pm_domains),
608 .domain_info = rk3366_pm_domains,
611 static const struct rockchip_pmu_info rk3368_pmu = {
613 .status_offset = 0x10,
618 .core_pwrcnt_offset = 0x48,
619 .gpu_pwrcnt_offset = 0x50,
621 .core_power_transition_time = 24,
622 .gpu_power_transition_time = 24,
624 .num_domains = ARRAY_SIZE(rk3368_pm_domains),
625 .domain_info = rk3368_pm_domains,
628 static const struct rockchip_pmu_info rk3399_pmu = {
630 .status_offset = 0x18,
635 .core_pwrcnt_offset = 0x9c,
636 .gpu_pwrcnt_offset = 0xa4,
638 .core_power_transition_time = 24,
639 .gpu_power_transition_time = 24,
641 .num_domains = ARRAY_SIZE(rk3399_pm_domains),
642 .domain_info = rk3399_pm_domains,
645 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
647 .compatible = "rockchip,rk3288-power-controller",
648 .data = (void *)&rk3288_pmu,
651 .compatible = "rockchip,rk3366-power-controller",
652 .data = (void *)&rk3366_pmu,
655 .compatible = "rockchip,rk3368-power-controller",
656 .data = (void *)&rk3368_pmu,
659 .compatible = "rockchip,rk3399-power-controller",
660 .data = (void *)&rk3399_pmu,
665 static struct platform_driver rockchip_pm_domain_driver = {
666 .probe = rockchip_pm_domain_probe,
668 .name = "rockchip-pm-domain",
669 .of_match_table = rockchip_pm_domain_dt_match,
671 * We can't forcibly eject devices form power domain,
672 * so we can't really remove power domains once they
675 .suppress_bind_attrs = true,
679 static int __init rockchip_pm_domain_drv_register(void)
681 return platform_driver_register(&rockchip_pm_domain_driver);
683 postcore_initcall(rockchip_pm_domain_drv_register);