xref: /linux/drivers/pmdomain/amlogic/meson-secure-pwrc.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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