blob: ce43ba4a21767a5d1b96c423dacbd4647bda417b [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
2 * Copyright (c) 2018, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <debug.h>
8#include <platform.h>
9#include <platform_def.h>
10#include <psci.h>
11#include <tftf.h>
12
13static unsigned int pstate_initialized;
14
15/*
16 * Stores pointer to the state_prop_t for all implemented levels
17 */
18static const plat_state_prop_t *plat_state_ptr[PLAT_MAX_PWR_LEVEL + 1];
19
20/*
21 * Saves number of implemented power states per level
22 */
23static unsigned int power_states_per_level[PLAT_MAX_PWR_LEVEL + 1];
24
25void tftf_init_pstate_framework(void)
26{
27 int i, j;
28
29 if (pstate_initialized)
30 return;
31
32 /* Detect the PSCI power state format used. */
33 tftf_detect_psci_pstate_format();
34
35 /*
36 * Get and save the pointers to plat_state_prop_t values for all
37 * levels. Also, store the max number of local states possible for
38 * each level in power_states_per_level.
39 */
40 for (i = 0; i <= PLAT_MAX_PWR_LEVEL; i++) {
41 plat_state_ptr[i] = plat_get_state_prop(i);
42 assert(plat_state_ptr[i]);
43
44 for (j = 0; (plat_state_ptr[i]+j)->state_ID != 0; j++)
45 ;
46
47 power_states_per_level[i] = j;
48 }
49
50 pstate_initialized = 1;
51}
52
53void tftf_set_next_state_id_idx(unsigned int power_level,
54 unsigned int pstate_id_idx[])
55{
56 unsigned int i;
57#if ENABLE_ASSERTIONS
58 /* Verify that this is a valid power level. */
59 assert(power_level <= PLAT_MAX_PWR_LEVEL);
60
61 /*
62 * Verify if a level has PWR_STATE_INIT_INDEX index, all higher levels
63 * have to be in PWR_STATE_INIT_INDEX. Not needed to check the top
64 * power level in the outer loop.
65 */
66 for (i = 0; i < power_level; i++) {
67 if (pstate_id_idx[i] == PWR_STATE_INIT_INDEX) {
68 for ( ; i <= power_level; i++)
69 assert(pstate_id_idx[i] == PWR_STATE_INIT_INDEX);
70 }
71 }
72#endif
73
74 /* Increment the pstate_id_idx starting from the lowest power level */
75 for (i = 0; i <= power_level; i++) {
76 pstate_id_idx[i]++;
77
78 /*
79 * Wraparound index if the maximum power states available for
80 * that level is reached and proceed to next level.
81 */
82 if (pstate_id_idx[i] == power_states_per_level[i])
83 pstate_id_idx[i] = 0;
84 else
85 break;
86 }
87
88 /*
89 * Check if the requested power level has wrapped around. If it has,
90 * reset pstate_id_idx.
91 */
92 if (i > power_level) {
93 for (i = 0; i <= power_level; i++)
94 pstate_id_idx[i] = PWR_STATE_INIT_INDEX;
95 }
96}
97
98void tftf_set_deepest_pstate_idx(unsigned int power_level,
99 unsigned int pstate_id_idx[])
100{
101 int i;
102
103 /* Verify that this is a valid power level. */
104 assert(power_level <= PLAT_MAX_PWR_LEVEL);
105
106 /*
107 * Assign the highest pstate_id_idx starting from the lowest power
108 * level
109 */
110 for (i = 0; i <= power_level; i++)
111 pstate_id_idx[i] = power_states_per_level[i] - 1;
112}
113
114
115int tftf_get_pstate_vars(unsigned int *test_power_level,
116 unsigned int *test_suspend_type,
117 unsigned int *suspend_state_id,
118 unsigned int pstate_id_idx[])
119{
120 unsigned int i;
121 int state_id = 0;
122 int suspend_type;
123 int suspend_depth;
124 int psci_ret = PSCI_E_SUCCESS;
125 const plat_state_prop_t *local_state;
126
127 /* Atleast one entry should be valid to generate correct power state params */
128 assert(pstate_id_idx[0] != PWR_STATE_INIT_INDEX &&
129 pstate_id_idx[0] <= power_states_per_level[0]);
130
131 suspend_depth = (plat_state_ptr[0] + pstate_id_idx[0])->suspend_depth;
132 suspend_type = (plat_state_ptr[0] + pstate_id_idx[0])->is_pwrdown;
133
134 for (i = 0; i <= PLAT_MAX_PWR_LEVEL; i++) {
135
136 /* Reached all levels with the valid power index values */
137 if (pstate_id_idx[i] == PWR_STATE_INIT_INDEX)
138 break;
139
140 assert(pstate_id_idx[i] <= power_states_per_level[i]);
141
142 local_state = plat_state_ptr[i] + pstate_id_idx[i];
143 state_id |= (local_state->state_ID << i * PLAT_LOCAL_PSTATE_WIDTH);
144
145 if (local_state->is_pwrdown > suspend_type)
146 suspend_type = local_state->is_pwrdown;
147
148 if (local_state->suspend_depth > suspend_depth)
149 psci_ret = PSCI_E_INVALID_PARAMS;
150 else
151 suspend_depth = local_state->suspend_depth;
152 }
153
154 *test_suspend_type = suspend_type;
155 *suspend_state_id = state_id;
156 *test_power_level = --i;
157
158 return psci_ret;
159}
160
161void tftf_set_next_local_state_id_idx(unsigned int power_level,
162 unsigned int pstate_id_idx[])
163{
164 assert(power_level <= PLAT_MAX_PWR_LEVEL);
165
166 if (pstate_id_idx[power_level] + 1 >= power_states_per_level[power_level]) {
167 pstate_id_idx[power_level] = PWR_STATE_INIT_INDEX;
168 return;
169 }
170
171 pstate_id_idx[power_level]++;
172}