1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3 * Copyright (c) 2019 Amlogic, Inc.
4 * Author: Jianxin Pan <jianxin.pan@amlogic.com>
5 */
6
7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
9 #include <linux/io.h>
10 #include <linux/of.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_domain.h>
13 #include <dt-bindings/power/meson-a1-power.h>
14 #include <dt-bindings/power/amlogic,c3-pwrc.h>
15 #include <dt-bindings/power/meson-s4-power.h>
16 #include <dt-bindings/power/amlogic,t7-pwrc.h>
17 #include <dt-bindings/power/amlogic,a4-pwrc.h>
18 #include <dt-bindings/power/amlogic,a5-pwrc.h>
19 #include <linux/arm-smccc.h>
20 #include <linux/firmware/meson/meson_sm.h>
21 #include <linux/module.h>
22
23 #define PWRC_ON 1
24 #define PWRC_OFF 0
25 #define PWRC_NO_PARENT UINT_MAX
26
27 struct meson_secure_pwrc_domain {
28 struct generic_pm_domain base;
29 unsigned int index;
30 unsigned int parent;
31 struct meson_secure_pwrc *pwrc;
32 };
33
34 struct meson_secure_pwrc {
35 struct meson_secure_pwrc_domain *domains;
36 struct genpd_onecell_data xlate;
37 struct meson_sm_firmware *fw;
38 };
39
40 struct meson_secure_pwrc_domain_desc {
41 unsigned int index;
42 unsigned int parent;
43 unsigned int flags;
44 char *name;
45 bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain);
46 };
47
48 struct meson_secure_pwrc_domain_data {
49 unsigned int count;
50 const struct meson_secure_pwrc_domain_desc *domains;
51 };
52
pwrc_secure_is_off(struct meson_secure_pwrc_domain * pwrc_domain)53 static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain)
54 {
55 int is_off = 1;
56
57 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off,
58 pwrc_domain->index, 0, 0, 0, 0) < 0)
59 pr_err("failed to get power domain status\n");
60
61 return is_off;
62 }
63
meson_secure_pwrc_off(struct generic_pm_domain * domain)64 static int meson_secure_pwrc_off(struct generic_pm_domain *domain)
65 {
66 int ret = 0;
67 struct meson_secure_pwrc_domain *pwrc_domain =
68 container_of(domain, struct meson_secure_pwrc_domain, base);
69
70 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
71 pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) {
72 pr_err("failed to set power domain off\n");
73 ret = -EINVAL;
74 }
75
76 return ret;
77 }
78
meson_secure_pwrc_on(struct generic_pm_domain * domain)79 static int meson_secure_pwrc_on(struct generic_pm_domain *domain)
80 {
81 int ret = 0;
82 struct meson_secure_pwrc_domain *pwrc_domain =
83 container_of(domain, struct meson_secure_pwrc_domain, base);
84
85 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
86 pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) {
87 pr_err("failed to set power domain on\n");
88 ret = -EINVAL;
89 }
90
91 return ret;
92 }
93
94 #define SEC_PD(__name, __flag) \
95 [PWRC_##__name##_ID] = \
96 { \
97 .name = #__name, \
98 .index = PWRC_##__name##_ID, \
99 .is_off = pwrc_secure_is_off, \
100 .flags = __flag, \
101 .parent = PWRC_NO_PARENT, \
102 }
103
104 #define TOP_PD(__name, __flag, __parent) \
105 [PWRC_##__name##_ID] = \
106 { \
107 .name = #__name, \
108 .index = PWRC_##__name##_ID, \
109 .is_off = pwrc_secure_is_off, \
110 .flags = __flag, \
111 .parent = __parent, \
112 }
113
114 static const struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
115 SEC_PD(DSPA, 0),
116 SEC_PD(DSPB, 0),
117 /* UART should keep working in ATF after suspend and before resume */
118 SEC_PD(UART, GENPD_FLAG_ALWAYS_ON),
119 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */
120 SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON),
121 SEC_PD(I2C, 0),
122 SEC_PD(PSRAM, 0),
123 SEC_PD(ACODEC, 0),
124 SEC_PD(AUDIO, 0),
125 SEC_PD(OTP, 0),
126 SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE),
127 SEC_PD(SD_EMMC, 0),
128 SEC_PD(RAMA, 0),
129 /* SRAMB is used as ATF runtime memory, and should be always on */
130 SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON),
131 SEC_PD(IR, 0),
132 SEC_PD(SPICC, 0),
133 SEC_PD(SPIFC, 0),
134 SEC_PD(USB, 0),
135 /* NIC is for the Arm NIC-400 interconnect, and should be always on */
136 SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON),
137 SEC_PD(PDMIN, 0),
138 SEC_PD(RSA, 0),
139 };
140
141 static const struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = {
142 SEC_PD(A4_AUDIO, 0),
143 SEC_PD(A4_SDIOA, 0),
144 SEC_PD(A4_EMMC, 0),
145 SEC_PD(A4_USB_COMB, 0),
146 SEC_PD(A4_ETH, 0),
147 SEC_PD(A4_VOUT, 0),
148 SEC_PD(A4_AUDIO_PDM, 0),
149 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */
150 SEC_PD(A4_DMC, GENPD_FLAG_ALWAYS_ON),
151 /* WRAP is secure_top, a lot of modules are included, and should be always on */
152 SEC_PD(A4_SYS_WRAP, GENPD_FLAG_ALWAYS_ON),
153 SEC_PD(A4_AO_I2C_S, 0),
154 SEC_PD(A4_AO_UART, 0),
155 /* IR is wake up trigger source, and should be always on */
156 SEC_PD(A4_AO_IR, GENPD_FLAG_ALWAYS_ON),
157 };
158
159 static const struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = {
160 SEC_PD(A5_NNA, 0),
161 SEC_PD(A5_AUDIO, 0),
162 SEC_PD(A5_SDIOA, 0),
163 SEC_PD(A5_EMMC, 0),
164 SEC_PD(A5_USB_COMB, 0),
165 SEC_PD(A5_ETH, 0),
166 SEC_PD(A5_RSA, 0),
167 SEC_PD(A5_AUDIO_PDM, 0),
168 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */
169 SEC_PD(A5_DMC, GENPD_FLAG_ALWAYS_ON),
170 /* WRAP is secure_top, a lot of modules are included, and should be always on */
171 SEC_PD(A5_SYS_WRAP, GENPD_FLAG_ALWAYS_ON),
172 SEC_PD(A5_DSPA, 0),
173 };
174
175 static const struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = {
176 SEC_PD(C3_NNA, 0),
177 SEC_PD(C3_AUDIO, 0),
178 SEC_PD(C3_SDIOA, 0),
179 SEC_PD(C3_EMMC, 0),
180 SEC_PD(C3_USB_COMB, 0),
181 SEC_PD(C3_SDCARD, 0),
182 /* ETH is for ethernet online wakeup, and should be always on */
183 SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON),
184 SEC_PD(C3_GE2D, 0),
185 SEC_PD(C3_CVE, 0),
186 SEC_PD(C3_GDC_WRAP, 0),
187 SEC_PD(C3_ISP_TOP, 0),
188 SEC_PD(C3_MIPI_ISP_WRAP, 0),
189 SEC_PD(C3_VCODEC, 0),
190 };
191
192 static const struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = {
193 SEC_PD(S4_DOS_HEVC, 0),
194 SEC_PD(S4_DOS_VDEC, 0),
195 SEC_PD(S4_VPU_HDMI, 0),
196 SEC_PD(S4_USB_COMB, 0),
197 SEC_PD(S4_GE2D, 0),
198 /* ETH is for ethernet online wakeup, and should be always on */
199 SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON),
200 SEC_PD(S4_DEMOD, 0),
201 SEC_PD(S4_AUDIO, 0),
202 };
203
204 static const struct meson_secure_pwrc_domain_desc t7_pwrc_domains[] = {
205 SEC_PD(T7_DSPA, 0),
206 SEC_PD(T7_DSPB, 0),
207 TOP_PD(T7_DOS_HCODEC, 0, PWRC_T7_NIC3_ID),
208 TOP_PD(T7_DOS_HEVC, 0, PWRC_T7_NIC3_ID),
209 TOP_PD(T7_DOS_VDEC, 0, PWRC_T7_NIC3_ID),
210 TOP_PD(T7_DOS_WAVE, 0, PWRC_T7_NIC3_ID),
211 SEC_PD(T7_VPU_HDMI, 0),
212 SEC_PD(T7_USB_COMB, 0),
213 SEC_PD(T7_PCIE, 0),
214 TOP_PD(T7_GE2D, 0, PWRC_T7_NIC3_ID),
215 /* SRAMA is used as ATF runtime memory, and should be always on */
216 SEC_PD(T7_SRAMA, GENPD_FLAG_ALWAYS_ON),
217 /* SRAMB is used as ATF runtime memory, and should be always on */
218 SEC_PD(T7_SRAMB, GENPD_FLAG_ALWAYS_ON),
219 SEC_PD(T7_HDMIRX, 0),
220 SEC_PD(T7_VI_CLK1, 0),
221 SEC_PD(T7_VI_CLK2, 0),
222 /* ETH is for ethernet online wakeup, and should be always on */
223 SEC_PD(T7_ETH, GENPD_FLAG_ALWAYS_ON),
224 SEC_PD(T7_ISP, 0),
225 SEC_PD(T7_MIPI_ISP, 0),
226 TOP_PD(T7_GDC, 0, PWRC_T7_NIC3_ID),
227 TOP_PD(T7_DEWARP, 0, PWRC_T7_NIC3_ID),
228 SEC_PD(T7_SDIO_A, 0),
229 SEC_PD(T7_SDIO_B, 0),
230 SEC_PD(T7_EMMC, 0),
231 TOP_PD(T7_MALI_SC0, 0, PWRC_T7_NNA_TOP_ID),
232 TOP_PD(T7_MALI_SC1, 0, PWRC_T7_NNA_TOP_ID),
233 TOP_PD(T7_MALI_SC2, 0, PWRC_T7_NNA_TOP_ID),
234 TOP_PD(T7_MALI_SC3, 0, PWRC_T7_NNA_TOP_ID),
235 SEC_PD(T7_MALI_TOP, 0),
236 TOP_PD(T7_NNA_CORE0, 0, PWRC_T7_NNA_TOP_ID),
237 TOP_PD(T7_NNA_CORE1, 0, PWRC_T7_NNA_TOP_ID),
238 TOP_PD(T7_NNA_CORE2, 0, PWRC_T7_NNA_TOP_ID),
239 TOP_PD(T7_NNA_CORE3, 0, PWRC_T7_NNA_TOP_ID),
240 SEC_PD(T7_NNA_TOP, 0),
241 SEC_PD(T7_DDR0, GENPD_FLAG_ALWAYS_ON),
242 SEC_PD(T7_DDR1, GENPD_FLAG_ALWAYS_ON),
243 /* DMC0 is for DDR PHY ana/dig and DMC, and should be always on */
244 SEC_PD(T7_DMC0, GENPD_FLAG_ALWAYS_ON),
245 /* DMC1 is for DDR PHY ana/dig and DMC, and should be always on */
246 SEC_PD(T7_DMC1, GENPD_FLAG_ALWAYS_ON),
247 /* NOC is related to clk bus, and should be always on */
248 SEC_PD(T7_NOC, GENPD_FLAG_ALWAYS_ON),
249 /* NIC is for the Arm NIC-400 interconnect, and should be always on */
250 SEC_PD(T7_NIC2, GENPD_FLAG_ALWAYS_ON),
251 SEC_PD(T7_NIC3, 0),
252 /* CPU accesses the interleave data to the ddr need cci, and should be always on */
253 SEC_PD(T7_CCI, GENPD_FLAG_ALWAYS_ON),
254 SEC_PD(T7_MIPI_DSI0, 0),
255 SEC_PD(T7_SPICC0, 0),
256 SEC_PD(T7_SPICC1, 0),
257 SEC_PD(T7_SPICC2, 0),
258 SEC_PD(T7_SPICC3, 0),
259 SEC_PD(T7_SPICC4, 0),
260 SEC_PD(T7_SPICC5, 0),
261 SEC_PD(T7_EDP0, 0),
262 SEC_PD(T7_EDP1, 0),
263 SEC_PD(T7_MIPI_DSI1, 0),
264 SEC_PD(T7_AUDIO, 0),
265 };
266
meson_secure_pwrc_probe(struct platform_device * pdev)267 static int meson_secure_pwrc_probe(struct platform_device *pdev)
268 {
269 int i;
270 struct device_node *sm_np;
271 struct meson_secure_pwrc *pwrc;
272 const struct meson_secure_pwrc_domain_data *match;
273
274 match = of_device_get_match_data(&pdev->dev);
275 if (!match) {
276 dev_err(&pdev->dev, "failed to get match data\n");
277 return -ENODEV;
278 }
279
280 sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm");
281 if (!sm_np) {
282 dev_err(&pdev->dev, "no secure-monitor node\n");
283 return -ENODEV;
284 }
285
286 pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
287 if (!pwrc) {
288 of_node_put(sm_np);
289 return -ENOMEM;
290 }
291
292 pwrc->fw = meson_sm_get(sm_np);
293 of_node_put(sm_np);
294 if (!pwrc->fw)
295 return -EPROBE_DEFER;
296
297 pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
298 sizeof(*pwrc->xlate.domains),
299 GFP_KERNEL);
300 if (!pwrc->xlate.domains)
301 return -ENOMEM;
302
303 pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
304 sizeof(*pwrc->domains), GFP_KERNEL);
305 if (!pwrc->domains)
306 return -ENOMEM;
307
308 pwrc->xlate.num_domains = match->count;
309 platform_set_drvdata(pdev, pwrc);
310
311 for (i = 0 ; i < match->count ; ++i) {
312 struct meson_secure_pwrc_domain *dom = &pwrc->domains[i];
313
314 if (!match->domains[i].name)
315 continue;
316
317 dom->pwrc = pwrc;
318 dom->index = match->domains[i].index;
319 dom->parent = match->domains[i].parent;
320 dom->base.name = match->domains[i].name;
321 dom->base.flags = match->domains[i].flags;
322 dom->base.power_on = meson_secure_pwrc_on;
323 dom->base.power_off = meson_secure_pwrc_off;
324
325 if (match->domains[i].is_off(dom) && (dom->base.flags & GENPD_FLAG_ALWAYS_ON))
326 meson_secure_pwrc_on(&dom->base);
327
328 pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom));
329
330 pwrc->xlate.domains[i] = &dom->base;
331 }
332
333 for (i = 0; i < match->count; i++) {
334 struct meson_secure_pwrc_domain *dom = pwrc->domains;
335
336 if (!match->domains[i].name || match->domains[i].parent == PWRC_NO_PARENT)
337 continue;
338
339 pm_genpd_add_subdomain(&dom[dom[i].parent].base, &dom[i].base);
340 }
341
342 return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
343 }
344
345 static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
346 .domains = a1_pwrc_domains,
347 .count = ARRAY_SIZE(a1_pwrc_domains),
348 };
349
350 static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = {
351 .domains = a4_pwrc_domains,
352 .count = ARRAY_SIZE(a4_pwrc_domains),
353 };
354
355 static struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = {
356 .domains = a5_pwrc_domains,
357 .count = ARRAY_SIZE(a5_pwrc_domains),
358 };
359
360 static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = {
361 .domains = c3_pwrc_domains,
362 .count = ARRAY_SIZE(c3_pwrc_domains),
363 };
364
365 static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {
366 .domains = s4_pwrc_domains,
367 .count = ARRAY_SIZE(s4_pwrc_domains),
368 };
369
370 static struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = {
371 .domains = t7_pwrc_domains,
372 .count = ARRAY_SIZE(t7_pwrc_domains),
373 };
374
375 static const struct of_device_id meson_secure_pwrc_match_table[] = {
376 {
377 .compatible = "amlogic,meson-a1-pwrc",
378 .data = &meson_secure_a1_pwrc_data,
379 },
380 {
381 .compatible = "amlogic,a4-pwrc",
382 .data = &amlogic_secure_a4_pwrc_data,
383 },
384 {
385 .compatible = "amlogic,a5-pwrc",
386 .data = &amlogic_secure_a5_pwrc_data,
387 },
388 {
389 .compatible = "amlogic,c3-pwrc",
390 .data = &amlogic_secure_c3_pwrc_data,
391 },
392 {
393 .compatible = "amlogic,meson-s4-pwrc",
394 .data = &meson_secure_s4_pwrc_data,
395 },
396 {
397 .compatible = "amlogic,t7-pwrc",
398 .data = &amlogic_secure_t7_pwrc_data,
399 },
400 { /* sentinel */ }
401 };
402 MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table);
403
404 static struct platform_driver meson_secure_pwrc_driver = {
405 .probe = meson_secure_pwrc_probe,
406 .driver = {
407 .name = "meson_secure_pwrc",
408 .of_match_table = meson_secure_pwrc_match_table,
409 },
410 };
411 module_platform_driver(meson_secure_pwrc_driver);
412 MODULE_DESCRIPTION("Amlogic Meson Secure Power Domains driver");
413 MODULE_LICENSE("Dual MIT/GPL");
414