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             &params);
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