Skip to content

Commit 75fcb5c

Browse files
superna9999khilman
authored andcommitted
soc: amlogic: add Meson GX VPU Domains driver
The Video Processing Unit needs a specific Power Domain powering scheme this driver handles this as a PM Power Domain driver. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Kevin Hilman <khilman@baylibre.com>
1 parent 5e68c0f commit 75fcb5c

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

drivers/soc/amlogic/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ config MESON_GX_SOCINFO
99
Say yes to support decoding of Amlogic Meson GX SoC family
1010
information about the type, package and version.
1111

12+
config MESON_GX_PM_DOMAINS
13+
bool "Amlogic Meson GX Power Domains driver"
14+
depends on ARCH_MESON || COMPILE_TEST
15+
default ARCH_MESON
16+
select PM_GENERIC_DOMAINS
17+
select PM_GENERIC_DOMAINS_OF
18+
help
19+
Say yes to expose Amlogic Meson GX Power Domains as
20+
Generic Power Domains.
21+
1222
config MESON_MX_SOCINFO
1323
bool "Amlogic Meson MX SoC Information driver"
1424
depends on ARCH_MESON || COMPILE_TEST

drivers/soc/amlogic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
2+
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
23
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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/reset.h>
15+
#include <linux/clk.h>
16+
17+
/* AO Offsets */
18+
19+
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
20+
21+
#define GEN_PWR_VPU_HDMI BIT(8)
22+
#define GEN_PWR_VPU_HDMI_ISO BIT(9)
23+
24+
/* HHI Offsets */
25+
26+
#define HHI_MEM_PD_REG0 (0x40 << 2)
27+
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
28+
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
29+
30+
struct meson_gx_pwrc_vpu {
31+
struct generic_pm_domain genpd;
32+
struct regmap *regmap_ao;
33+
struct regmap *regmap_hhi;
34+
struct reset_control *rstc;
35+
struct clk *vpu_clk;
36+
struct clk *vapb_clk;
37+
bool powered;
38+
};
39+
40+
static inline
41+
struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
42+
{
43+
return container_of(d, struct meson_gx_pwrc_vpu, genpd);
44+
}
45+
46+
static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
47+
{
48+
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
49+
int i;
50+
51+
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
52+
GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
53+
udelay(20);
54+
55+
/* Power Down Memories */
56+
for (i = 0; i < 32; i += 2) {
57+
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
58+
0x2 << i, 0x3 << i);
59+
udelay(5);
60+
}
61+
for (i = 0; i < 32; i += 2) {
62+
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
63+
0x2 << i, 0x3 << i);
64+
udelay(5);
65+
}
66+
for (i = 8; i < 16; i++) {
67+
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
68+
BIT(i), BIT(i));
69+
udelay(5);
70+
}
71+
udelay(20);
72+
73+
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
74+
GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
75+
76+
msleep(20);
77+
78+
clk_disable_unprepare(pd->vpu_clk);
79+
clk_disable_unprepare(pd->vapb_clk);
80+
81+
pd->powered = false;
82+
83+
return 0;
84+
}
85+
86+
static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
87+
{
88+
int ret;
89+
90+
ret = clk_prepare_enable(pd->vpu_clk);
91+
if (ret)
92+
return ret;
93+
94+
return clk_prepare_enable(pd->vapb_clk);
95+
}
96+
97+
static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
98+
{
99+
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
100+
int ret;
101+
int i;
102+
103+
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
104+
GEN_PWR_VPU_HDMI, 0);
105+
udelay(20);
106+
107+
/* Power Up Memories */
108+
for (i = 0; i < 32; i += 2) {
109+
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
110+
0x2 << i, 0);
111+
udelay(5);
112+
}
113+
114+
for (i = 0; i < 32; i += 2) {
115+
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
116+
0x2 << i, 0);
117+
udelay(5);
118+
}
119+
120+
for (i = 8; i < 16; i++) {
121+
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
122+
BIT(i), 0);
123+
udelay(5);
124+
}
125+
udelay(20);
126+
127+
ret = reset_control_assert(pd->rstc);
128+
if (ret)
129+
return ret;
130+
131+
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
132+
GEN_PWR_VPU_HDMI_ISO, 0);
133+
134+
ret = reset_control_deassert(pd->rstc);
135+
if (ret)
136+
return ret;
137+
138+
ret = meson_gx_pwrc_vpu_setup_clk(pd);
139+
if (ret)
140+
return ret;
141+
142+
pd->powered = true;
143+
144+
return 0;
145+
}
146+
147+
static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
148+
{
149+
u32 reg;
150+
151+
regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
152+
153+
return (reg & GEN_PWR_VPU_HDMI);
154+
}
155+
156+
static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
157+
.genpd = {
158+
.name = "vpu_hdmi",
159+
.power_off = meson_gx_pwrc_vpu_power_off,
160+
.power_on = meson_gx_pwrc_vpu_power_on,
161+
},
162+
};
163+
164+
static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
165+
{
166+
struct regmap *regmap_ao, *regmap_hhi;
167+
struct reset_control *rstc;
168+
struct clk *vpu_clk;
169+
struct clk *vapb_clk;
170+
171+
regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
172+
if (IS_ERR(regmap_ao)) {
173+
dev_err(&pdev->dev, "failed to get regmap\n");
174+
return PTR_ERR(regmap_ao);
175+
}
176+
177+
regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
178+
"amlogic,hhi-sysctrl");
179+
if (IS_ERR(regmap_hhi)) {
180+
dev_err(&pdev->dev, "failed to get HHI regmap\n");
181+
return PTR_ERR(regmap_hhi);
182+
}
183+
184+
rstc = devm_reset_control_array_get(&pdev->dev, false, false);
185+
if (IS_ERR(rstc)) {
186+
dev_err(&pdev->dev, "failed to get reset lines\n");
187+
return PTR_ERR(rstc);
188+
}
189+
190+
vpu_clk = devm_clk_get(&pdev->dev, "vpu");
191+
if (IS_ERR(vpu_clk)) {
192+
dev_err(&pdev->dev, "vpu clock request failed\n");
193+
return PTR_ERR(vpu_clk);
194+
}
195+
196+
vapb_clk = devm_clk_get(&pdev->dev, "vapb");
197+
if (IS_ERR(vapb_clk)) {
198+
dev_err(&pdev->dev, "vapb clock request failed\n");
199+
return PTR_ERR(vapb_clk);
200+
}
201+
202+
vpu_hdmi_pd.regmap_ao = regmap_ao;
203+
vpu_hdmi_pd.regmap_hhi = regmap_hhi;
204+
vpu_hdmi_pd.rstc = rstc;
205+
vpu_hdmi_pd.vpu_clk = vpu_clk;
206+
vpu_hdmi_pd.vapb_clk = vapb_clk;
207+
208+
pm_genpd_init(&vpu_hdmi_pd.genpd, &simple_qos_governor,
209+
meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd));
210+
211+
return of_genpd_add_provider_simple(pdev->dev.of_node,
212+
&vpu_hdmi_pd.genpd);
213+
}
214+
215+
static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
216+
{
217+
if (vpu_hdmi_pd.powered)
218+
meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
219+
}
220+
221+
static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
222+
{ .compatible = "amlogic,meson-gx-pwrc-vpu" },
223+
{ /* sentinel */ }
224+
};
225+
226+
static struct platform_driver meson_gx_pwrc_vpu_driver = {
227+
.probe = meson_gx_pwrc_vpu_probe,
228+
.shutdown = meson_gx_pwrc_vpu_shutdown,
229+
.driver = {
230+
.name = "meson_gx_pwrc_vpu",
231+
.of_match_table = meson_gx_pwrc_vpu_match_table,
232+
},
233+
};
234+
builtin_platform_driver(meson_gx_pwrc_vpu_driver);

0 commit comments

Comments
 (0)