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>
21 #include <linux/rockchip/cpu_axi.h>
25 static void __iomem *rk_pmu_base;
27 static u32 pmu_readl(u32 offset)
29 return readl_relaxed(rk_pmu_base + (offset));
32 static void pmu_writel(u32 val, u32 offset)
34 writel_relaxed(val, rk_pmu_base + (offset));
38 static const u8 pmu_pd_map[] = {
46 static const u8 pmu_st_map[] = {
54 static bool rk3368_pmu_power_domain_is_on(enum pmu_power_domain pd)
56 /* 1'b0: power on, 1'b1: power off */
57 return !(pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[pd]));
60 static DEFINE_SPINLOCK(pmu_idle_lock);
62 static const u8 pmu_idle_map[] = {
70 static int rk3368_pmu_set_idle_request(enum pmu_idle_req req, bool idle)
72 u32 bit = pmu_idle_map[req];
73 u32 idle_mask = BIT(bit) | BIT(bit + 16);
74 u32 idle_target = (idle << bit) | (idle << (bit + 16));
79 spin_lock_irqsave(&pmu_idle_lock, flags);
81 val = pmu_readl(RK3368_PMU_IDLE_REQ);
86 pmu_writel(val, RK3368_PMU_IDLE_REQ);
89 while ((pmu_readl(RK3368_PMU_IDLE_ST) & idle_mask) != idle_target)
92 spin_unlock_irqrestore(&pmu_idle_lock, flags);
97 static DEFINE_SPINLOCK(pmu_pd_lock);
99 static noinline void rk3368_do_pmu_set_power_domain
100 (enum pmu_power_domain domain, bool on)
102 u8 pd = pmu_pd_map[domain];
103 u32 val = pmu_readl(RK3368_PMU_PWRDN_CON);
110 pmu_writel(val, RK3368_PMU_PWRDN_CON);
113 while ((pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[domain])) == on)
118 static void __iomem *iep_qos_base;
119 static u32 iep_qos[CPU_AXI_QOS_NUM_REGS];
120 static void __iomem *isp_r0_qos_base;
121 static u32 isp_r0_qos[CPU_AXI_QOS_NUM_REGS];
122 static void __iomem *isp_r1_qos_base;
123 static u32 isp_r1_qos[CPU_AXI_QOS_NUM_REGS];
124 static void __iomem *isp_w0_qos_base;
125 static u32 isp_w0_qos[CPU_AXI_QOS_NUM_REGS];
126 static void __iomem *isp_w1_qos_base;
127 static u32 isp_w1_qos[CPU_AXI_QOS_NUM_REGS];
128 static void __iomem *vip_qos_base;
129 static u32 vip_qos[CPU_AXI_QOS_NUM_REGS];
130 static void __iomem *vop_qos_base;
131 static u32 vop_qos[CPU_AXI_QOS_NUM_REGS];
132 static void __iomem *rga_r_qos_base;
133 static u32 rga_r_qos[CPU_AXI_QOS_NUM_REGS];
134 static void __iomem *rga_w_qos_base;
135 static u32 rga_w_qos[CPU_AXI_QOS_NUM_REGS];
137 static void __iomem *hevc_r_qos_base;
138 static u32 hevc_r_qos[CPU_AXI_QOS_NUM_REGS];
139 static void __iomem *vpu_r_qos_base;
140 static u32 vpu_r_qos[CPU_AXI_QOS_NUM_REGS];
141 static void __iomem *vpu_w_qos_base;
142 static u32 vpu_w_qos[CPU_AXI_QOS_NUM_REGS];
144 static void __iomem *gpu_qos_base;
145 static u32 gpu_qos[CPU_AXI_QOS_NUM_REGS];
147 static void __iomem *peri_qos_base;
148 static u32 peri_qos[CPU_AXI_QOS_NUM_REGS];
150 #define PD_SAVE_QOS(name) CPU_AXI_SAVE_QOS(name##_qos, name##_qos_base)
151 #define PD_RESTORE_QOS(name) CPU_AXI_RESTORE_QOS(name##_qos, name##_qos_base)
153 static int rk3368_pmu_set_power_domain(enum pmu_power_domain pd, bool on)
157 spin_lock_irqsave(&pmu_pd_lock, flags);
159 if (rk3368_pmu_power_domain_is_on(pd) == on)
163 /* if power down, idle request to NIU first */
174 rk3368_pmu_set_idle_request(IDLE_REQ_VIO, true);
175 } else if (pd == PD_VIDEO) {
179 rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, true);
180 } else if (pd == PD_GPU_0) {
182 rk3368_pmu_set_idle_request(IDLE_REQ_GPU, true);
183 } else if (pd == PD_PERI) {
185 rk3368_pmu_set_idle_request(IDLE_REQ_PERI, true);
189 rk3368_do_pmu_set_power_domain(pd, on);
192 /* if power up, idle request release to NIU */
194 rk3368_pmu_set_idle_request(IDLE_REQ_VIO, false);
196 PD_RESTORE_QOS(isp_r0);
197 PD_RESTORE_QOS(isp_r1);
198 PD_RESTORE_QOS(isp_w0);
199 PD_RESTORE_QOS(isp_w1);
202 PD_RESTORE_QOS(rga_r);
203 PD_RESTORE_QOS(rga_w);
204 } else if (pd == PD_VIDEO) {
205 rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, false);
206 PD_RESTORE_QOS(hevc_r);
207 PD_RESTORE_QOS(vpu_r);
208 PD_RESTORE_QOS(vpu_w);
209 } else if (pd == PD_GPU_0) {
210 rk3368_pmu_set_idle_request(IDLE_REQ_GPU, false);
212 } else if (pd == PD_PERI) {
213 rk3368_pmu_set_idle_request(IDLE_REQ_PERI, false);
214 PD_RESTORE_QOS(peri);
219 spin_unlock_irqrestore(&pmu_pd_lock, flags);
223 static int rk3368_sys_set_power_domain(enum pmu_power_domain pd, bool on)
225 u32 clks_ungating[RK3368_CRU_CLKGATES_CON_CNT];
226 u32 clks_save[RK3368_CRU_CLKGATES_CON_CNT];
229 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++) {
230 clks_save[i] = cru_readl(RK3368_CRU_CLKGATES_CON(i));
231 clks_ungating[i] = 0;
234 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
235 cru_writel(0xffff0000, RK3368_CRU_CLKGATES_CON(i));
237 ret = rk3368_pmu_set_power_domain(pd, on);
239 for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
240 cru_writel(clks_save[i] | 0xffff0000,
241 RK3368_CRU_CLKGATES_CON(i));
246 static int __init rk3368_init_rockchip_pmu_ops(void)
248 struct device_node *node, *gp, *cp;
250 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3368-pmu");
252 pr_err("%s: could not find pmu dt node\n", __func__);
256 rk_pmu_base = of_iomap(node, 0);
258 pr_err("%s: could not map pmu registers\n", __func__);
262 node = of_find_compatible_node(NULL, NULL, "rockchip,cpu_axi_bus");
268 cp = of_get_child_by_name(gp, #name); \
270 name##_qos_base = of_iomap(cp, 0); \
271 if (!name##_qos_base) \
272 pr_err("%s: could not map qos %s register\n", \
276 gp = of_get_child_by_name(node, "qos");
296 rockchip_pmu_ops.set_power_domain = rk3368_sys_set_power_domain;
297 rockchip_pmu_ops.power_domain_is_on = rk3368_pmu_power_domain_is_on;
302 arch_initcall(rk3368_init_rockchip_pmu_ops);