Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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);