1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "ppu_v1.h"
9
10 #include <fwk_assert.h>
11 #include <fwk_status.h>
12
13 #include <stddef.h>
14
15 struct set_power_status_check_params_v1 {
16 enum ppu_v1_mode mode;
17 struct ppu_v1_reg *reg;
18 };
19
ppu_v1_set_power_status_check(void * data)20 static bool ppu_v1_set_power_status_check(void *data)
21 {
22 struct set_power_status_check_params_v1 *params;
23
24 fwk_assert(data != NULL);
25 params = (struct set_power_status_check_params_v1 *)data;
26
27 return (
28 (params->reg->PWSR &
29 (PPU_V1_PWSR_PWR_STATUS | PPU_V1_PWSR_PWR_DYN_STATUS)) ==
30 params->mode);
31 }
32
ppu_v1_init(struct ppu_v1_reg * ppu)33 void ppu_v1_init(struct ppu_v1_reg *ppu)
34 {
35 fwk_assert(ppu != NULL);
36
37 /* Set edge sensitivity to masked for all input edges */
38 ppu->IESR = 0;
39
40 /* Mask all interrupts */
41 ppu->IMR = PPU_V1_IMR_MASK;
42
43 /* Acknowledge any interrupt left pending */
44 ppu->ISR = PPU_V1_ISR_MASK;
45 }
46
47 /*
48 * PWPR and PWSR registers
49 */
ppu_v1_request_power_mode(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode)50 int ppu_v1_request_power_mode(struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode)
51 {
52 uint32_t power_policy;
53 fwk_assert(ppu != NULL);
54 fwk_assert(ppu_mode < PPU_V1_MODE_COUNT);
55
56 power_policy = ppu->PWPR & ~(PPU_V1_PWPR_POLICY | PPU_V1_PWPR_DYNAMIC_EN);
57 ppu->PWPR = power_policy | ppu_mode;
58
59 return FWK_SUCCESS;
60 }
61
ppu_v1_set_power_mode(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode,struct ppu_v1_timer_ctx * timer_ctx)62 int ppu_v1_set_power_mode(
63 struct ppu_v1_reg *ppu,
64 enum ppu_v1_mode ppu_mode,
65 struct ppu_v1_timer_ctx *timer_ctx)
66 {
67 int status;
68 struct set_power_status_check_params_v1 params;
69 status = ppu_v1_request_power_mode(ppu, ppu_mode);
70 if (status != FWK_SUCCESS)
71 return status;
72
73 if (timer_ctx == NULL) {
74 while ((ppu->PWSR &
75 (PPU_V1_PWSR_PWR_STATUS | PPU_V1_PWSR_PWR_DYN_STATUS)) !=
76 ppu_mode)
77 continue;
78 } else {
79 params.mode = ppu_mode;
80 params.reg = ppu;
81 return timer_ctx->timer_api->wait(
82 timer_ctx->timer_id,
83 timer_ctx->delay_us,
84 ppu_v1_set_power_status_check,
85 ¶ms);
86 }
87
88 return FWK_SUCCESS;
89 }
90
ppu_v1_request_operating_mode(struct ppu_v1_reg * ppu,enum ppu_v1_opmode op_mode)91 int ppu_v1_request_operating_mode(struct ppu_v1_reg *ppu,
92 enum ppu_v1_opmode op_mode)
93 {
94 uint32_t power_policy;
95 fwk_assert(ppu != NULL);
96 fwk_assert(op_mode < PPU_V1_OPMODE_COUNT);
97
98 power_policy = ppu->PWPR & ~(PPU_V1_PWPR_OP_POLICY | PPU_V1_PWPR_OP_DYN_EN);
99 ppu->PWPR = power_policy | (op_mode << PPU_V1_PWPR_OP_POLICY_POS);
100
101 return FWK_SUCCESS;
102 }
103
ppu_v1_opmode_dynamic_enable(struct ppu_v1_reg * ppu,enum ppu_v1_opmode min_dyn_mode)104 void ppu_v1_opmode_dynamic_enable(struct ppu_v1_reg *ppu,
105 enum ppu_v1_opmode min_dyn_mode)
106 {
107 uint32_t power_policy;
108
109 fwk_assert(ppu != NULL);
110 fwk_assert(min_dyn_mode < PPU_V1_OPMODE_COUNT);
111
112 power_policy = ppu->PWPR & ~PPU_V1_PWPR_OP_POLICY;
113 ppu->PWPR = power_policy |
114 PPU_V1_PWPR_OP_DYN_EN |
115 (min_dyn_mode << PPU_V1_PWPR_OP_POLICY_POS);
116 while ((ppu->PWSR & PPU_V1_PWSR_OP_DYN_STATUS) == 0)
117 continue;
118 }
119
ppu_v1_dynamic_enable(struct ppu_v1_reg * ppu,enum ppu_v1_mode min_dyn_state)120 void ppu_v1_dynamic_enable(struct ppu_v1_reg *ppu,
121 enum ppu_v1_mode min_dyn_state)
122 {
123 uint32_t power_policy;
124
125 fwk_assert(ppu != NULL);
126 fwk_assert(min_dyn_state < PPU_V1_MODE_COUNT);
127
128 power_policy = ppu->PWPR & ~PPU_V1_PWPR_POLICY;
129 ppu->PWPR = power_policy | PPU_V1_PWPR_DYNAMIC_EN | min_dyn_state;
130 while ((ppu->PWSR & PPU_V1_PWSR_PWR_DYN_STATUS) == 0)
131 continue;
132 }
133
ppu_v1_lock_off_enable(struct ppu_v1_reg * ppu)134 void ppu_v1_lock_off_enable(struct ppu_v1_reg *ppu)
135 {
136 fwk_assert(ppu != NULL);
137
138 ppu->PWPR |= PPU_V1_PWPR_OFF_LOCK_EN;
139 }
140
ppu_v1_lock_off_disable(struct ppu_v1_reg * ppu)141 void ppu_v1_lock_off_disable(struct ppu_v1_reg *ppu)
142 {
143 fwk_assert(ppu != NULL);
144
145 ppu->PWPR &= ~PPU_V1_PWPR_OFF_LOCK_EN;
146 }
147
ppu_v1_get_power_mode(struct ppu_v1_reg * ppu)148 enum ppu_v1_mode ppu_v1_get_power_mode(struct ppu_v1_reg *ppu)
149 {
150 fwk_assert(ppu != NULL);
151
152 return (enum ppu_v1_mode)(ppu->PWSR & PPU_V1_PWSR_PWR_STATUS);
153 }
154
ppu_v1_get_programmed_power_mode(struct ppu_v1_reg * ppu)155 enum ppu_v1_mode ppu_v1_get_programmed_power_mode(struct ppu_v1_reg *ppu)
156 {
157 fwk_assert(ppu != NULL);
158
159 return (enum ppu_v1_mode)(ppu->PWPR & PPU_V1_PWPR_POLICY);
160 }
161
ppu_v1_get_operating_mode(struct ppu_v1_reg * ppu)162 enum ppu_v1_opmode ppu_v1_get_operating_mode(struct ppu_v1_reg *ppu)
163 {
164 fwk_assert(ppu != NULL);
165
166 return (enum ppu_v1_opmode)
167 ((ppu->PWSR & PPU_V1_PWSR_OP_STATUS) >> PPU_V1_PWSR_OP_STATUS_POS);
168 }
169
ppu_v1_get_programmed_operating_mode(struct ppu_v1_reg * ppu)170 enum ppu_v1_opmode ppu_v1_get_programmed_operating_mode(struct ppu_v1_reg *ppu)
171 {
172 fwk_assert(ppu != NULL);
173
174 return (enum ppu_v1_opmode)
175 ((ppu->PWPR & PPU_V1_PWPR_OP_POLICY) >> PPU_V1_PWPR_OP_POLICY_POS);
176 }
177
ppu_v1_is_dynamic_enabled(struct ppu_v1_reg * ppu)178 bool ppu_v1_is_dynamic_enabled(struct ppu_v1_reg *ppu)
179 {
180 fwk_assert(ppu != NULL);
181
182 return ((ppu->PWSR & PPU_V1_PWSR_PWR_DYN_STATUS) != 0);
183 }
184
ppu_v1_is_locked(struct ppu_v1_reg * ppu)185 bool ppu_v1_is_locked(struct ppu_v1_reg *ppu)
186 {
187 fwk_assert(ppu != NULL);
188
189 return ((ppu->PWSR & PPU_V1_PWSR_OFF_LOCK_STATUS) != 0);
190 }
191
192 /*
193 * DISR register
194 */
ppu_v1_is_power_devactive_high(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode)195 bool ppu_v1_is_power_devactive_high(struct ppu_v1_reg *ppu,
196 enum ppu_v1_mode ppu_mode)
197 {
198 fwk_assert(ppu != NULL);
199
200 return (ppu->DISR &
201 (1 << (ppu_mode + PPU_V1_DISR_PWR_DEVACTIVE_STATUS_POS))) != 0;
202 }
203
ppu_v1_is_op_devactive_high(struct ppu_v1_reg * ppu,enum ppu_v1_op_devactive op_devactive)204 bool ppu_v1_is_op_devactive_high(struct ppu_v1_reg *ppu,
205 enum ppu_v1_op_devactive op_devactive)
206 {
207 fwk_assert(ppu != NULL);
208
209 return (ppu->DISR &
210 (1 << (op_devactive + PPU_V1_DISR_OP_DEVACTIVE_STATUS_POS))) != 0;
211 }
212
213 /*
214 * UNLK register
215 */
ppu_v1_off_unlock(struct ppu_v1_reg * ppu)216 void ppu_v1_off_unlock(struct ppu_v1_reg *ppu)
217 {
218 fwk_assert(ppu != NULL);
219
220 ppu->UNLK = PPU_V1_UNLK_OFF_UNLOCK;
221 }
222
223 /*
224 * PWCR register
225 */
ppu_v1_disable_devactive(struct ppu_v1_reg * ppu)226 void ppu_v1_disable_devactive(struct ppu_v1_reg *ppu)
227 {
228 fwk_assert(ppu != NULL);
229
230 ppu->PWCR &= ~PPU_V1_PWCR_DEV_ACTIVE_EN;
231 }
232
ppu_v1_disable_handshake(struct ppu_v1_reg * ppu)233 void ppu_v1_disable_handshake(struct ppu_v1_reg *ppu)
234 {
235 fwk_assert(ppu != NULL);
236
237 ppu->PWCR &= ~PPU_V1_PWCR_DEV_REQ_EN;
238 }
239
240 /*
241 * Interrupt registers: IMR, AIMR, ISR, AISR, IESR, OPSR
242 */
ppu_v1_interrupt_mask(struct ppu_v1_reg * ppu,unsigned int mask)243 void ppu_v1_interrupt_mask(struct ppu_v1_reg *ppu, unsigned int mask)
244 {
245 fwk_assert(ppu != NULL);
246
247 ppu->IMR |= mask & PPU_V1_IMR_MASK;
248 }
249
ppu_v1_additional_interrupt_mask(struct ppu_v1_reg * ppu,unsigned int mask)250 void ppu_v1_additional_interrupt_mask(struct ppu_v1_reg *ppu, unsigned int mask)
251 {
252 fwk_assert(ppu != NULL);
253
254 ppu->AIMR |= mask & PPU_V1_AIMR_MASK;
255 }
256
ppu_v1_interrupt_unmask(struct ppu_v1_reg * ppu,unsigned int mask)257 void ppu_v1_interrupt_unmask(struct ppu_v1_reg *ppu, unsigned int mask)
258 {
259 fwk_assert(ppu != NULL);
260
261 ppu->IMR &= ~(mask & PPU_V1_IMR_MASK);
262 }
263
ppu_v1_additional_interrupt_unmask(struct ppu_v1_reg * ppu,unsigned int mask)264 void ppu_v1_additional_interrupt_unmask(struct ppu_v1_reg *ppu,
265 unsigned int mask)
266 {
267 fwk_assert(ppu != NULL);
268
269 ppu->AIMR &= ~(mask & PPU_V1_AIMR_MASK);
270 }
271
ppu_v1_is_additional_interrupt_pending(struct ppu_v1_reg * ppu,unsigned int mask)272 bool ppu_v1_is_additional_interrupt_pending(struct ppu_v1_reg *ppu,
273 unsigned int mask)
274 {
275 return (ppu->AISR & (mask & PPU_V1_AISR_MASK)) != 0;
276 }
277
ppu_v1_ack_interrupt(struct ppu_v1_reg * ppu,unsigned int mask)278 void ppu_v1_ack_interrupt(struct ppu_v1_reg *ppu, unsigned int mask)
279 {
280 fwk_assert(ppu != NULL);
281
282 ppu->ISR &= mask & PPU_V1_IMR_MASK;
283 }
284
ppu_v1_ack_additional_interrupt(struct ppu_v1_reg * ppu,unsigned int mask)285 void ppu_v1_ack_additional_interrupt(struct ppu_v1_reg *ppu, unsigned int mask)
286 {
287 fwk_assert(ppu != NULL);
288
289 ppu->AISR &= mask & PPU_V1_AIMR_MASK;
290 }
291
ppu_v1_set_input_edge_sensitivity(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode,enum ppu_v1_edge_sensitivity edge_sensitivity)292 void ppu_v1_set_input_edge_sensitivity(struct ppu_v1_reg *ppu,
293 enum ppu_v1_mode ppu_mode, enum ppu_v1_edge_sensitivity edge_sensitivity)
294 {
295 fwk_assert(ppu != NULL);
296 fwk_assert(ppu_mode < PPU_V1_MODE_COUNT);
297 fwk_assert((edge_sensitivity & ~PPU_V1_EDGE_SENSITIVITY_MASK) == 0);
298
299 /* Clear current settings */
300 ppu->IESR &= ~(PPU_V1_EDGE_SENSITIVITY_MASK <<
301 (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY));
302
303 /* Update settings */
304 ppu->IESR |= edge_sensitivity <<
305 (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY);
306 }
307
ppu_v1_get_input_edge_sensitivity(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode)308 enum ppu_v1_edge_sensitivity ppu_v1_get_input_edge_sensitivity(
309 struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode)
310 {
311 fwk_assert(ppu != NULL);
312 fwk_assert(ppu_mode < PPU_V1_MODE_COUNT);
313
314 return (enum ppu_v1_edge_sensitivity)(
315 (ppu->IESR >> (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY)) &
316 PPU_V1_EDGE_SENSITIVITY_MASK);
317 }
318
ppu_v1_ack_power_active_edge_interrupt(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode)319 void ppu_v1_ack_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
320 enum ppu_v1_mode ppu_mode)
321 {
322 ppu->ISR = 1 << (ppu_mode + PPU_V1_ISR_ACTIVE_EDGE_POS);
323 }
324
ppu_v1_is_power_active_edge_interrupt(struct ppu_v1_reg * ppu,enum ppu_v1_mode ppu_mode)325 bool ppu_v1_is_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
326 enum ppu_v1_mode ppu_mode)
327 {
328 return ppu->ISR & (1 << (ppu_mode + PPU_V1_ISR_ACTIVE_EDGE_POS));
329 }
330
ppu_v1_set_op_active_edge_sensitivity(struct ppu_v1_reg * ppu,enum ppu_v1_op_devactive op_devactive,enum ppu_v1_edge_sensitivity edge_sensitivity)331 void ppu_v1_set_op_active_edge_sensitivity(struct ppu_v1_reg *ppu,
332 enum ppu_v1_op_devactive op_devactive,
333 enum ppu_v1_edge_sensitivity edge_sensitivity)
334 {
335 fwk_assert(ppu != NULL);
336 fwk_assert(op_devactive < PPU_V1_OP_DEVACTIVE_COUNT);
337 fwk_assert((edge_sensitivity & ~PPU_V1_EDGE_SENSITIVITY_MASK) == 0);
338
339 /* Clear current settings */
340 ppu->OPSR &= ~(PPU_V1_EDGE_SENSITIVITY_MASK <<
341 (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY));
342
343 /* Update settings */
344 ppu->OPSR |= edge_sensitivity <<
345 (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY);
346 }
347
ppu_v1_get_op_active_edge_sensitivity(struct ppu_v1_reg * ppu,enum ppu_v1_op_devactive op_devactive)348 enum ppu_v1_edge_sensitivity ppu_v1_get_op_active_edge_sensitivity(
349 struct ppu_v1_reg *ppu, enum ppu_v1_op_devactive op_devactive)
350 {
351 fwk_assert(ppu != NULL);
352 fwk_assert(op_devactive < PPU_V1_OP_DEVACTIVE_COUNT);
353
354 return (enum ppu_v1_edge_sensitivity)(
355 (ppu->OPSR >> (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY)) &
356 PPU_V1_EDGE_SENSITIVITY_MASK);
357 }
358
ppu_v1_ack_op_active_edge_interrupt(struct ppu_v1_reg * ppu,enum ppu_v1_op_devactive op_devactive)359 void ppu_v1_ack_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
360 enum ppu_v1_op_devactive op_devactive)
361 {
362 ppu->ISR = 1 << (op_devactive + PPU_V1_ISR_OP_ACTIVE_EDGE_POS);
363 }
364
ppu_v1_is_op_active_edge_interrupt(struct ppu_v1_reg * ppu,enum ppu_v1_op_devactive op_devactive)365 bool ppu_v1_is_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
366 enum ppu_v1_op_devactive op_devactive)
367 {
368 return ppu->ISR & (1 << (op_devactive + PPU_V1_ISR_OP_ACTIVE_EDGE_POS));
369 }
370
ppu_v1_is_dyn_policy_min_interrupt(struct ppu_v1_reg * ppu)371 bool ppu_v1_is_dyn_policy_min_interrupt(struct ppu_v1_reg *ppu)
372 {
373 return ppu->ISR & PPU_V1_ISR_DYN_POLICY_MIN_IRQ;
374 }
375
376 /*
377 * IDR0 register
378 */
ppu_v1_get_num_opmode(struct ppu_v1_reg * ppu)379 unsigned int ppu_v1_get_num_opmode(struct ppu_v1_reg *ppu)
380 {
381 return ((ppu->IDR0 & PPU_V1_IDR0_NUM_OPMODE)
382 >> PPU_V1_IDR0_NUM_OPMODE_POS) + 1;
383 }
384
385 /*
386 * AIDR register
387 */
ppu_v1_get_arch_id(struct ppu_v1_reg * ppu)388 unsigned int ppu_v1_get_arch_id(struct ppu_v1_reg *ppu)
389 {
390 fwk_assert(ppu != NULL);
391
392 return (ppu->AIDR & (PPU_V1_AIDR_ARCH_REV_MINOR |
393 PPU_V1_AIDR_ARCH_REV_MAJOR));
394 }
395