pd: rk3368: add rk3368 power domain support (as pd clk)
[firefly-linux-kernel-4.4.55.git] / drivers / clk / rockchip / pd-rk3368.c
1 /*
2  * Power domain support for Rockchip RK3368
3  *
4  * Copyright (C) 2014-2015 ROCKCHIP, Inc.
5  *
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.
10  *
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.
15  */
16
17 #include <linux/of.h>
18 #include <linux/of_address.h>
19 #include <linux/rockchip/pmu.h>
20 #include <linux/rockchip/cru.h>
21
22 #include "clk-ops.h"
23
24 static void __iomem *rk_pmu_base;
25
26 static u32 pmu_readl(u32 offset)
27 {
28         return readl_relaxed(rk_pmu_base + (offset));
29 }
30
31 static void pmu_writel(u32 val, u32 offset)
32 {
33         writel_relaxed(val, rk_pmu_base + (offset));
34         dsb(sy);
35 }
36
37 static const u8 pmu_pd_map[] = {
38         [PD_PERI] = 13,
39         [PD_VIDEO] = 14,
40         [PD_VIO] = 15,
41         [PD_GPU_0] = 16,
42         [PD_GPU_1] = 17,
43 };
44
45 static const u8 pmu_st_map[] = {
46         [PD_PERI] = 12,
47         [PD_VIDEO] = 13,
48         [PD_VIO] = 14,
49         [PD_GPU_0] = 15,
50         [PD_GPU_1] = 16,
51 };
52
53 static bool rk3368_pmu_power_domain_is_on(enum pmu_power_domain pd)
54 {
55         /* 1'b0: power on, 1'b1: power off */
56         return !(pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[pd]));
57 }
58
59 static DEFINE_SPINLOCK(pmu_idle_lock);
60
61 static const u8 pmu_idle_map[] = {
62         [IDLE_REQ_GPU] = 2,
63         [IDLE_REQ_BUS] = 4,
64         [IDLE_REQ_PERI] = 6,
65         [IDLE_REQ_VIDEO] = 7,
66         [IDLE_REQ_VIO] = 8,
67 };
68
69 static int rk3368_pmu_set_idle_request(enum pmu_idle_req req, bool idle)
70 {
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));
74         u32 mask = BIT(bit);
75         u32 val;
76         unsigned long flags;
77
78         spin_lock_irqsave(&pmu_idle_lock, flags);
79
80         val = pmu_readl(RK3368_PMU_IDLE_REQ);
81         if (idle)
82                 val |=  mask;
83         else
84                 val &= ~mask;
85         pmu_writel(val, RK3368_PMU_IDLE_REQ);
86         dsb(sy);
87
88         while ((pmu_readl(RK3368_PMU_IDLE_ST) & idle_mask) != idle_target)
89                 ;
90
91         spin_unlock_irqrestore(&pmu_idle_lock, flags);
92
93         return 0;
94 }
95
96 static DEFINE_SPINLOCK(pmu_pd_lock);
97
98 static noinline void rk3368_do_pmu_set_power_domain
99                                         (enum pmu_power_domain domain, bool on)
100 {
101         u8 pd = pmu_pd_map[domain];
102         u32 val = pmu_readl(RK3368_PMU_PWRDN_CON);
103
104         if (on)
105                 val &= ~BIT(pd);
106         else
107                 val |=  BIT(pd);
108
109         pmu_writel(val, RK3368_PMU_PWRDN_CON);
110         dsb(sy);
111
112         while ((pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[domain])) == on)
113                 ;
114 }
115
116 static int rk3368_pmu_set_power_domain(enum pmu_power_domain pd, bool on)
117 {
118         unsigned long flags;
119
120         spin_lock_irqsave(&pmu_pd_lock, flags);
121
122         if (rk3368_pmu_power_domain_is_on(pd) == on)
123                 goto out;
124
125         if (!on) {
126                 /* if power down, idle request to NIU first */
127                 if (pd == PD_VIO)
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);
135         }
136
137         rk3368_do_pmu_set_power_domain(pd, on);
138
139         if (on) {
140                 /* if power up, idle request release to NIU */
141                 if (pd == PD_VIO)
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);
149         }
150
151 out:
152         spin_unlock_irqrestore(&pmu_pd_lock, flags);
153         return 0;
154 }
155
156 static int rk3368_sys_set_power_domain(enum pmu_power_domain pd, bool on)
157 {
158         u32 clks_ungating[RK3368_CRU_CLKGATES_CON_CNT];
159         u32 clks_save[RK3368_CRU_CLKGATES_CON_CNT];
160         u32 i, ret;
161
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;
165         }
166
167         for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
168                 cru_writel(0xffff0000, RK3368_CRU_CLKGATES_CON(i));
169
170         ret = rk3368_pmu_set_power_domain(pd, on);
171
172         for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
173                 cru_writel(clks_save[i] | 0xffff0000,
174                            RK3368_CRU_CLKGATES_CON(i));
175
176         return ret;
177 }
178
179 static int __init rk3368_init_rockchip_pmu_ops(void)
180 {
181         struct device_node *node;
182
183         node = of_find_compatible_node(NULL, NULL, "rockchip,rk3368-pmu");
184         if (!node) {
185                 pr_err("%s: could not find pmu dt node\n", __func__);
186                 return -ENXIO;
187         }
188
189         rk_pmu_base = of_iomap(node, 0);
190         if (!rk_pmu_base) {
191                 pr_err("%s: could not map pmu registers\n", __func__);
192                 return -ENXIO;
193         }
194
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;
197
198         return 0;
199 }
200
201 arch_initcall(rk3368_init_rockchip_pmu_ops);