1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2019 BayLibre, SAS
4*4882a593Smuzhiyun * Author: Neil Armstrong <narmstrong@baylibre.com>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/of_address.h>
8*4882a593Smuzhiyun #include <linux/platform_device.h>
9*4882a593Smuzhiyun #include <linux/pm_domain.h>
10*4882a593Smuzhiyun #include <linux/bitfield.h>
11*4882a593Smuzhiyun #include <linux/regmap.h>
12*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
13*4882a593Smuzhiyun #include <linux/of_device.h>
14*4882a593Smuzhiyun #include <linux/reset-controller.h>
15*4882a593Smuzhiyun #include <linux/reset.h>
16*4882a593Smuzhiyun #include <linux/clk.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <dt-bindings/power/meson8-power.h>
19*4882a593Smuzhiyun #include <dt-bindings/power/meson-axg-power.h>
20*4882a593Smuzhiyun #include <dt-bindings/power/meson-g12a-power.h>
21*4882a593Smuzhiyun #include <dt-bindings/power/meson-gxbb-power.h>
22*4882a593Smuzhiyun #include <dt-bindings/power/meson-sm1-power.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /* AO Offsets */
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define GX_AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
27*4882a593Smuzhiyun #define GX_AO_RTI_GEN_PWR_ISO0 (0x3b << 2)
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /*
30*4882a593Smuzhiyun * Meson8/Meson8b/Meson8m2 only expose the power management registers of the
31*4882a593Smuzhiyun * AO-bus as syscon. 0x3a from GX translates to 0x02, 0x3b translates to 0x03
32*4882a593Smuzhiyun * and so on.
33*4882a593Smuzhiyun */
34*4882a593Smuzhiyun #define MESON8_AO_RTI_GEN_PWR_SLEEP0 (0x02 << 2)
35*4882a593Smuzhiyun #define MESON8_AO_RTI_GEN_PWR_ISO0 (0x03 << 2)
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* HHI Offsets */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define HHI_MEM_PD_REG0 (0x40 << 2)
40*4882a593Smuzhiyun #define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
41*4882a593Smuzhiyun #define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
42*4882a593Smuzhiyun #define HHI_VPU_MEM_PD_REG3 (0x43 << 2)
43*4882a593Smuzhiyun #define HHI_VPU_MEM_PD_REG4 (0x44 << 2)
44*4882a593Smuzhiyun #define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2)
45*4882a593Smuzhiyun #define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2)
46*4882a593Smuzhiyun #define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2)
47*4882a593Smuzhiyun #define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun struct meson_ee_pwrc;
50*4882a593Smuzhiyun struct meson_ee_pwrc_domain;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun struct meson_ee_pwrc_mem_domain {
53*4882a593Smuzhiyun unsigned int reg;
54*4882a593Smuzhiyun unsigned int mask;
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun struct meson_ee_pwrc_top_domain {
58*4882a593Smuzhiyun unsigned int sleep_reg;
59*4882a593Smuzhiyun unsigned int sleep_mask;
60*4882a593Smuzhiyun unsigned int iso_reg;
61*4882a593Smuzhiyun unsigned int iso_mask;
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun struct meson_ee_pwrc_domain_desc {
65*4882a593Smuzhiyun char *name;
66*4882a593Smuzhiyun unsigned int reset_names_count;
67*4882a593Smuzhiyun unsigned int clk_names_count;
68*4882a593Smuzhiyun struct meson_ee_pwrc_top_domain *top_pd;
69*4882a593Smuzhiyun unsigned int mem_pd_count;
70*4882a593Smuzhiyun struct meson_ee_pwrc_mem_domain *mem_pd;
71*4882a593Smuzhiyun bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain);
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun struct meson_ee_pwrc_domain_data {
75*4882a593Smuzhiyun unsigned int count;
76*4882a593Smuzhiyun struct meson_ee_pwrc_domain_desc *domains;
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun /* TOP Power Domains */
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain gx_pwrc_vpu = {
82*4882a593Smuzhiyun .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0,
83*4882a593Smuzhiyun .sleep_mask = BIT(8),
84*4882a593Smuzhiyun .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0,
85*4882a593Smuzhiyun .iso_mask = BIT(9),
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = {
89*4882a593Smuzhiyun .sleep_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0,
90*4882a593Smuzhiyun .sleep_mask = BIT(8),
91*4882a593Smuzhiyun .iso_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0,
92*4882a593Smuzhiyun .iso_mask = BIT(9),
93*4882a593Smuzhiyun };
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun #define SM1_EE_PD(__bit) \
96*4882a593Smuzhiyun { \
97*4882a593Smuzhiyun .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, \
98*4882a593Smuzhiyun .sleep_mask = BIT(__bit), \
99*4882a593Smuzhiyun .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, \
100*4882a593Smuzhiyun .iso_mask = BIT(__bit), \
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8);
104*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16);
105*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17);
106*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18);
107*4882a593Smuzhiyun static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /* Memory PD Domains */
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun #define VPU_MEMPD(__reg) \
112*4882a593Smuzhiyun { __reg, GENMASK(1, 0) }, \
113*4882a593Smuzhiyun { __reg, GENMASK(3, 2) }, \
114*4882a593Smuzhiyun { __reg, GENMASK(5, 4) }, \
115*4882a593Smuzhiyun { __reg, GENMASK(7, 6) }, \
116*4882a593Smuzhiyun { __reg, GENMASK(9, 8) }, \
117*4882a593Smuzhiyun { __reg, GENMASK(11, 10) }, \
118*4882a593Smuzhiyun { __reg, GENMASK(13, 12) }, \
119*4882a593Smuzhiyun { __reg, GENMASK(15, 14) }, \
120*4882a593Smuzhiyun { __reg, GENMASK(17, 16) }, \
121*4882a593Smuzhiyun { __reg, GENMASK(19, 18) }, \
122*4882a593Smuzhiyun { __reg, GENMASK(21, 20) }, \
123*4882a593Smuzhiyun { __reg, GENMASK(23, 22) }, \
124*4882a593Smuzhiyun { __reg, GENMASK(25, 24) }, \
125*4882a593Smuzhiyun { __reg, GENMASK(27, 26) }, \
126*4882a593Smuzhiyun { __reg, GENMASK(29, 28) }, \
127*4882a593Smuzhiyun { __reg, GENMASK(31, 30) }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun #define VPU_HHI_MEMPD(__reg) \
130*4882a593Smuzhiyun { __reg, BIT(8) }, \
131*4882a593Smuzhiyun { __reg, BIT(9) }, \
132*4882a593Smuzhiyun { __reg, BIT(10) }, \
133*4882a593Smuzhiyun { __reg, BIT(11) }, \
134*4882a593Smuzhiyun { __reg, BIT(12) }, \
135*4882a593Smuzhiyun { __reg, BIT(13) }, \
136*4882a593Smuzhiyun { __reg, BIT(14) }, \
137*4882a593Smuzhiyun { __reg, BIT(15) }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_vpu[] = {
140*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
141*4882a593Smuzhiyun VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = {
145*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
146*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
147*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
148*4882a593Smuzhiyun VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = {
152*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
153*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
154*4882a593Smuzhiyun VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = {
158*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(3, 2) },
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = {
162*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(1, 0) },
163*4882a593Smuzhiyun };
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = {
166*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
167*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
168*4882a593Smuzhiyun VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
169*4882a593Smuzhiyun };
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = {
172*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
173*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
174*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
175*4882a593Smuzhiyun VPU_MEMPD(HHI_VPU_MEM_PD_REG3),
176*4882a593Smuzhiyun { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) },
177*4882a593Smuzhiyun { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) },
178*4882a593Smuzhiyun { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) },
179*4882a593Smuzhiyun { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) },
180*4882a593Smuzhiyun VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = {
184*4882a593Smuzhiyun { HHI_NANOQ_MEM_PD_REG0, 0xff },
185*4882a593Smuzhiyun { HHI_NANOQ_MEM_PD_REG1, 0xff },
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = {
189*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(31, 30) },
190*4882a593Smuzhiyun };
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = {
193*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(29, 26) },
194*4882a593Smuzhiyun };
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = {
197*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(25, 18) },
198*4882a593Smuzhiyun };
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_audio[] = {
201*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(5, 4) },
202*4882a593Smuzhiyun };
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = {
205*4882a593Smuzhiyun { HHI_MEM_PD_REG0, GENMASK(5, 4) },
206*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) },
207*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) },
208*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) },
209*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) },
210*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) },
211*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) },
212*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) },
213*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) },
214*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) },
215*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) },
216*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) },
217*4882a593Smuzhiyun { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) },
218*4882a593Smuzhiyun };
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun #define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \
221*4882a593Smuzhiyun { \
222*4882a593Smuzhiyun .name = __name, \
223*4882a593Smuzhiyun .reset_names_count = __resets, \
224*4882a593Smuzhiyun .clk_names_count = __clks, \
225*4882a593Smuzhiyun .top_pd = __top_pd, \
226*4882a593Smuzhiyun .mem_pd_count = ARRAY_SIZE(__mem), \
227*4882a593Smuzhiyun .mem_pd = __mem, \
228*4882a593Smuzhiyun .get_power = __get_power, \
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun #define TOP_PD(__name, __top_pd, __mem, __get_power) \
232*4882a593Smuzhiyun { \
233*4882a593Smuzhiyun .name = __name, \
234*4882a593Smuzhiyun .top_pd = __top_pd, \
235*4882a593Smuzhiyun .mem_pd_count = ARRAY_SIZE(__mem), \
236*4882a593Smuzhiyun .mem_pd = __mem, \
237*4882a593Smuzhiyun .get_power = __get_power, \
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun #define MEM_PD(__name, __mem) \
241*4882a593Smuzhiyun TOP_PD(__name, NULL, __mem, NULL)
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain);
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc axg_pwrc_domains[] = {
246*4882a593Smuzhiyun [PWRC_AXG_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, axg_pwrc_mem_vpu,
247*4882a593Smuzhiyun pwrc_ee_get_power, 5, 2),
248*4882a593Smuzhiyun [PWRC_AXG_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth),
249*4882a593Smuzhiyun [PWRC_AXG_AUDIO_ID] = MEM_PD("AUDIO", axg_pwrc_mem_audio),
250*4882a593Smuzhiyun };
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = {
253*4882a593Smuzhiyun [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, g12a_pwrc_mem_vpu,
254*4882a593Smuzhiyun pwrc_ee_get_power, 11, 2),
255*4882a593Smuzhiyun [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth),
256*4882a593Smuzhiyun };
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = {
259*4882a593Smuzhiyun [PWRC_GXBB_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, gxbb_pwrc_mem_vpu,
260*4882a593Smuzhiyun pwrc_ee_get_power, 12, 2),
261*4882a593Smuzhiyun [PWRC_GXBB_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth),
262*4882a593Smuzhiyun };
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = {
265*4882a593Smuzhiyun [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu,
266*4882a593Smuzhiyun meson8_pwrc_mem_vpu, pwrc_ee_get_power,
267*4882a593Smuzhiyun 0, 1),
268*4882a593Smuzhiyun [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM",
269*4882a593Smuzhiyun meson_pwrc_mem_eth),
270*4882a593Smuzhiyun [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM",
271*4882a593Smuzhiyun meson8_pwrc_audio_dsp_mem),
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = {
275*4882a593Smuzhiyun [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu,
276*4882a593Smuzhiyun meson8_pwrc_mem_vpu, pwrc_ee_get_power,
277*4882a593Smuzhiyun 11, 1),
278*4882a593Smuzhiyun [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM",
279*4882a593Smuzhiyun meson_pwrc_mem_eth),
280*4882a593Smuzhiyun [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM",
281*4882a593Smuzhiyun meson8_pwrc_audio_dsp_mem),
282*4882a593Smuzhiyun };
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = {
285*4882a593Smuzhiyun [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu,
286*4882a593Smuzhiyun pwrc_ee_get_power, 11, 2),
287*4882a593Smuzhiyun [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna,
288*4882a593Smuzhiyun pwrc_ee_get_power),
289*4882a593Smuzhiyun [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb,
290*4882a593Smuzhiyun pwrc_ee_get_power),
291*4882a593Smuzhiyun [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie,
292*4882a593Smuzhiyun pwrc_ee_get_power),
293*4882a593Smuzhiyun [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d,
294*4882a593Smuzhiyun pwrc_ee_get_power),
295*4882a593Smuzhiyun [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio),
296*4882a593Smuzhiyun [PWRC_SM1_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth),
297*4882a593Smuzhiyun };
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun struct meson_ee_pwrc_domain {
300*4882a593Smuzhiyun struct generic_pm_domain base;
301*4882a593Smuzhiyun bool enabled;
302*4882a593Smuzhiyun struct meson_ee_pwrc *pwrc;
303*4882a593Smuzhiyun struct meson_ee_pwrc_domain_desc desc;
304*4882a593Smuzhiyun struct clk_bulk_data *clks;
305*4882a593Smuzhiyun int num_clks;
306*4882a593Smuzhiyun struct reset_control *rstc;
307*4882a593Smuzhiyun int num_rstc;
308*4882a593Smuzhiyun };
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun struct meson_ee_pwrc {
311*4882a593Smuzhiyun struct regmap *regmap_ao;
312*4882a593Smuzhiyun struct regmap *regmap_hhi;
313*4882a593Smuzhiyun struct meson_ee_pwrc_domain *domains;
314*4882a593Smuzhiyun struct genpd_onecell_data xlate;
315*4882a593Smuzhiyun };
316*4882a593Smuzhiyun
pwrc_ee_get_power(struct meson_ee_pwrc_domain * pwrc_domain)317*4882a593Smuzhiyun static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun u32 reg;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun regmap_read(pwrc_domain->pwrc->regmap_ao,
322*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_reg, ®);
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun return (reg & pwrc_domain->desc.top_pd->sleep_mask);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
meson_ee_pwrc_off(struct generic_pm_domain * domain)327*4882a593Smuzhiyun static int meson_ee_pwrc_off(struct generic_pm_domain *domain)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun struct meson_ee_pwrc_domain *pwrc_domain =
330*4882a593Smuzhiyun container_of(domain, struct meson_ee_pwrc_domain, base);
331*4882a593Smuzhiyun int i;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun if (pwrc_domain->desc.top_pd)
334*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
335*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_reg,
336*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_mask,
337*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_mask);
338*4882a593Smuzhiyun udelay(20);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
341*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
342*4882a593Smuzhiyun pwrc_domain->desc.mem_pd[i].reg,
343*4882a593Smuzhiyun pwrc_domain->desc.mem_pd[i].mask,
344*4882a593Smuzhiyun pwrc_domain->desc.mem_pd[i].mask);
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun udelay(20);
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun if (pwrc_domain->desc.top_pd)
349*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
350*4882a593Smuzhiyun pwrc_domain->desc.top_pd->iso_reg,
351*4882a593Smuzhiyun pwrc_domain->desc.top_pd->iso_mask,
352*4882a593Smuzhiyun pwrc_domain->desc.top_pd->iso_mask);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun if (pwrc_domain->num_clks) {
355*4882a593Smuzhiyun msleep(20);
356*4882a593Smuzhiyun clk_bulk_disable_unprepare(pwrc_domain->num_clks,
357*4882a593Smuzhiyun pwrc_domain->clks);
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun return 0;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun
meson_ee_pwrc_on(struct generic_pm_domain * domain)363*4882a593Smuzhiyun static int meson_ee_pwrc_on(struct generic_pm_domain *domain)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun struct meson_ee_pwrc_domain *pwrc_domain =
366*4882a593Smuzhiyun container_of(domain, struct meson_ee_pwrc_domain, base);
367*4882a593Smuzhiyun int i, ret;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun if (pwrc_domain->desc.top_pd)
370*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
371*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_reg,
372*4882a593Smuzhiyun pwrc_domain->desc.top_pd->sleep_mask, 0);
373*4882a593Smuzhiyun udelay(20);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
376*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
377*4882a593Smuzhiyun pwrc_domain->desc.mem_pd[i].reg,
378*4882a593Smuzhiyun pwrc_domain->desc.mem_pd[i].mask, 0);
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun udelay(20);
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun ret = reset_control_assert(pwrc_domain->rstc);
383*4882a593Smuzhiyun if (ret)
384*4882a593Smuzhiyun return ret;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun if (pwrc_domain->desc.top_pd)
387*4882a593Smuzhiyun regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
388*4882a593Smuzhiyun pwrc_domain->desc.top_pd->iso_reg,
389*4882a593Smuzhiyun pwrc_domain->desc.top_pd->iso_mask, 0);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun ret = reset_control_deassert(pwrc_domain->rstc);
392*4882a593Smuzhiyun if (ret)
393*4882a593Smuzhiyun return ret;
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun return clk_bulk_prepare_enable(pwrc_domain->num_clks,
396*4882a593Smuzhiyun pwrc_domain->clks);
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun
meson_ee_pwrc_init_domain(struct platform_device * pdev,struct meson_ee_pwrc * pwrc,struct meson_ee_pwrc_domain * dom)399*4882a593Smuzhiyun static int meson_ee_pwrc_init_domain(struct platform_device *pdev,
400*4882a593Smuzhiyun struct meson_ee_pwrc *pwrc,
401*4882a593Smuzhiyun struct meson_ee_pwrc_domain *dom)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun int ret;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun dom->pwrc = pwrc;
406*4882a593Smuzhiyun dom->num_rstc = dom->desc.reset_names_count;
407*4882a593Smuzhiyun dom->num_clks = dom->desc.clk_names_count;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun if (dom->num_rstc) {
410*4882a593Smuzhiyun int count = reset_control_get_count(&pdev->dev);
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun if (count != dom->num_rstc)
413*4882a593Smuzhiyun dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n",
414*4882a593Smuzhiyun count, dom->desc.name);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun dom->rstc = devm_reset_control_array_get(&pdev->dev, false,
417*4882a593Smuzhiyun false);
418*4882a593Smuzhiyun if (IS_ERR(dom->rstc))
419*4882a593Smuzhiyun return PTR_ERR(dom->rstc);
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun if (dom->num_clks) {
423*4882a593Smuzhiyun int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks);
424*4882a593Smuzhiyun if (ret < 0)
425*4882a593Smuzhiyun return ret;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun if (dom->num_clks != ret) {
428*4882a593Smuzhiyun dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n",
429*4882a593Smuzhiyun ret, dom->desc.name);
430*4882a593Smuzhiyun dom->num_clks = ret;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun dom->base.name = dom->desc.name;
435*4882a593Smuzhiyun dom->base.power_on = meson_ee_pwrc_on;
436*4882a593Smuzhiyun dom->base.power_off = meson_ee_pwrc_off;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /*
439*4882a593Smuzhiyun * TOFIX: This is a special case for the VPU power domain, which can
440*4882a593Smuzhiyun * be enabled previously by the bootloader. In this case the VPU
441*4882a593Smuzhiyun * pipeline may be functional but no driver maybe never attach
442*4882a593Smuzhiyun * to this power domain, and if the domain is disabled it could
443*4882a593Smuzhiyun * cause system errors. This is why the pm_domain_always_on_gov
444*4882a593Smuzhiyun * is used here.
445*4882a593Smuzhiyun * For the same reason, the clocks should be enabled in case
446*4882a593Smuzhiyun * we need to power the domain off, otherwise the internal clocks
447*4882a593Smuzhiyun * prepare/enable counters won't be in sync.
448*4882a593Smuzhiyun */
449*4882a593Smuzhiyun if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) {
450*4882a593Smuzhiyun ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks);
451*4882a593Smuzhiyun if (ret)
452*4882a593Smuzhiyun return ret;
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun dom->base.flags = GENPD_FLAG_ALWAYS_ON;
455*4882a593Smuzhiyun ret = pm_genpd_init(&dom->base, NULL, false);
456*4882a593Smuzhiyun if (ret)
457*4882a593Smuzhiyun return ret;
458*4882a593Smuzhiyun } else {
459*4882a593Smuzhiyun ret = pm_genpd_init(&dom->base, NULL,
460*4882a593Smuzhiyun (dom->desc.get_power ?
461*4882a593Smuzhiyun dom->desc.get_power(dom) : true));
462*4882a593Smuzhiyun if (ret)
463*4882a593Smuzhiyun return ret;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun return 0;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun
meson_ee_pwrc_probe(struct platform_device * pdev)469*4882a593Smuzhiyun static int meson_ee_pwrc_probe(struct platform_device *pdev)
470*4882a593Smuzhiyun {
471*4882a593Smuzhiyun const struct meson_ee_pwrc_domain_data *match;
472*4882a593Smuzhiyun struct regmap *regmap_ao, *regmap_hhi;
473*4882a593Smuzhiyun struct meson_ee_pwrc *pwrc;
474*4882a593Smuzhiyun int i, ret;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun match = of_device_get_match_data(&pdev->dev);
477*4882a593Smuzhiyun if (!match) {
478*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to get match data\n");
479*4882a593Smuzhiyun return -ENODEV;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
483*4882a593Smuzhiyun if (!pwrc)
484*4882a593Smuzhiyun return -ENOMEM;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
487*4882a593Smuzhiyun sizeof(*pwrc->xlate.domains),
488*4882a593Smuzhiyun GFP_KERNEL);
489*4882a593Smuzhiyun if (!pwrc->xlate.domains)
490*4882a593Smuzhiyun return -ENOMEM;
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
493*4882a593Smuzhiyun sizeof(*pwrc->domains), GFP_KERNEL);
494*4882a593Smuzhiyun if (!pwrc->domains)
495*4882a593Smuzhiyun return -ENOMEM;
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun pwrc->xlate.num_domains = match->count;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
500*4882a593Smuzhiyun if (IS_ERR(regmap_hhi)) {
501*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to get HHI regmap\n");
502*4882a593Smuzhiyun return PTR_ERR(regmap_hhi);
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
506*4882a593Smuzhiyun "amlogic,ao-sysctrl");
507*4882a593Smuzhiyun if (IS_ERR(regmap_ao)) {
508*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to get AO regmap\n");
509*4882a593Smuzhiyun return PTR_ERR(regmap_ao);
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun pwrc->regmap_ao = regmap_ao;
513*4882a593Smuzhiyun pwrc->regmap_hhi = regmap_hhi;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun platform_set_drvdata(pdev, pwrc);
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun for (i = 0 ; i < match->count ; ++i) {
518*4882a593Smuzhiyun struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc));
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom);
523*4882a593Smuzhiyun if (ret)
524*4882a593Smuzhiyun return ret;
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun pwrc->xlate.domains[i] = &dom->base;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
meson_ee_pwrc_shutdown(struct platform_device * pdev)532*4882a593Smuzhiyun static void meson_ee_pwrc_shutdown(struct platform_device *pdev)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev);
535*4882a593Smuzhiyun int i;
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) {
538*4882a593Smuzhiyun struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun if (dom->desc.get_power && !dom->desc.get_power(dom))
541*4882a593Smuzhiyun meson_ee_pwrc_off(&dom->base);
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = {
546*4882a593Smuzhiyun .count = ARRAY_SIZE(g12a_pwrc_domains),
547*4882a593Smuzhiyun .domains = g12a_pwrc_domains,
548*4882a593Smuzhiyun };
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_axg_pwrc_data = {
551*4882a593Smuzhiyun .count = ARRAY_SIZE(axg_pwrc_domains),
552*4882a593Smuzhiyun .domains = axg_pwrc_domains,
553*4882a593Smuzhiyun };
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = {
556*4882a593Smuzhiyun .count = ARRAY_SIZE(gxbb_pwrc_domains),
557*4882a593Smuzhiyun .domains = gxbb_pwrc_domains,
558*4882a593Smuzhiyun };
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = {
561*4882a593Smuzhiyun .count = ARRAY_SIZE(meson8_pwrc_domains),
562*4882a593Smuzhiyun .domains = meson8_pwrc_domains,
563*4882a593Smuzhiyun };
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = {
566*4882a593Smuzhiyun .count = ARRAY_SIZE(meson8b_pwrc_domains),
567*4882a593Smuzhiyun .domains = meson8b_pwrc_domains,
568*4882a593Smuzhiyun };
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = {
571*4882a593Smuzhiyun .count = ARRAY_SIZE(sm1_pwrc_domains),
572*4882a593Smuzhiyun .domains = sm1_pwrc_domains,
573*4882a593Smuzhiyun };
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun static const struct of_device_id meson_ee_pwrc_match_table[] = {
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun .compatible = "amlogic,meson8-pwrc",
578*4882a593Smuzhiyun .data = &meson_ee_m8_pwrc_data,
579*4882a593Smuzhiyun },
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun .compatible = "amlogic,meson8b-pwrc",
582*4882a593Smuzhiyun .data = &meson_ee_m8b_pwrc_data,
583*4882a593Smuzhiyun },
584*4882a593Smuzhiyun {
585*4882a593Smuzhiyun .compatible = "amlogic,meson8m2-pwrc",
586*4882a593Smuzhiyun .data = &meson_ee_m8b_pwrc_data,
587*4882a593Smuzhiyun },
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun .compatible = "amlogic,meson-axg-pwrc",
590*4882a593Smuzhiyun .data = &meson_ee_axg_pwrc_data,
591*4882a593Smuzhiyun },
592*4882a593Smuzhiyun {
593*4882a593Smuzhiyun .compatible = "amlogic,meson-gxbb-pwrc",
594*4882a593Smuzhiyun .data = &meson_ee_gxbb_pwrc_data,
595*4882a593Smuzhiyun },
596*4882a593Smuzhiyun {
597*4882a593Smuzhiyun .compatible = "amlogic,meson-g12a-pwrc",
598*4882a593Smuzhiyun .data = &meson_ee_g12a_pwrc_data,
599*4882a593Smuzhiyun },
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun .compatible = "amlogic,meson-sm1-pwrc",
602*4882a593Smuzhiyun .data = &meson_ee_sm1_pwrc_data,
603*4882a593Smuzhiyun },
604*4882a593Smuzhiyun { /* sentinel */ }
605*4882a593Smuzhiyun };
606*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, meson_ee_pwrc_match_table);
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun static struct platform_driver meson_ee_pwrc_driver = {
609*4882a593Smuzhiyun .probe = meson_ee_pwrc_probe,
610*4882a593Smuzhiyun .shutdown = meson_ee_pwrc_shutdown,
611*4882a593Smuzhiyun .driver = {
612*4882a593Smuzhiyun .name = "meson_ee_pwrc",
613*4882a593Smuzhiyun .of_match_table = meson_ee_pwrc_match_table,
614*4882a593Smuzhiyun },
615*4882a593Smuzhiyun };
616*4882a593Smuzhiyun module_platform_driver(meson_ee_pwrc_driver);
617*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
618