Linux Audio

Check our new training course

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