blob: 12cdfcdd1f51c4b47711022ec7596a828237d251 [file] [log] [blame]
Neil Armstrong8d5579c2018-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 Glass336d4612020-02-03 07:36:16 -070011#include <malloc.h>
Neil Armstrong8d5579c2018-08-06 14:49:19 +020012#include <power-domain-uclass.h>
13#include <regmap.h>
14#include <syscon.h>
15#include <reset.h>
16#include <clk.h>
Simon Glass61b29b82020-02-03 07:36:15 -070017#include <linux/err.h>
Neil Armstrong8d5579c2018-08-06 14:49:19 +020018
Neil Armstrong3dcdf852019-08-30 14:09:21 +020019enum {
20 VPU_PWRC_COMPATIBLE_GX = 0,
21 VPU_PWRC_COMPATIBLE_G12A = 1,
22};
23
Neil Armstrong8d5579c2018-08-06 14:49:19 +020024/* AO Offsets */
25
26#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
27
28#define GEN_PWR_VPU_HDMI BIT(8)
29#define GEN_PWR_VPU_HDMI_ISO BIT(9)
30
31/* HHI Offsets */
32
33#define HHI_MEM_PD_REG0 (0x40 << 2)
34#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
35#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
Neil Armstrong3dcdf852019-08-30 14:09:21 +020036#define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
Neil Armstrong8d5579c2018-08-06 14:49:19 +020037
38struct meson_gx_pwrc_vpu_priv {
39 struct regmap *regmap_ao;
40 struct regmap *regmap_hhi;
41 struct reset_ctl_bulk resets;
42 struct clk_bulk clks;
43};
44
Neil Armstrong3dcdf852019-08-30 14:09:21 +020045static int meson_pwrc_vpu_request(struct power_domain *power_domain)
Neil Armstrong8d5579c2018-08-06 14:49:19 +020046{
47 return 0;
48}
49
Neil Armstrong3dcdf852019-08-30 14:09:21 +020050static int meson_pwrc_vpu_free(struct power_domain *power_domain)
Neil Armstrong8d5579c2018-08-06 14:49:19 +020051{
52 return 0;
53}
54
55static int meson_gx_pwrc_vpu_on(struct power_domain *power_domain)
56{
57 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
58 int i, ret;
59
60 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
61 GEN_PWR_VPU_HDMI, 0);
62 udelay(20);
63
64 /* Power Up Memories */
65 for (i = 0; i < 32; i += 2) {
66 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
67 0x3 << i, 0);
68 udelay(5);
69 }
70
71 for (i = 0; i < 32; i += 2) {
72 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
73 0x3 << i, 0);
74 udelay(5);
75 }
76
77 for (i = 8; i < 16; i++) {
78 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
79 BIT(i), 0);
80 udelay(5);
81 }
82 udelay(20);
83
84 ret = reset_assert_bulk(&priv->resets);
85 if (ret)
86 return ret;
87
88 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
89 GEN_PWR_VPU_HDMI_ISO, 0);
90
91 ret = reset_deassert_bulk(&priv->resets);
92 if (ret)
93 return ret;
94
95 ret = clk_enable_bulk(&priv->clks);
96 if (ret)
97 return ret;
98
99 return 0;
100}
101
Neil Armstrong3dcdf852019-08-30 14:09:21 +0200102static int meson_g12a_pwrc_vpu_on(struct power_domain *power_domain)
103{
104 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
105 int i, ret;
106
107 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
108 GEN_PWR_VPU_HDMI, 0);
109 udelay(20);
110
111 /* Power Up Memories */
112 for (i = 0; i < 32; i += 2) {
113 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
114 0x3 << i, 0);
115 udelay(5);
116 }
117
118 for (i = 0; i < 32; i += 2) {
119 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
120 0x3 << i, 0);
121 udelay(5);
122 }
123
124 for (i = 0; i < 32; i += 2) {
125 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
126 0x3 << i, 0);
127 udelay(5);
128 }
129
130 for (i = 8; i < 16; i++) {
131 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
132 BIT(i), 0);
133 udelay(5);
134 }
135 udelay(20);
136
137 ret = reset_assert_bulk(&priv->resets);
138 if (ret)
139 return ret;
140
141 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
142 GEN_PWR_VPU_HDMI_ISO, 0);
143
144 ret = reset_deassert_bulk(&priv->resets);
145 if (ret)
146 return ret;
147
148 ret = clk_enable_bulk(&priv->clks);
149 if (ret)
150 return ret;
151
152 return 0;
153}
154
155static int meson_pwrc_vpu_on(struct power_domain *power_domain)
156{
157 unsigned int compat = dev_get_driver_data(power_domain->dev);
158
159 switch (compat) {
160 case VPU_PWRC_COMPATIBLE_GX:
161 return meson_gx_pwrc_vpu_on(power_domain);
162 case VPU_PWRC_COMPATIBLE_G12A:
163 return meson_g12a_pwrc_vpu_on(power_domain);
164 }
165
166 return -EINVAL;
167}
168
Neil Armstrong8d5579c2018-08-06 14:49:19 +0200169static int meson_gx_pwrc_vpu_off(struct power_domain *power_domain)
170{
171 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
172 int i;
173
174 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
175 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
176 udelay(20);
177
178 /* Power Down Memories */
179 for (i = 0; i < 32; i += 2) {
180 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
181 0x3 << i, 0x3 << i);
182 udelay(5);
183 }
184 for (i = 0; i < 32; i += 2) {
185 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
186 0x3 << i, 0x3 << i);
187 udelay(5);
188 }
189 for (i = 8; i < 16; i++) {
190 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
191 BIT(i), BIT(i));
192 udelay(5);
193 }
194 udelay(20);
195
196 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
197 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
198 mdelay(20);
199
200 clk_disable_bulk(&priv->clks);
201
202 return 0;
203}
204
Neil Armstrong3dcdf852019-08-30 14:09:21 +0200205static int meson_g12a_pwrc_vpu_off(struct power_domain *power_domain)
206{
207 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
208 int i;
209
210 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
211 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
212 udelay(20);
213
214 /* Power Down Memories */
215 for (i = 0; i < 32; i += 2) {
216 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
217 0x3 << i, 0x3 << i);
218 udelay(5);
219 }
220 for (i = 0; i < 32; i += 2) {
221 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
222 0x3 << i, 0x3 << i);
223 udelay(5);
224 }
225 for (i = 0; i < 32; i += 2) {
226 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
227 0x3 << i, 0x3 << i);
228 udelay(5);
229 }
230 for (i = 8; i < 16; i++) {
231 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
232 BIT(i), BIT(i));
233 udelay(5);
234 }
235 udelay(20);
236
237 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
238 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
239 mdelay(20);
240
241 clk_disable_bulk(&priv->clks);
242
243 return 0;
244}
245
246static int meson_pwrc_vpu_off(struct power_domain *power_domain)
247{
248 unsigned int compat = dev_get_driver_data(power_domain->dev);
249
250 switch (compat) {
251 case VPU_PWRC_COMPATIBLE_GX:
252 return meson_gx_pwrc_vpu_off(power_domain);
253 case VPU_PWRC_COMPATIBLE_G12A:
254 return meson_g12a_pwrc_vpu_off(power_domain);
255 }
256
257 return -EINVAL;
258}
259
260static int meson_pwrc_vpu_of_xlate(struct power_domain *power_domain,
261 struct ofnode_phandle_args *args)
Neil Armstrong8d5579c2018-08-06 14:49:19 +0200262{
263 /* #power-domain-cells is 0 */
264
265 if (args->args_count != 0) {
266 debug("Invalid args_count: %d\n", args->args_count);
267 return -EINVAL;
268 }
269
270 return 0;
271}
272
273struct power_domain_ops meson_gx_pwrc_vpu_ops = {
Simon Glass4f511882020-02-03 07:35:51 -0700274 .rfree = meson_pwrc_vpu_free,
Neil Armstrong3dcdf852019-08-30 14:09:21 +0200275 .off = meson_pwrc_vpu_off,
276 .on = meson_pwrc_vpu_on,
277 .request = meson_pwrc_vpu_request,
278 .of_xlate = meson_pwrc_vpu_of_xlate,
Neil Armstrong8d5579c2018-08-06 14:49:19 +0200279};
280
281static const struct udevice_id meson_gx_pwrc_vpu_ids[] = {
Neil Armstrong3dcdf852019-08-30 14:09:21 +0200282 {
283 .compatible = "amlogic,meson-gx-pwrc-vpu",
284 .data = VPU_PWRC_COMPATIBLE_GX,
285 },
286 {
287 .compatible = "amlogic,meson-g12a-pwrc-vpu",
288 .data = VPU_PWRC_COMPATIBLE_G12A,
289 },
Neil Armstrong8d5579c2018-08-06 14:49:19 +0200290 { }
291};
292
293static int meson_gx_pwrc_vpu_probe(struct udevice *dev)
294{
295 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev);
296 u32 hhi_phandle;
297 ofnode hhi_node;
298 int ret;
299
300 priv->regmap_ao = syscon_node_to_regmap(dev_get_parent(dev)->node);
301 if (IS_ERR(priv->regmap_ao))
302 return PTR_ERR(priv->regmap_ao);
303
304 ret = ofnode_read_u32(dev->node, "amlogic,hhi-sysctrl",
305 &hhi_phandle);
306 if (ret)
307 return ret;
308
309 hhi_node = ofnode_get_by_phandle(hhi_phandle);
310 if (!ofnode_valid(hhi_node))
311 return -EINVAL;
312
313 priv->regmap_hhi = syscon_node_to_regmap(hhi_node);
314 if (IS_ERR(priv->regmap_hhi))
315 return PTR_ERR(priv->regmap_hhi);
316
317 ret = reset_get_bulk(dev, &priv->resets);
318 if (ret)
319 return ret;
320
321 ret = clk_get_bulk(dev, &priv->clks);
322 if (ret)
323 return ret;
324
325 return 0;
326}
327
328U_BOOT_DRIVER(meson_gx_pwrc_vpu) = {
329 .name = "meson_gx_pwrc_vpu",
330 .id = UCLASS_POWER_DOMAIN,
331 .of_match = meson_gx_pwrc_vpu_ids,
332 .probe = meson_gx_pwrc_vpu_probe,
333 .ops = &meson_gx_pwrc_vpu_ops,
334 .priv_auto_alloc_size = sizeof(struct meson_gx_pwrc_vpu_priv),
335};