2 * Power domain support for Rockchip RK3368
4 * Copyright (C) 2014-2015 ROCKCHIP, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 #include <linux/of_address.h>
19 #include <linux/rockchip/pmu.h>
20 #include <linux/rockchip/cru.h>
24 static void __iomem *rk_pmu_base;
26 static u32 pmu_readl(u32 offset)
28 return readl_relaxed(rk_pmu_base + (offset));
31 static void pmu_writel(u32 val, u32 offset)
33 writel_relaxed(val, rk_pmu_base + (offset));
37 static const u8 pmu_pd_map[] = {
45 static const u8 pmu_st_map[] = {
53 static bool rk3368_pmu_power_domain_is_on(enum pmu_power_domain pd)
55 /* 1'b0: power on, 1'b1: power off */
56 return !(pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[pd]));
59 static DEFINE_SPINLOCK(pmu_idle_lock);
61 static const u8 pmu_idle_map[] = {
69 static int rk3368_pmu_set_idle_request(enum pmu_idle_req req, bool idle)
71 u32 bit = pmu_idle_map[req];
72 u32 idle_mask = BIT(bit) | BIT(bit + 16);
73 u32 idle_target = (idle << bit) | (idle << (bit + 16));
78 spin_lock_irqsave(&pmu_idle_lock, flags);
80 val = pmu_readl(RK3368_PMU_IDLE_REQ);
85 pmu_writel(val, RK3368_PMU_IDLE_REQ);
88 while ((pmu_readl(RK3368_PMU_IDLE_ST) & idle_mask) != idle_target)
91 spin_unlock_irqrestore(&pmu_idle_lock, flags);
96 static DEFINE_SPINLOCK(pmu_pd_lock);
98 static noinline void rk3368_do_pmu_set_power_domain
99 (enum pmu_power_domain domain, bool on)
101 u8 pd = pmu_pd_map[domain];
102 u32 val = pmu_readl(RK3368_PMU_PWRDN_CON);
109 pmu_writel(val, RK3368_PMU_PWRDN_CON);
112 while ((pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[domain])) == on)
116 static int rk3368_pmu_set_power_domain(enum pmu_power_domain pd, bool on)
120 spin_lock_irqsave(&pmu_pd_lock, flags);
122 if (rk3368_pmu_power_domain_is_on(pd) == on)
126 /* if power down, idle request to NIU first */
128 rk3368_pmu_set_idle_request(IDLE_REQ_VIO, true);
129 else if (pd == PD_VIDEO)
130 rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, true);
131 else if (pd == PD_GPU_0)
132 rk3368_pmu_set_idle_request(IDLE_REQ_GPU, true);
133 else if (pd == PD_PERI)
134 rk3368_pmu_set_idle_request(IDLE_REQ_PERI, true);
137 rk3368_do_pmu_set_power_domain(pd, on);
140 /* if power up, idle request release to NIU */
142 rk3368_pmu_set_idle_request(IDLE_REQ_VIO, false);
143 else if (pd == PD_VIDEO)
144 rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, false);
145 else if (pd == PD_GPU_0)
146 rk3368_pmu_set_idle_request(IDLE_REQ_GPU, false);
147 else if (pd == PD_PERI)
148 rk3368_pmu_set_idle_request(IDLE_REQ_PERI, false);
152 spin_unlock_irqrestore(&pmu_pd_lock, flags);
156 static int rk3368_sys_set_power_domain(enum pmu_power_domain pd, bool on)
158 u32 clks_ungating[RK3368_CRU_CLKGATES_CON_CNT];
159 u32 clks_save[RK3368_CRU_CLKGATES_CON_CNT];
162 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++) {
163 clks_save[i] = cru_readl(RK3368_CRU_CLKGATES_CON(i));
164 clks_ungating[i] = 0;
167 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
168 cru_writel(0xffff0000, RK3368_CRU_CLKGATES_CON(i));
170 ret = rk3368_pmu_set_power_domain(pd, on);
172 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
173 cru_writel(clks_save[i] | 0xffff0000,
174 RK3368_CRU_CLKGATES_CON(i));
179 static int __init rk3368_init_rockchip_pmu_ops(void)
181 struct device_node *node;
183 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3368-pmu");
185 pr_err("%s: could not find pmu dt node\n", __func__);
189 rk_pmu_base = of_iomap(node, 0);
191 pr_err("%s: could not map pmu registers\n", __func__);
195 rockchip_pmu_ops.set_power_domain = rk3368_sys_set_power_domain;
196 rockchip_pmu_ops.power_domain_is_on = rk3368_pmu_power_domain_is_on;
201 arch_initcall(rk3368_init_rockchip_pmu_ops);