blob: ae361e97346ceb6098be90507c3dbd6fbac1b699 [file] [log] [blame]
Saurabh Gorecha70389ca2020-04-22 21:31:24 +05301/*
2 * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
3 * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7#include <assert.h>
8
9#include <arch_helpers.h>
10#include <bl31/bl31.h>
11#include <common/debug.h>
Julius Werner0375d322019-02-20 17:33:23 -080012#include <drivers/delay_timer.h>
13#include <lib/mmio.h>
Saurabh Gorecha70389ca2020-04-22 21:31:24 +053014#include <lib/psci/psci.h>
15
16#include <platform.h>
17#include <platform_def.h>
18#include <qti_cpu.h>
19#include <qti_plat.h>
20#include <qtiseclib_cb_interface.h>
21#include <qtiseclib_defs_plat.h>
22#include <qtiseclib_interface.h>
23
24#define QTI_LOCAL_PSTATE_WIDTH 4
25#define QTI_LOCAL_PSTATE_MASK ((1 << QTI_LOCAL_PSTATE_WIDTH) - 1)
26
27/* Make composite power state parameter till level 0 */
28#define qti_make_pwrstate_lvl0(lvl0_state, type) \
29 (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT))
30
31/* Make composite power state parameter till level 1 */
32#define qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type) \
33 (((lvl1_state) << QTI_LOCAL_PSTATE_WIDTH) | \
34 qti_make_pwrstate_lvl0(lvl0_state, type))
35
36/* Make composite power state parameter till level 2 */
37#define qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type) \
38 (((lvl2_state) << (QTI_LOCAL_PSTATE_WIDTH * 2)) | \
39 qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type))
40
41/* Make composite power state parameter till level 3 */
42#define qti_make_pwrstate_lvl3(lvl3_state, lvl2_state, lvl1_state, lvl0_state, type) \
43 (((lvl3_state) << (QTI_LOCAL_PSTATE_WIDTH * 3)) | \
44 qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type))
45
46/* QTI_CORE_PWRDN_EN_MASK happens to be same across all CPUs */
47#define QTI_CORE_PWRDN_EN_MASK 1
48
49/* cpu power control happens to be same across all CPUs */
Andre Przywara23b57bb2022-11-14 10:39:48 +000050DEFINE_RENAME_SYSREG_RW_FUNCS(cpu_pwrctrl_val, S3_0_C15_C2_7)
Saurabh Gorecha70389ca2020-04-22 21:31:24 +053051
52const unsigned int qti_pm_idle_states[] = {
53 qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_OFF,
54 PSTATE_TYPE_POWERDOWN),
55 qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_DEEPOFF,
56 PSTATE_TYPE_POWERDOWN),
57 qti_make_pwrstate_lvl1(QTI_LOCAL_STATE_DEEPOFF,
58 QTI_LOCAL_STATE_DEEPOFF,
59 PSTATE_TYPE_POWERDOWN),
60 qti_make_pwrstate_lvl2(QTI_LOCAL_STATE_OFF,
61 QTI_LOCAL_STATE_DEEPOFF,
62 QTI_LOCAL_STATE_DEEPOFF,
63 PSTATE_TYPE_POWERDOWN),
64 qti_make_pwrstate_lvl3(QTI_LOCAL_STATE_OFF,
65 QTI_LOCAL_STATE_DEEPOFF,
66 QTI_LOCAL_STATE_DEEPOFF,
67 QTI_LOCAL_STATE_DEEPOFF,
68 PSTATE_TYPE_POWERDOWN),
69 0,
70};
71
72/*******************************************************************************
73 * QTI standard platform handler called to check the validity of the power
74 * state parameter. The power state parameter has to be a composite power
75 * state.
76 ******************************************************************************/
77int qti_validate_power_state(unsigned int power_state,
78 psci_power_state_t *req_state)
79{
80 unsigned int state_id;
81 int i;
82
83 assert(req_state);
84
85 /*
86 * Currently we are using a linear search for finding the matching
87 * entry in the idle power state array. This can be made a binary
88 * search if the number of entries justify the additional complexity.
89 */
90 for (i = 0; !!qti_pm_idle_states[i]; i++) {
91 if (power_state == qti_pm_idle_states[i])
92 break;
93 }
94
95 /* Return error if entry not found in the idle state array */
96 if (!qti_pm_idle_states[i])
97 return PSCI_E_INVALID_PARAMS;
98
99 i = 0;
100 state_id = psci_get_pstate_id(power_state);
101
102 /* Parse the State ID and populate the state info parameter */
103 while (state_id) {
104 req_state->pwr_domain_state[i++] = state_id &
105 QTI_LOCAL_PSTATE_MASK;
106 state_id >>= QTI_LOCAL_PSTATE_WIDTH;
107 }
108
109 return PSCI_E_SUCCESS;
110}
111
112/*******************************************************************************
113 * PLATFORM FUNCTIONS
114 ******************************************************************************/
115
116static void qti_set_cpupwrctlr_val(void)
117{
118 unsigned long val;
119
120 val = read_cpu_pwrctrl_val();
121 val |= QTI_CORE_PWRDN_EN_MASK;
122 write_cpu_pwrctrl_val(val);
123
124 isb();
125}
126
127/**
128 * CPU power on function - ideally we want a wrapper since this function is
129 * target specific. But to unblock teams.
130 */
131static int qti_cpu_power_on(u_register_t mpidr)
132{
133 int core_pos = plat_core_pos_by_mpidr(mpidr);
134
135 /* If not valid mpidr, return error */
136 if (core_pos < 0 || core_pos >= QTISECLIB_PLAT_CORE_COUNT) {
137 return PSCI_E_INVALID_PARAMS;
138 }
139
140 return qtiseclib_psci_node_power_on(mpidr);
141}
142
143static bool is_cpu_off(const psci_power_state_t *target_state)
144{
145 if ((target_state->pwr_domain_state[QTI_PWR_LVL0] ==
146 QTI_LOCAL_STATE_OFF) ||
147 (target_state->pwr_domain_state[QTI_PWR_LVL0] ==
148 QTI_LOCAL_STATE_DEEPOFF)) {
149 return true;
150 } else {
151 return false;
152 }
153}
154
155static void qti_cpu_power_on_finish(const psci_power_state_t *target_state)
156{
157 const uint8_t *pwr_states =
158 (const uint8_t *)target_state->pwr_domain_state;
159 qtiseclib_psci_node_on_finish(pwr_states);
160
161 if (is_cpu_off(target_state)) {
162 plat_qti_gic_cpuif_enable();
163 }
164}
165
166static void qti_cpu_standby(plat_local_state_t cpu_state)
167{
168}
169
170static void qti_node_power_off(const psci_power_state_t *target_state)
171{
172 qtiseclib_psci_node_power_off((const uint8_t *)
173 target_state->pwr_domain_state);
174 if (is_cpu_off(target_state)) {
175 plat_qti_gic_cpuif_disable();
176 qti_set_cpupwrctlr_val();
177 }
178}
179
180static void qti_node_suspend(const psci_power_state_t *target_state)
181{
182 qtiseclib_psci_node_suspend((const uint8_t *)target_state->
183 pwr_domain_state);
184 if (is_cpu_off(target_state)) {
185 plat_qti_gic_cpuif_disable();
186 qti_set_cpupwrctlr_val();
187 }
188}
189
190static void qti_node_suspend_finish(const psci_power_state_t *target_state)
191{
192 const uint8_t *pwr_states =
193 (const uint8_t *)target_state->pwr_domain_state;
194 qtiseclib_psci_node_suspend_finish(pwr_states);
195 if (is_cpu_off(target_state)) {
196 plat_qti_gic_cpuif_enable();
197 }
198}
199
200__dead2 void qti_domain_power_down_wfi(const psci_power_state_t *target_state)
201{
202
203 /* For now just do WFI - add any target specific handling if needed */
204 psci_power_down_wfi();
205 /* We should never reach here */
206}
207
Julius Werner0375d322019-02-20 17:33:23 -0800208static __dead2 void assert_ps_hold(void)
209{
210 mmio_write_32(QTI_PS_HOLD_REG, 0);
211 mdelay(1000);
212
213 /* Should be dead before reaching this. */
214 panic();
215}
216
Saurabh Gorecha70389ca2020-04-22 21:31:24 +0530217__dead2 void qti_system_off(void)
218{
Julius Werner0375d322019-02-20 17:33:23 -0800219 qti_pmic_prepare_shutdown();
220 assert_ps_hold();
Saurabh Gorecha70389ca2020-04-22 21:31:24 +0530221}
222
223__dead2 void qti_system_reset(void)
224{
Julius Werner0375d322019-02-20 17:33:23 -0800225 qti_pmic_prepare_reset();
226 assert_ps_hold();
Saurabh Gorecha70389ca2020-04-22 21:31:24 +0530227}
228
229void qti_get_sys_suspend_power_state(psci_power_state_t *req_state)
230{
231 int i = 0;
232 unsigned int state_id, power_state;
233 int size = ARRAY_SIZE(qti_pm_idle_states);
234
235 /*
236 * Find deepest state.
237 * The arm_pm_idle_states[] array has last element by default 0,
238 * so the real deepest state is second last element of that array.
239 */
240 power_state = qti_pm_idle_states[size - 2];
241 state_id = psci_get_pstate_id(power_state);
242
243 /* Parse the State ID and populate the state info parameter */
244 while (state_id) {
245 req_state->pwr_domain_state[i++] =
246 state_id & QTI_LOCAL_PSTATE_MASK;
247 state_id >>= QTI_LOCAL_PSTATE_WIDTH;
248 }
249}
250
251/*
252 * Structure containing platform specific PSCI operations. Common
253 * PSCI layer will use this.
254 */
255const plat_psci_ops_t plat_qti_psci_pm_ops = {
256 .pwr_domain_on = qti_cpu_power_on,
257 .pwr_domain_on_finish = qti_cpu_power_on_finish,
258 .cpu_standby = qti_cpu_standby,
259 .pwr_domain_off = qti_node_power_off,
260 .pwr_domain_suspend = qti_node_suspend,
261 .pwr_domain_suspend_finish = qti_node_suspend_finish,
262 .pwr_domain_pwr_down_wfi = qti_domain_power_down_wfi,
263 .system_off = qti_system_off,
264 .system_reset = qti_system_reset,
265 .get_node_hw_state = NULL,
266 .translate_power_state_by_mpidr = NULL,
267 .get_sys_suspend_power_state = qti_get_sys_suspend_power_state,
268 .validate_power_state = qti_validate_power_state,
269};
270
271/**
272 * The QTI Standard platform definition of platform porting API
273 * `plat_setup_psci_ops`.
274 */
275int plat_setup_psci_ops(uintptr_t sec_entrypoint,
276 const plat_psci_ops_t **psci_ops)
277{
278 int err;
279
280 err = qtiseclib_psci_init((uintptr_t)bl31_warm_entrypoint);
281 if (err == PSCI_E_SUCCESS) {
282 *psci_ops = &plat_qti_psci_pm_ops;
283 }
284
285 return err;
286}