PCI: tegra: Continue unconfig sequence even if parts fail
[linux-2.6-microblaze.git] / drivers / soc / amlogic / meson-gx-pwrc-vpu.c
1 /*
2  * Copyright (c) 2017 BayLibre, SAS
3  * Author: Neil Armstrong <narmstrong@baylibre.com>
4  *
5  * SPDX-License-Identifier: GPL-2.0+
6  */
7
8 #include <linux/of_address.h>
9 #include <linux/platform_device.h>
10 #include <linux/pm_domain.h>
11 #include <linux/bitfield.h>
12 #include <linux/regmap.h>
13 #include <linux/mfd/syscon.h>
14 #include <linux/of_device.h>
15 #include <linux/reset.h>
16 #include <linux/clk.h>
17
18 /* AO Offsets */
19
20 #define AO_RTI_GEN_PWR_SLEEP0           (0x3a << 2)
21
22 #define GEN_PWR_VPU_HDMI                BIT(8)
23 #define GEN_PWR_VPU_HDMI_ISO            BIT(9)
24
25 /* HHI Offsets */
26
27 #define HHI_MEM_PD_REG0                 (0x40 << 2)
28 #define HHI_VPU_MEM_PD_REG0             (0x41 << 2)
29 #define HHI_VPU_MEM_PD_REG1             (0x42 << 2)
30 #define HHI_VPU_MEM_PD_REG2             (0x4d << 2)
31
32 struct meson_gx_pwrc_vpu {
33         struct generic_pm_domain genpd;
34         struct regmap *regmap_ao;
35         struct regmap *regmap_hhi;
36         struct reset_control *rstc;
37         struct clk *vpu_clk;
38         struct clk *vapb_clk;
39 };
40
41 static inline
42 struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
43 {
44         return container_of(d, struct meson_gx_pwrc_vpu, genpd);
45 }
46
47 static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
48 {
49         struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
50         int i;
51
52         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
53                            GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
54         udelay(20);
55
56         /* Power Down Memories */
57         for (i = 0; i < 32; i += 2) {
58                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
59                                    0x3 << i, 0x3 << i);
60                 udelay(5);
61         }
62         for (i = 0; i < 32; i += 2) {
63                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
64                                    0x3 << i, 0x3 << i);
65                 udelay(5);
66         }
67         for (i = 8; i < 16; i++) {
68                 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
69                                    BIT(i), BIT(i));
70                 udelay(5);
71         }
72         udelay(20);
73
74         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
75                            GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
76
77         msleep(20);
78
79         clk_disable_unprepare(pd->vpu_clk);
80         clk_disable_unprepare(pd->vapb_clk);
81
82         return 0;
83 }
84
85 static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
86 {
87         struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
88         int i;
89
90         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
91                            GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
92         udelay(20);
93
94         /* Power Down Memories */
95         for (i = 0; i < 32; i += 2) {
96                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
97                                    0x3 << i, 0x3 << i);
98                 udelay(5);
99         }
100         for (i = 0; i < 32; i += 2) {
101                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
102                                    0x3 << i, 0x3 << i);
103                 udelay(5);
104         }
105         for (i = 0; i < 32; i += 2) {
106                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2,
107                                    0x3 << i, 0x3 << i);
108                 udelay(5);
109         }
110         for (i = 8; i < 16; i++) {
111                 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
112                                    BIT(i), BIT(i));
113                 udelay(5);
114         }
115         udelay(20);
116
117         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
118                            GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
119
120         msleep(20);
121
122         clk_disable_unprepare(pd->vpu_clk);
123         clk_disable_unprepare(pd->vapb_clk);
124
125         return 0;
126 }
127
128 static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
129 {
130         int ret;
131
132         ret = clk_prepare_enable(pd->vpu_clk);
133         if (ret)
134                 return ret;
135
136         ret = clk_prepare_enable(pd->vapb_clk);
137         if (ret)
138                 clk_disable_unprepare(pd->vpu_clk);
139
140         return ret;
141 }
142
143 static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
144 {
145         struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
146         int ret;
147         int i;
148
149         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
150                            GEN_PWR_VPU_HDMI, 0);
151         udelay(20);
152
153         /* Power Up Memories */
154         for (i = 0; i < 32; i += 2) {
155                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
156                                    0x3 << i, 0);
157                 udelay(5);
158         }
159
160         for (i = 0; i < 32; i += 2) {
161                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
162                                    0x3 << i, 0);
163                 udelay(5);
164         }
165
166         for (i = 8; i < 16; i++) {
167                 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
168                                    BIT(i), 0);
169                 udelay(5);
170         }
171         udelay(20);
172
173         ret = reset_control_assert(pd->rstc);
174         if (ret)
175                 return ret;
176
177         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
178                            GEN_PWR_VPU_HDMI_ISO, 0);
179
180         ret = reset_control_deassert(pd->rstc);
181         if (ret)
182                 return ret;
183
184         ret = meson_gx_pwrc_vpu_setup_clk(pd);
185         if (ret)
186                 return ret;
187
188         return 0;
189 }
190
191 static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
192 {
193         struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
194         int ret;
195         int i;
196
197         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
198                            GEN_PWR_VPU_HDMI, 0);
199         udelay(20);
200
201         /* Power Up Memories */
202         for (i = 0; i < 32; i += 2) {
203                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
204                                    0x3 << i, 0);
205                 udelay(5);
206         }
207
208         for (i = 0; i < 32; i += 2) {
209                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
210                                    0x3 << i, 0);
211                 udelay(5);
212         }
213
214         for (i = 0; i < 32; i += 2) {
215                 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2,
216                                    0x3 << i, 0);
217                 udelay(5);
218         }
219
220         for (i = 8; i < 16; i++) {
221                 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
222                                    BIT(i), 0);
223                 udelay(5);
224         }
225         udelay(20);
226
227         ret = reset_control_assert(pd->rstc);
228         if (ret)
229                 return ret;
230
231         regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
232                            GEN_PWR_VPU_HDMI_ISO, 0);
233
234         ret = reset_control_deassert(pd->rstc);
235         if (ret)
236                 return ret;
237
238         ret = meson_gx_pwrc_vpu_setup_clk(pd);
239         if (ret)
240                 return ret;
241
242         return 0;
243 }
244
245 static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
246 {
247         u32 reg;
248
249         regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
250
251         return (reg & GEN_PWR_VPU_HDMI);
252 }
253
254 static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
255         .genpd = {
256                 .name = "vpu_hdmi",
257                 .power_off = meson_gx_pwrc_vpu_power_off,
258                 .power_on = meson_gx_pwrc_vpu_power_on,
259         },
260 };
261
262 static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = {
263         .genpd = {
264                 .name = "vpu_hdmi",
265                 .power_off = meson_g12a_pwrc_vpu_power_off,
266                 .power_on = meson_g12a_pwrc_vpu_power_on,
267         },
268 };
269
270 static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
271 {
272         const struct meson_gx_pwrc_vpu *vpu_pd_match;
273         struct regmap *regmap_ao, *regmap_hhi;
274         struct meson_gx_pwrc_vpu *vpu_pd;
275         struct reset_control *rstc;
276         struct clk *vpu_clk;
277         struct clk *vapb_clk;
278         bool powered_off;
279         int ret;
280
281         vpu_pd_match = of_device_get_match_data(&pdev->dev);
282         if (!vpu_pd_match) {
283                 dev_err(&pdev->dev, "failed to get match data\n");
284                 return -ENODEV;
285         }
286
287         vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL);
288         if (!vpu_pd)
289                 return -ENOMEM;
290
291         memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd));
292
293         regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
294         if (IS_ERR(regmap_ao)) {
295                 dev_err(&pdev->dev, "failed to get regmap\n");
296                 return PTR_ERR(regmap_ao);
297         }
298
299         regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
300                                                      "amlogic,hhi-sysctrl");
301         if (IS_ERR(regmap_hhi)) {
302                 dev_err(&pdev->dev, "failed to get HHI regmap\n");
303                 return PTR_ERR(regmap_hhi);
304         }
305
306         rstc = devm_reset_control_array_get(&pdev->dev, false, false);
307         if (IS_ERR(rstc)) {
308                 if (PTR_ERR(rstc) != -EPROBE_DEFER)
309                         dev_err(&pdev->dev, "failed to get reset lines\n");
310                 return PTR_ERR(rstc);
311         }
312
313         vpu_clk = devm_clk_get(&pdev->dev, "vpu");
314         if (IS_ERR(vpu_clk)) {
315                 dev_err(&pdev->dev, "vpu clock request failed\n");
316                 return PTR_ERR(vpu_clk);
317         }
318
319         vapb_clk = devm_clk_get(&pdev->dev, "vapb");
320         if (IS_ERR(vapb_clk)) {
321                 dev_err(&pdev->dev, "vapb clock request failed\n");
322                 return PTR_ERR(vapb_clk);
323         }
324
325         vpu_pd->regmap_ao = regmap_ao;
326         vpu_pd->regmap_hhi = regmap_hhi;
327         vpu_pd->rstc = rstc;
328         vpu_pd->vpu_clk = vpu_clk;
329         vpu_pd->vapb_clk = vapb_clk;
330
331         platform_set_drvdata(pdev, vpu_pd);
332
333         powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
334
335         /* If already powered, sync the clock states */
336         if (!powered_off) {
337                 ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd);
338                 if (ret)
339                         return ret;
340         }
341
342         vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON;
343         pm_genpd_init(&vpu_pd->genpd, NULL, powered_off);
344
345         return of_genpd_add_provider_simple(pdev->dev.of_node,
346                                             &vpu_pd->genpd);
347 }
348
349 static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
350 {
351         struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev);
352         bool powered_off;
353
354         powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
355         if (!powered_off)
356                 vpu_pd->genpd.power_off(&vpu_pd->genpd);
357 }
358
359 static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
360         { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd },
361         {
362           .compatible = "amlogic,meson-g12a-pwrc-vpu",
363           .data = &vpu_hdmi_pd_g12a
364         },
365         { /* sentinel */ }
366 };
367
368 static struct platform_driver meson_gx_pwrc_vpu_driver = {
369         .probe  = meson_gx_pwrc_vpu_probe,
370         .shutdown = meson_gx_pwrc_vpu_shutdown,
371         .driver = {
372                 .name           = "meson_gx_pwrc_vpu",
373                 .of_match_table = meson_gx_pwrc_vpu_match_table,
374         },
375 };
376 builtin_platform_driver(meson_gx_pwrc_vpu_driver);