blob: ed1363537e26a5a31b899a39f43cfef90e38838e [file] [log] [blame]
Samuel Hollandad6f6ca2021-01-16 00:56:48 -06001/*
2 * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8
9#include <platform_def.h>
10
11#include <arch_helpers.h>
12#include <common/debug.h>
13#include <drivers/arm/css/css_scpi.h>
14#include <drivers/arm/gicv2.h>
15#include <lib/mmio.h>
16#include <lib/psci/psci.h>
17
18#include <sunxi_mmap.h>
19#include <sunxi_private.h>
20
21/*
22 * The addresses for the SCP exception vectors are defined in the or1k
23 * architecture specification.
24 */
25#define OR1K_VEC_FIRST 0x01
26#define OR1K_VEC_LAST 0x0e
27#define OR1K_VEC_ADDR(n) (0x100 * (n))
28
29/*
30 * This magic value is the little-endian representation of the or1k
31 * instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the
32 * first instruction in the SCP firmware.
33 */
34#define SCP_FIRMWARE_MAGIC 0xb4400012
35
36#define CPU_PWR_LVL MPIDR_AFFLVL0
37#define CLUSTER_PWR_LVL MPIDR_AFFLVL1
38#define SYSTEM_PWR_LVL MPIDR_AFFLVL2
39
40#define CPU_PWR_STATE(state) \
41 ((state)->pwr_domain_state[CPU_PWR_LVL])
42#define CLUSTER_PWR_STATE(state) \
43 ((state)->pwr_domain_state[CLUSTER_PWR_LVL])
44#define SYSTEM_PWR_STATE(state) \
45 ((state)->pwr_domain_state[SYSTEM_PWR_LVL])
46
Samuel Hollandad6f6ca2021-01-16 00:56:48 -060047static void sunxi_cpu_standby(plat_local_state_t cpu_state)
48{
49 u_register_t scr = read_scr_el3();
50
51 assert(is_local_state_retn(cpu_state));
52
53 write_scr_el3(scr | SCR_IRQ_BIT);
54 wfi();
55 write_scr_el3(scr);
56}
57
58static int sunxi_pwr_domain_on(u_register_t mpidr)
59{
60 scpi_set_css_power_state(mpidr,
61 scpi_power_on,
62 scpi_power_on,
63 scpi_power_on);
64
65 return PSCI_E_SUCCESS;
66}
67
68static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
69{
70 plat_local_state_t cpu_pwr_state = CPU_PWR_STATE(target_state);
71 plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state);
72 plat_local_state_t system_pwr_state = SYSTEM_PWR_STATE(target_state);
73
74 if (is_local_state_off(cpu_pwr_state)) {
75 gicv2_cpuif_disable();
76 }
77
78 scpi_set_css_power_state(read_mpidr(),
Samuel Holland72897662021-03-18 22:55:15 -050079 cpu_pwr_state,
80 cluster_pwr_state,
81 system_pwr_state);
Samuel Hollandad6f6ca2021-01-16 00:56:48 -060082}
83
84static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state)
85{
86 if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
87 gicv2_distif_init();
88 }
89 if (is_local_state_off(CPU_PWR_STATE(target_state))) {
90 gicv2_pcpu_distif_init();
91 gicv2_cpuif_enable();
92 }
93}
94
95static void __dead2 sunxi_system_off(void)
96{
97 uint32_t ret;
98
99 gicv2_cpuif_disable();
100
101 /* Send the power down request to the SCP. */
102 ret = scpi_sys_power_state(scpi_system_shutdown);
103 if (ret != SCP_OK) {
104 ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret);
105 }
106
107 psci_power_down_wfi();
108}
109
110static void __dead2 sunxi_system_reset(void)
111{
112 uint32_t ret;
113
114 gicv2_cpuif_disable();
115
116 /* Send the system reset request to the SCP. */
117 ret = scpi_sys_power_state(scpi_system_reboot);
118 if (ret != SCP_OK) {
119 ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret);
120 }
121
122 psci_power_down_wfi();
123}
124
125static int sunxi_validate_power_state(unsigned int power_state,
126 psci_power_state_t *req_state)
127{
128 unsigned int power_level = psci_get_pstate_pwrlvl(power_state);
129 unsigned int type = psci_get_pstate_type(power_state);
130
131 assert(req_state != NULL);
132
133 if (power_level > PLAT_MAX_PWR_LVL) {
134 return PSCI_E_INVALID_PARAMS;
135 }
136
137 if (type == PSTATE_TYPE_STANDBY) {
138 /* Only one retention power state is supported. */
139 if (psci_get_pstate_id(power_state) > 0) {
140 return PSCI_E_INVALID_PARAMS;
141 }
142 /* The SoC cannot be suspended without losing state */
143 if (power_level == SYSTEM_PWR_LVL) {
144 return PSCI_E_INVALID_PARAMS;
145 }
146 for (unsigned int i = 0; i <= power_level; ++i) {
147 req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE;
148 }
149 } else {
150 /* Only one off power state is supported. */
151 if (psci_get_pstate_id(power_state) > 0) {
152 return PSCI_E_INVALID_PARAMS;
153 }
154 for (unsigned int i = 0; i <= power_level; ++i) {
155 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
156 }
157 }
158 /* Higher power domain levels should all remain running */
159 for (unsigned int i = power_level + 1; i <= PLAT_MAX_PWR_LVL; ++i) {
160 req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN;
161 }
162
163 return PSCI_E_SUCCESS;
164}
165
166static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state)
167{
168 assert(req_state != NULL);
169
170 for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) {
171 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
172 }
173}
174
175static const plat_psci_ops_t sunxi_scpi_psci_ops = {
176 .cpu_standby = sunxi_cpu_standby,
177 .pwr_domain_on = sunxi_pwr_domain_on,
178 .pwr_domain_off = sunxi_pwr_domain_off,
179 .pwr_domain_suspend = sunxi_pwr_domain_off,
180 .pwr_domain_on_finish = sunxi_pwr_domain_on_finish,
181 .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish,
182 .system_off = sunxi_system_off,
183 .system_reset = sunxi_system_reset,
184 .validate_power_state = sunxi_validate_power_state,
185 .validate_ns_entrypoint = sunxi_validate_ns_entrypoint,
186 .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state,
187};
188
189int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops)
190{
191 *psci_ops = &sunxi_scpi_psci_ops;
192
193 /* Check for a valid SCP firmware. */
194 if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) {
195 return -1;
196 }
197
198 /* Program SCP exception vectors to the firmware entrypoint. */
199 for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) {
200 uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i);
201 uint32_t offset = SUNXI_SCP_BASE - vector;
202
203 mmio_write_32(vector, offset >> 2);
Samuel Hollandad6f6ca2021-01-16 00:56:48 -0600204 }
205
206 /* Take the SCP out of reset. */
207 mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0));
208
209 /* Wait for the SCP firmware to boot. */
210 return scpi_wait_ready();
211}