blob: a41b230448fca23af2b198050c0fb1d26cda29dd [file] [log] [blame]
Neil Armstrong32edad52018-08-06 14:49:19 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Amlogic Meson VPU Power Domain Controller driver
4 *
5 * Copyright (c) 2018 BayLibre, SAS.
6 * Author: Neil Armstrong <narmstrong@baylibre.com>
7 */
8
9#include <common.h>
10#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060011#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070012#include <malloc.h>
Neil Armstrong32edad52018-08-06 14:49:19 +020013#include <power-domain-uclass.h>
14#include <regmap.h>
15#include <syscon.h>
16#include <reset.h>
17#include <clk.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060018#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060019#include <linux/delay.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070020#include <linux/err.h>
Neil Armstrong32edad52018-08-06 14:49:19 +020021
Neil Armstrong108f0af2019-08-30 14:09:21 +020022enum {
23 VPU_PWRC_COMPATIBLE_GX = 0,
24 VPU_PWRC_COMPATIBLE_G12A = 1,
25};
26
Neil Armstrong32edad52018-08-06 14:49:19 +020027/* AO Offsets */
28
29#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
30
31#define GEN_PWR_VPU_HDMI BIT(8)
32#define GEN_PWR_VPU_HDMI_ISO BIT(9)
33
34/* HHI Offsets */
35
36#define HHI_MEM_PD_REG0 (0x40 << 2)
37#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
38#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
Neil Armstrong108f0af2019-08-30 14:09:21 +020039#define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
Neil Armstrong32edad52018-08-06 14:49:19 +020040
41struct meson_gx_pwrc_vpu_priv {
42 struct regmap *regmap_ao;
43 struct regmap *regmap_hhi;
44 struct reset_ctl_bulk resets;
45 struct clk_bulk clks;
46};
47
Neil Armstrong108f0af2019-08-30 14:09:21 +020048static int meson_pwrc_vpu_request(struct power_domain *power_domain)
Neil Armstrong32edad52018-08-06 14:49:19 +020049{
50 return 0;
51}
52
Neil Armstrong108f0af2019-08-30 14:09:21 +020053static int meson_pwrc_vpu_free(struct power_domain *power_domain)
Neil Armstrong32edad52018-08-06 14:49:19 +020054{
55 return 0;
56}
57
58static int meson_gx_pwrc_vpu_on(struct power_domain *power_domain)
59{
60 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
61 int i, ret;
62
63 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
64 GEN_PWR_VPU_HDMI, 0);
65 udelay(20);
66
67 /* Power Up Memories */
68 for (i = 0; i < 32; i += 2) {
69 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
70 0x3 << i, 0);
71 udelay(5);
72 }
73
74 for (i = 0; i < 32; i += 2) {
75 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
76 0x3 << i, 0);
77 udelay(5);
78 }
79
Neil Armstrong108f0af2019-08-30 14:09:21 +020080 for (i = 8; i < 16; i++) {
81 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
82 BIT(i), 0);
83 udelay(5);
84 }
85 udelay(20);
86
87 ret = reset_assert_bulk(&priv->resets);
88 if (ret)
89 return ret;
90
91 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
92 GEN_PWR_VPU_HDMI_ISO, 0);
93
94 ret = reset_deassert_bulk(&priv->resets);
95 if (ret)
96 return ret;
97
98 ret = clk_enable_bulk(&priv->clks);
99 if (ret)
100 return ret;
101
102 return 0;
103}
104
105static int meson_g12a_pwrc_vpu_on(struct power_domain *power_domain)
106{
107 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
108 int i, ret;
109
110 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
111 GEN_PWR_VPU_HDMI, 0);
112 udelay(20);
113
114 /* Power Up Memories */
115 for (i = 0; i < 32; i += 2) {
116 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
117 0x3 << i, 0);
118 udelay(5);
119 }
120
121 for (i = 0; i < 32; i += 2) {
122 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
123 0x3 << i, 0);
124 udelay(5);
125 }
126
127 for (i = 0; i < 32; i += 2) {
128 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
129 0x3 << i, 0);
130 udelay(5);
131 }
132
Neil Armstrong32edad52018-08-06 14:49:19 +0200133 for (i = 8; i < 16; i++) {
134 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
135 BIT(i), 0);
136 udelay(5);
137 }
138 udelay(20);
139
140 ret = reset_assert_bulk(&priv->resets);
141 if (ret)
142 return ret;
143
144 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
145 GEN_PWR_VPU_HDMI_ISO, 0);
146
147 ret = reset_deassert_bulk(&priv->resets);
148 if (ret)
149 return ret;
150
151 ret = clk_enable_bulk(&priv->clks);
152 if (ret)
153 return ret;
154
155 return 0;
156}
157
Neil Armstrong108f0af2019-08-30 14:09:21 +0200158static int meson_pwrc_vpu_on(struct power_domain *power_domain)
159{
160 unsigned int compat = dev_get_driver_data(power_domain->dev);
161
162 switch (compat) {
163 case VPU_PWRC_COMPATIBLE_GX:
164 return meson_gx_pwrc_vpu_on(power_domain);
165 case VPU_PWRC_COMPATIBLE_G12A:
166 return meson_g12a_pwrc_vpu_on(power_domain);
167 }
168
169 return -EINVAL;
170}
171
Neil Armstrong32edad52018-08-06 14:49:19 +0200172static int meson_gx_pwrc_vpu_off(struct power_domain *power_domain)
173{
174 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
175 int i;
176
177 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
178 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
179 udelay(20);
180
181 /* Power Down Memories */
182 for (i = 0; i < 32; i += 2) {
183 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
184 0x3 << i, 0x3 << i);
185 udelay(5);
186 }
187 for (i = 0; i < 32; i += 2) {
188 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
189 0x3 << i, 0x3 << i);
190 udelay(5);
191 }
Neil Armstrong108f0af2019-08-30 14:09:21 +0200192 for (i = 8; i < 16; i++) {
193 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
194 BIT(i), BIT(i));
195 udelay(5);
196 }
197 udelay(20);
198
199 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
200 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
201 mdelay(20);
202
203 clk_disable_bulk(&priv->clks);
204
205 return 0;
206}
207
208static int meson_g12a_pwrc_vpu_off(struct power_domain *power_domain)
209{
210 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
211 int i;
212
213 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
214 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
215 udelay(20);
216
217 /* Power Down Memories */
218 for (i = 0; i < 32; i += 2) {
219 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
220 0x3 << i, 0x3 << i);
221 udelay(5);
222 }
223 for (i = 0; i < 32; i += 2) {
224 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
225 0x3 << i, 0x3 << i);
226 udelay(5);
227 }
228 for (i = 0; i < 32; i += 2) {
229 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
230 0x3 << i, 0x3 << i);
231 udelay(5);
232 }
Neil Armstrong32edad52018-08-06 14:49:19 +0200233 for (i = 8; i < 16; i++) {
234 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
235 BIT(i), BIT(i));
236 udelay(5);
237 }
238 udelay(20);
239
240 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
241 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
242 mdelay(20);
243
244 clk_disable_bulk(&priv->clks);
245
246 return 0;
247}
248
Neil Armstrong108f0af2019-08-30 14:09:21 +0200249static int meson_pwrc_vpu_off(struct power_domain *power_domain)
250{
251 unsigned int compat = dev_get_driver_data(power_domain->dev);
252
253 switch (compat) {
254 case VPU_PWRC_COMPATIBLE_GX:
255 return meson_gx_pwrc_vpu_off(power_domain);
256 case VPU_PWRC_COMPATIBLE_G12A:
257 return meson_g12a_pwrc_vpu_off(power_domain);
258 }
259
260 return -EINVAL;
261}
262
263static int meson_pwrc_vpu_of_xlate(struct power_domain *power_domain,
264 struct ofnode_phandle_args *args)
Neil Armstrong32edad52018-08-06 14:49:19 +0200265{
266 /* #power-domain-cells is 0 */
267
268 if (args->args_count != 0) {
269 debug("Invalid args_count: %d\n", args->args_count);
270 return -EINVAL;
271 }
272
273 return 0;
274}
275
276struct power_domain_ops meson_gx_pwrc_vpu_ops = {
Simon Glass92ed3a32020-02-03 07:35:51 -0700277 .rfree = meson_pwrc_vpu_free,
Neil Armstrong108f0af2019-08-30 14:09:21 +0200278 .off = meson_pwrc_vpu_off,
279 .on = meson_pwrc_vpu_on,
280 .request = meson_pwrc_vpu_request,
281 .of_xlate = meson_pwrc_vpu_of_xlate,
Neil Armstrong32edad52018-08-06 14:49:19 +0200282};
283
284static const struct udevice_id meson_gx_pwrc_vpu_ids[] = {
Neil Armstrong108f0af2019-08-30 14:09:21 +0200285 {
286 .compatible = "amlogic,meson-gx-pwrc-vpu",
287 .data = VPU_PWRC_COMPATIBLE_GX,
288 },
289 {
290 .compatible = "amlogic,meson-g12a-pwrc-vpu",
291 .data = VPU_PWRC_COMPATIBLE_G12A,
292 },
Neil Armstrong32edad52018-08-06 14:49:19 +0200293 { }
294};
295
296static int meson_gx_pwrc_vpu_probe(struct udevice *dev)
297{
298 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev);
299 u32 hhi_phandle;
300 ofnode hhi_node;
301 int ret;
302
303 priv->regmap_ao = syscon_node_to_regmap(dev_get_parent(dev)->node);
304 if (IS_ERR(priv->regmap_ao))
305 return PTR_ERR(priv->regmap_ao);
306
307 ret = ofnode_read_u32(dev->node, "amlogic,hhi-sysctrl",
308 &hhi_phandle);
309 if (ret)
310 return ret;
311
312 hhi_node = ofnode_get_by_phandle(hhi_phandle);
313 if (!ofnode_valid(hhi_node))
314 return -EINVAL;
315
316 priv->regmap_hhi = syscon_node_to_regmap(hhi_node);
317 if (IS_ERR(priv->regmap_hhi))
318 return PTR_ERR(priv->regmap_hhi);
319
320 ret = reset_get_bulk(dev, &priv->resets);
321 if (ret)
322 return ret;
323
324 ret = clk_get_bulk(dev, &priv->clks);
325 if (ret)
326 return ret;
327
328 return 0;
329}
330
331U_BOOT_DRIVER(meson_gx_pwrc_vpu) = {
332 .name = "meson_gx_pwrc_vpu",
333 .id = UCLASS_POWER_DOMAIN,
334 .of_match = meson_gx_pwrc_vpu_ids,
335 .probe = meson_gx_pwrc_vpu_probe,
336 .ops = &meson_gx_pwrc_vpu_ops,
337 .priv_auto_alloc_size = sizeof(struct meson_gx_pwrc_vpu_priv),
338};