blob: 781b2164ceeff96d2e610ec4a05727d485697847 [file] [log] [blame]
Soby Mathew200fffd2016-10-21 11:34:59 +01001/*
Boyan Karatotev5eefc812025-01-07 11:04:16 +00002 * Copyright (c) 2016-2025, Arm Limited and Contributors. All rights reserved.
Soby Mathew200fffd2016-10-21 11:34:59 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soby Mathew200fffd2016-10-21 11:34:59 +01005 */
6
Soby Mathew200fffd2016-10-21 11:34:59 +01007#include <assert.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00008
9#include <arch_helpers.h>
10#include <common/debug.h>
Antonio Nino Diaz326f56b2019-01-23 18:55:03 +000011#include <drivers/arm/css/css_scp.h>
Antonio Nino Diazae9654d2019-01-25 14:23:49 +000012#include <drivers/arm/css/css_scpi.h>
Antonio Nino Diazbd7b7402019-01-25 14:30:04 +000013#include <plat/arm/common/plat_arm.h>
14#include <plat/arm/css/common/css_pm.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000015
Soby Mathew200fffd2016-10-21 11:34:59 +010016/*
17 * This file implements the SCP power management functions using SCPI protocol.
18 */
19
20/*
21 * Helper function to inform power down state to SCP.
22 */
Roberto Vargas5f5a5e62018-02-12 12:36:17 +000023void css_scp_suspend(const struct psci_power_state *target_state)
Soby Mathew200fffd2016-10-21 11:34:59 +010024{
25 uint32_t cluster_state = scpi_power_on;
26 uint32_t system_state = scpi_power_on;
27
28 /* Check if power down at system power domain level is requested */
Nariman Poushincd956262018-05-01 09:28:40 +010029 if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF)
Soby Mathew200fffd2016-10-21 11:34:59 +010030 system_state = scpi_power_retention;
31
32 /* Cluster is to be turned off, so disable coherency */
33 if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF)
34 cluster_state = scpi_power_off;
35
36 /*
37 * Ask the SCP to power down the appropriate components depending upon
38 * their state.
39 */
40 scpi_set_css_power_state(read_mpidr_el1(),
41 scpi_power_off,
42 cluster_state,
43 system_state);
44}
45
46/*
47 * Helper function to turn off a CPU power domain and its parent power domains
48 * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we
49 * call the suspend helper here.
50 */
Roberto Vargas85664f52018-02-12 12:36:17 +000051void css_scp_off(const struct psci_power_state *target_state)
Soby Mathew200fffd2016-10-21 11:34:59 +010052{
53 css_scp_suspend(target_state);
54}
55
56/*
57 * Helper function to turn ON a CPU power domain and its parent power domains
58 * if applicable.
59 */
60void css_scp_on(u_register_t mpidr)
61{
62 /*
63 * SCP takes care of powering up parent power domains so we
64 * only need to care about level 0
65 */
66 scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on,
67 scpi_power_on);
68}
69
70/*
71 * Helper function to get the power state of a power domain node as reported
72 * by the SCP.
73 */
74int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level)
75{
76 int rc, element;
77 unsigned int cpu_state, cluster_state;
78
79 /*
80 * The format of 'power_level' is implementation-defined, but 0 must
81 * mean a CPU. We also allow 1 to denote the cluster
82 */
83 if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1)
84 return PSCI_E_INVALID_PARAMS;
85
86 /* Query SCP */
87 rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state);
88 if (rc != 0)
89 return PSCI_E_INVALID_PARAMS;
90
91 /* Map power states of CPU and cluster to expected PSCI return codes */
92 if (power_level == ARM_PWR_LVL0) {
93 /*
94 * The CPU state returned by SCP is an 8-bit bit mask
95 * corresponding to each CPU in the cluster
96 */
jagadeesh ujja64fa64b2017-05-11 16:32:18 +053097#if ARM_PLAT_MT
98 /*
99 * The current SCPI driver only caters for single-threaded
100 * platforms. Hence we ignore the thread ID (which is always 0)
101 * for such platforms.
102 */
103 element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK;
104#else
Soby Mathew200fffd2016-10-21 11:34:59 +0100105 element = mpidr & MPIDR_AFFLVL_MASK;
jagadeesh ujja64fa64b2017-05-11 16:32:18 +0530106#endif /* ARM_PLAT_MT */
Soby Mathew200fffd2016-10-21 11:34:59 +0100107 return CSS_CPU_PWR_STATE(cpu_state, element) ==
108 CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF;
109 } else {
110 assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON ||
111 cluster_state == CSS_CLUSTER_PWR_STATE_OFF);
112 return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON :
113 HW_OFF;
114 }
115}
116
117/*
118 * Helper function to shutdown the system via SCPI.
119 */
Boyan Karatotev304b9692024-09-26 16:04:16 +0100120void css_scp_sys_shutdown(void)
Soby Mathew200fffd2016-10-21 11:34:59 +0100121{
122 uint32_t response;
123
124 /* Send the power down request to the SCP */
125 response = scpi_sys_power_state(scpi_system_shutdown);
126
127 if (response != SCP_OK) {
128 ERROR("CSS System Off: SCP error %u.\n", response);
129 panic();
130 }
Soby Mathew200fffd2016-10-21 11:34:59 +0100131}
132
133/*
134 * Helper function to reset the system via SCPI.
135 */
Boyan Karatotev304b9692024-09-26 16:04:16 +0100136void css_scp_sys_reboot(void)
Soby Mathew200fffd2016-10-21 11:34:59 +0100137{
138 uint32_t response;
139
Soby Mathew200fffd2016-10-21 11:34:59 +0100140 /* Send the system reset request to the SCP */
141 response = scpi_sys_power_state(scpi_system_reboot);
142
143 if (response != SCP_OK) {
144 ERROR("CSS System Reset: SCP error %u.\n", response);
145 panic();
146 }
Soby Mathew200fffd2016-10-21 11:34:59 +0100147}