1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
3 * Copyright (c) 2019 Amlogic, Inc.
4 * Author: Jianxin Pan <jianxin.pan@amlogic.com>
7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
25 #define PWRC_NO_PARENT UINT_MAX
27 struct meson_secure_pwrc_domain
{
28 struct generic_pm_domain base
;
31 struct meson_secure_pwrc
*pwrc
;
34 struct meson_secure_pwrc
{
35 struct meson_secure_pwrc_domain
*domains
;
36 struct genpd_onecell_data xlate
;
37 struct meson_sm_firmware
*fw
;
40 struct meson_secure_pwrc_domain_desc
{
45 bool (*is_off
)(struct meson_secure_pwrc_domain
*pwrc_domain
);
48 struct meson_secure_pwrc_domain_data
{
50 const struct meson_secure_pwrc_domain_desc
*domains
;
53 static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain
*pwrc_domain
)
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");
64 static int meson_secure_pwrc_off(struct generic_pm_domain
*domain
)
67 struct meson_secure_pwrc_domain
*pwrc_domain
=
68 container_of(domain
, struct meson_secure_pwrc_domain
, base
);
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");
79 static int meson_secure_pwrc_on(struct generic_pm_domain
*domain
)
82 struct meson_secure_pwrc_domain
*pwrc_domain
=
83 container_of(domain
, struct meson_secure_pwrc_domain
, base
);
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");
94 #define SEC_PD(__name, __flag) \
95 [PWRC_##__name##_ID] = \
98 .index = PWRC_##__name##_ID, \
99 .is_off = pwrc_secure_is_off, \
101 .parent = PWRC_NO_PARENT, \
104 #define TOP_PD(__name, __flag, __parent) \
105 [PWRC_##__name##_ID] = \
108 .index = PWRC_##__name##_ID, \
109 .is_off = pwrc_secure_is_off, \
111 .parent = __parent, \
114 static const struct meson_secure_pwrc_domain_desc a1_pwrc_domains
[] = {
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
),
126 SEC_PD(DMA
, GENPD_FLAG_ALWAYS_ON
| GENPD_FLAG_IRQ_SAFE
),
129 /* SRAMB is used as ATF runtime memory, and should be always on */
130 SEC_PD(RAMB
, GENPD_FLAG_ALWAYS_ON
),
135 /* NIC is for the Arm NIC-400 interconnect, and should be always on */
136 SEC_PD(NIC
, GENPD_FLAG_ALWAYS_ON
),
141 static const struct meson_secure_pwrc_domain_desc a4_pwrc_domains
[] = {
145 SEC_PD(A4_USB_COMB
, 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
),
159 static const struct meson_secure_pwrc_domain_desc a5_pwrc_domains
[] = {
164 SEC_PD(A5_USB_COMB
, 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
),
175 static const struct meson_secure_pwrc_domain_desc c3_pwrc_domains
[] = {
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
),
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),
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),
198 /* ETH is for ethernet online wakeup, and should be always on */
199 SEC_PD(S4_ETH
, GENPD_FLAG_ALWAYS_ON
),
204 static const struct meson_secure_pwrc_domain_desc t7_pwrc_domains
[] = {
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),
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
),
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),
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
),
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),
263 SEC_PD(T7_MIPI_DSI1
, 0),
267 static int meson_secure_pwrc_probe(struct platform_device
*pdev
)
270 struct device_node
*sm_np
;
271 struct meson_secure_pwrc
*pwrc
;
272 const struct meson_secure_pwrc_domain_data
*match
;
274 match
= of_device_get_match_data(&pdev
->dev
);
276 dev_err(&pdev
->dev
, "failed to get match data\n");
280 sm_np
= of_find_compatible_node(NULL
, NULL
, "amlogic,meson-gxbb-sm");
282 dev_err(&pdev
->dev
, "no secure-monitor node\n");
286 pwrc
= devm_kzalloc(&pdev
->dev
, sizeof(*pwrc
), GFP_KERNEL
);
292 pwrc
->fw
= meson_sm_get(sm_np
);
295 return -EPROBE_DEFER
;
297 pwrc
->xlate
.domains
= devm_kcalloc(&pdev
->dev
, match
->count
,
298 sizeof(*pwrc
->xlate
.domains
),
300 if (!pwrc
->xlate
.domains
)
303 pwrc
->domains
= devm_kcalloc(&pdev
->dev
, match
->count
,
304 sizeof(*pwrc
->domains
), GFP_KERNEL
);
308 pwrc
->xlate
.num_domains
= match
->count
;
309 platform_set_drvdata(pdev
, pwrc
);
311 for (i
= 0 ; i
< match
->count
; ++i
) {
312 struct meson_secure_pwrc_domain
*dom
= &pwrc
->domains
[i
];
314 if (!match
->domains
[i
].name
)
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
;
325 if (match
->domains
[i
].is_off(dom
) && (dom
->base
.flags
& GENPD_FLAG_ALWAYS_ON
))
326 meson_secure_pwrc_on(&dom
->base
);
328 pm_genpd_init(&dom
->base
, NULL
, match
->domains
[i
].is_off(dom
));
330 pwrc
->xlate
.domains
[i
] = &dom
->base
;
333 for (i
= 0; i
< match
->count
; i
++) {
334 struct meson_secure_pwrc_domain
*dom
= pwrc
->domains
;
336 if (!match
->domains
[i
].name
|| match
->domains
[i
].parent
== PWRC_NO_PARENT
)
339 pm_genpd_add_subdomain(&dom
[dom
[i
].parent
].base
, &dom
[i
].base
);
342 return of_genpd_add_provider_onecell(pdev
->dev
.of_node
, &pwrc
->xlate
);
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
),
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
),
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
),
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
),
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
),
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
),
375 static const struct of_device_id meson_secure_pwrc_match_table
[] = {
377 .compatible
= "amlogic,meson-a1-pwrc",
378 .data
= &meson_secure_a1_pwrc_data
,
381 .compatible
= "amlogic,a4-pwrc",
382 .data
= &amlogic_secure_a4_pwrc_data
,
385 .compatible
= "amlogic,a5-pwrc",
386 .data
= &amlogic_secure_a5_pwrc_data
,
389 .compatible
= "amlogic,c3-pwrc",
390 .data
= &amlogic_secure_c3_pwrc_data
,
393 .compatible
= "amlogic,meson-s4-pwrc",
394 .data
= &meson_secure_s4_pwrc_data
,
397 .compatible
= "amlogic,t7-pwrc",
398 .data
= &amlogic_secure_t7_pwrc_data
,
402 MODULE_DEVICE_TABLE(of
, meson_secure_pwrc_match_table
);
404 static struct platform_driver meson_secure_pwrc_driver
= {
405 .probe
= meson_secure_pwrc_probe
,
407 .name
= "meson_secure_pwrc",
408 .of_match_table
= meson_secure_pwrc_match_table
,
411 module_platform_driver(meson_secure_pwrc_driver
);
412 MODULE_DESCRIPTION("Amlogic Meson Secure Power Domains driver");
413 MODULE_LICENSE("Dual MIT/GPL");