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/platform_device.h>
9#include <linux/pm_domain.h>
10#include <linux/bitfield.h>
11#include <linux/regmap.h>
12#include <linux/mfd/syscon.h>
13#include <linux/of.h>
14#include <linux/reset.h>
15#include <linux/clk.h>
16#include <linux/module.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
32struct 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
41static inline
42struct 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
47static 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
85static 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
128static 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
143static 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
191static 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
245static 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
254static 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
262static 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
270static 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 device_node *parent_np;
276	struct reset_control *rstc;
277	struct clk *vpu_clk;
278	struct clk *vapb_clk;
279	bool powered_off;
280	int ret;
281
282	vpu_pd_match = of_device_get_match_data(&pdev->dev);
283	if (!vpu_pd_match) {
284		dev_err(&pdev->dev, "failed to get match data\n");
285		return -ENODEV;
286	}
287
288	vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL);
289	if (!vpu_pd)
290		return -ENOMEM;
291
292	memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd));
293
294	parent_np = of_get_parent(pdev->dev.of_node);
295	regmap_ao = syscon_node_to_regmap(parent_np);
296	of_node_put(parent_np);
297	if (IS_ERR(regmap_ao)) {
298		dev_err(&pdev->dev, "failed to get regmap\n");
299		return PTR_ERR(regmap_ao);
300	}
301
302	regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
303						     "amlogic,hhi-sysctrl");
304	if (IS_ERR(regmap_hhi)) {
305		dev_err(&pdev->dev, "failed to get HHI regmap\n");
306		return PTR_ERR(regmap_hhi);
307	}
308
309	rstc = devm_reset_control_array_get_exclusive(&pdev->dev);
310	if (IS_ERR(rstc))
311		return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
312				     "failed to get reset lines\n");
313
314	vpu_clk = devm_clk_get(&pdev->dev, "vpu");
315	if (IS_ERR(vpu_clk)) {
316		dev_err(&pdev->dev, "vpu clock request failed\n");
317		return PTR_ERR(vpu_clk);
318	}
319
320	vapb_clk = devm_clk_get(&pdev->dev, "vapb");
321	if (IS_ERR(vapb_clk)) {
322		dev_err(&pdev->dev, "vapb clock request failed\n");
323		return PTR_ERR(vapb_clk);
324	}
325
326	vpu_pd->regmap_ao = regmap_ao;
327	vpu_pd->regmap_hhi = regmap_hhi;
328	vpu_pd->rstc = rstc;
329	vpu_pd->vpu_clk = vpu_clk;
330	vpu_pd->vapb_clk = vapb_clk;
331
332	platform_set_drvdata(pdev, vpu_pd);
333
334	powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
335
336	/* If already powered, sync the clock states */
337	if (!powered_off) {
338		ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd);
339		if (ret)
340			return ret;
341	}
342
343	vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON;
344	pm_genpd_init(&vpu_pd->genpd, NULL, powered_off);
345
346	return of_genpd_add_provider_simple(pdev->dev.of_node,
347					    &vpu_pd->genpd);
348}
349
350static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
351{
352	struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev);
353	bool powered_off;
354
355	powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
356	if (!powered_off)
357		vpu_pd->genpd.power_off(&vpu_pd->genpd);
358}
359
360static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
361	{ .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd },
362	{
363	  .compatible = "amlogic,meson-g12a-pwrc-vpu",
364	  .data = &vpu_hdmi_pd_g12a
365	},
366	{ /* sentinel */ }
367};
368MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table);
369
370static struct platform_driver meson_gx_pwrc_vpu_driver = {
371	.probe	= meson_gx_pwrc_vpu_probe,
372	.shutdown = meson_gx_pwrc_vpu_shutdown,
373	.driver = {
374		.name		= "meson_gx_pwrc_vpu",
375		.of_match_table	= meson_gx_pwrc_vpu_match_table,
376	},
377};
378module_platform_driver(meson_gx_pwrc_vpu_driver);
379MODULE_LICENSE("GPL v2");
380