1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     System Power Support.
9  */
10 
11 #include <mod_power_domain.h>
12 #include <mod_system_power.h>
13 
14 #include <fwk_assert.h>
15 #include <fwk_id.h>
16 #include <fwk_interrupt.h>
17 #include <fwk_log.h>
18 #include <fwk_mm.h>
19 #include <fwk_module.h>
20 #include <fwk_module_idx.h>
21 #include <fwk_status.h>
22 
23 #include <stdbool.h>
24 #include <stdint.h>
25 
26 /* SoC wakeup composite state */
27 #define MOD_SYSTEM_POWER_SOC_WAKEUP_STATE \
28     MOD_PD_COMPOSITE_STATE( \
29         (unsigned int)MOD_PD_LEVEL_2, \
30         0U, \
31         (unsigned int)MOD_PD_STATE_ON, \
32         (unsigned int)MOD_PD_STATE_ON, \
33         (unsigned int)MOD_PD_STATE_ON)
34 
35 /* Element context */
36 struct system_power_dev_ctx {
37     /* Element configuration data pointer */
38     const struct mod_system_power_dev_config *config;
39 
40     /* Power domain driver API pointer */
41     const struct mod_pd_driver_api *sys_ppu_api;
42 };
43 
44 /* Module context */
45 struct mod_system_power_ctx {
46     /* System power element context table */
47     struct system_power_dev_ctx *dev_ctx_table;
48 
49     /* Number of elements */
50     unsigned int dev_count;
51 
52     /* Pointer to array of extended PPU power domain driver APIs */
53     const struct mod_pd_driver_api **ext_ppu_apis;
54 
55     /* Power domain module restricted API pointer */
56     const struct mod_pd_restricted_api *pd_restricted_api;
57 
58     /* Power domain module driver input API pointer */
59     const struct mod_pd_driver_input_api *pd_driver_input_api;
60 
61     /* Driver API pointer */
62     const struct mod_system_power_driver_api *driver_api;
63 
64     /* Power domain module identifier of the system power domain */
65     fwk_id_t mod_pd_system_id;
66 
67     /* Current system-level power state */
68     unsigned int state;
69 
70     /* Requested power state */
71     unsigned int requested_state;
72 
73     /* Pointer to module config */
74     const struct mod_system_power_config *config;
75 
76     /*
77      * Identifier of the power domain of the last standing core before system
78      * suspend.
79      */
80     fwk_id_t last_core_pd_id;
81 };
82 
83 static struct mod_system_power_ctx system_power_ctx;
84 
85 /*
86  * Static helpers
87  */
88 
ext_ppus_set_state(enum mod_pd_state state)89 static void ext_ppus_set_state(enum mod_pd_state state)
90 {
91     unsigned int i;
92     int status;
93 
94     for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
95         status = system_power_ctx.ext_ppu_apis[i]->set_state(
96             system_power_ctx.config->ext_ppus[i].ppu_id, state);
97         if (status != FWK_SUCCESS) {
98             FWK_LOG_DEBUG("[SYS-POW] ext-ppu%i %s @%d", i, __func__, __LINE__);
99         }
100     }
101 }
102 
ext_ppus_shutdown(enum mod_pd_system_shutdown system_shutdown)103 static void ext_ppus_shutdown(enum mod_pd_system_shutdown system_shutdown)
104 {
105     unsigned int i;
106     const struct mod_pd_driver_api *api;
107     fwk_id_t ppu_id;
108     int status;
109 
110     /* Shutdown external PPUs */
111     for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
112         api = system_power_ctx.ext_ppu_apis[i];
113         ppu_id = system_power_ctx.config->ext_ppus[i].ppu_id;
114 
115         if (api->shutdown != NULL) {
116             status = api->shutdown(ppu_id, system_shutdown);
117         } else {
118             status = api->set_state(ppu_id, MOD_PD_STATE_OFF);
119         }
120         if (status != FWK_SUCCESS) {
121             FWK_LOG_DEBUG("[SYS-POW] ext-ppu%i %s @%d", i, __func__, __LINE__);
122         }
123     }
124 }
125 
set_system_power_state(unsigned int state)126 static int set_system_power_state(unsigned int state)
127 {
128     int status;
129     unsigned int i;
130     struct system_power_dev_ctx *dev_ctx;
131     const uint8_t *sys_state_table;
132 
133     for (i = 0; i < system_power_ctx.dev_count; i++) {
134         dev_ctx = &system_power_ctx.dev_ctx_table[i];
135 
136         sys_state_table = dev_ctx->config->sys_state_table;
137 
138         status = dev_ctx->sys_ppu_api->set_state(dev_ctx->config->sys_ppu_id,
139                                                  sys_state_table[state]);
140         if (status != FWK_SUCCESS) {
141             return status;
142         }
143     }
144 
145     return FWK_SUCCESS;
146 }
147 
shutdown_system_power_ppus(enum mod_pd_system_shutdown system_shutdown)148 static int shutdown_system_power_ppus(
149     enum mod_pd_system_shutdown system_shutdown)
150 {
151     unsigned int i;
152     struct system_power_dev_ctx *dev_ctx;
153     const struct mod_pd_driver_api *api;
154     fwk_id_t ppu_id;
155     unsigned int state;
156     int status;
157 
158     for (i = 0; i < system_power_ctx.dev_count; i++) {
159         dev_ctx = &system_power_ctx.dev_ctx_table[i];
160 
161         api = dev_ctx->sys_ppu_api;
162         ppu_id = dev_ctx->config->sys_ppu_id;
163 
164         if (api->shutdown != NULL) {
165             status = api->shutdown(ppu_id, system_shutdown);
166         } else {
167             state = dev_ctx->config->sys_state_table[MOD_PD_STATE_OFF];
168 
169             status = api->set_state(ppu_id, state);
170         }
171         if (status != FWK_SUCCESS) {
172             return status;
173         }
174     }
175 
176     return FWK_SUCCESS;
177 }
178 
disable_all_irqs(void)179 static int disable_all_irqs(void)
180 {
181     int status = FWK_SUCCESS;
182 
183     if (system_power_ctx.config->soc_wakeup_irq != FWK_INTERRUPT_NONE) {
184         status = fwk_interrupt_disable(system_power_ctx.config->soc_wakeup_irq);
185         if (status != FWK_SUCCESS) {
186             return FWK_E_DEVICE;
187         }
188     }
189 
190     if (system_power_ctx.driver_api->platform_interrupts != NULL) {
191         status = system_power_ctx.driver_api->platform_interrupts(
192             MOD_SYSTEM_POWER_PLATFORM_INTERRUPT_CMD_DISABLE);
193         if (status != FWK_SUCCESS) {
194             status = FWK_E_DEVICE;
195         }
196     }
197 
198     return status;
199 }
200 
shutdown(fwk_id_t pd_id,enum mod_pd_system_shutdown system_shutdown)201 static int shutdown(
202     fwk_id_t pd_id,
203     enum mod_pd_system_shutdown system_shutdown)
204 {
205     int status;
206 
207     status = disable_all_irqs();
208     if (status != FWK_SUCCESS) {
209         return status;
210     }
211 
212     /* Shutdown external PPUs */
213     ext_ppus_shutdown(system_shutdown);
214 
215     system_power_ctx.requested_state = (unsigned int)MOD_PD_STATE_OFF;
216 
217     /* Shutdown system PPUs */
218     status = shutdown_system_power_ppus(system_shutdown);
219     if (status != FWK_SUCCESS) {
220         return status;
221     }
222 
223     return FWK_SUCCESS;
224 }
225 
226 /*
227  * Functions fulfilling the Power Domain module's driver API
228  */
229 
system_power_set_state(fwk_id_t pd_id,unsigned int state)230 static int system_power_set_state(fwk_id_t pd_id, unsigned int state)
231 {
232     int status;
233     unsigned int soc_wakeup_irq;
234 
235     if (!fwk_expect(state < MOD_SYSTEM_POWER_POWER_STATE_COUNT)) {
236         return FWK_E_PARAM;
237     }
238 
239     soc_wakeup_irq = system_power_ctx.config->soc_wakeup_irq;
240 
241     system_power_ctx.requested_state = state;
242 
243     switch (state) {
244     case (unsigned int)MOD_PD_STATE_ON:
245         status = disable_all_irqs();
246         if (status != FWK_SUCCESS) {
247             return status;
248         }
249 
250         status = set_system_power_state(state);
251         if (status != FWK_SUCCESS) {
252             return status;
253         }
254 
255         ext_ppus_set_state(MOD_PD_STATE_ON);
256 
257         break;
258 
259     case (unsigned int)MOD_SYSTEM_POWER_POWER_STATE_SLEEP0:
260         ext_ppus_set_state(MOD_PD_STATE_OFF);
261 
262         status = fwk_interrupt_clear_pending(soc_wakeup_irq);
263         if (status != FWK_SUCCESS) {
264             return status;
265         }
266 
267         if (system_power_ctx.driver_api->platform_interrupts != NULL) {
268             status =
269                 system_power_ctx.driver_api->platform_interrupts(
270                     MOD_SYSTEM_POWER_PLATFORM_INTERRUPT_CMD_CLEAR_PENDING);
271             if (status != FWK_SUCCESS) {
272                 return FWK_E_DEVICE;
273             }
274         }
275 
276         status = set_system_power_state(state);
277         if (status != FWK_SUCCESS) {
278             return status;
279         }
280 
281         /* Store the identifier of the power domain of the last standing core */
282         status = system_power_ctx.pd_driver_input_api->get_last_core_pd_id(
283             &system_power_ctx.last_core_pd_id);
284         if (status != FWK_SUCCESS) {
285             return status;
286         }
287 
288         status = fwk_interrupt_enable(soc_wakeup_irq);
289         if (status != FWK_SUCCESS) {
290             return status;
291         }
292 
293         if (system_power_ctx.driver_api->platform_interrupts != NULL) {
294             status = system_power_ctx.driver_api->platform_interrupts(
295                 MOD_SYSTEM_POWER_PLATFORM_INTERRUPT_CMD_ENABLE);
296             if (status != FWK_SUCCESS) {
297                 return FWK_E_DEVICE;
298             }
299         }
300 
301         break;
302 
303     case (unsigned int)MOD_PD_STATE_OFF:
304         status = disable_all_irqs();
305         if (status != FWK_SUCCESS) {
306             return status;
307         }
308 
309         ext_ppus_set_state(MOD_PD_STATE_OFF);
310 
311         status = set_system_power_state(state);
312         if (status != FWK_SUCCESS) {
313             return status;
314         }
315 
316         break;
317 
318     default:
319         return FWK_E_SUPPORT;
320     }
321 
322     return FWK_SUCCESS;
323 }
324 
system_power_get_state(fwk_id_t pd_id,unsigned int * state)325 static int system_power_get_state(fwk_id_t pd_id, unsigned int *state)
326 {
327     *state = system_power_ctx.state;
328 
329     return FWK_SUCCESS;
330 }
331 
system_power_reset(fwk_id_t pd_id)332 static int system_power_reset(fwk_id_t pd_id)
333 {
334     return FWK_E_SUPPORT;
335 }
336 
system_power_shutdown(fwk_id_t pd_id,enum mod_pd_system_shutdown system_shutdown)337 static int system_power_shutdown(fwk_id_t pd_id,
338                                 enum mod_pd_system_shutdown system_shutdown)
339 {
340     int status;
341 
342     status = shutdown(pd_id, system_shutdown);
343     if (status != FWK_SUCCESS) {
344         return status;
345     }
346 
347     return system_power_ctx.driver_api->system_shutdown(system_shutdown);
348 }
349 
soc_wakeup_handler(void)350 static void soc_wakeup_handler(void)
351 {
352     int status;
353     uint32_t state = MOD_SYSTEM_POWER_SOC_WAKEUP_STATE;
354 
355     status = disable_all_irqs();
356     if (status != FWK_SUCCESS) {
357         fwk_trap();
358     }
359 
360     status = system_power_ctx.pd_restricted_api->set_state(
361         system_power_ctx.last_core_pd_id, false, state);
362     fwk_check(status == FWK_SUCCESS);
363 }
364 
365 static const struct mod_pd_driver_api system_power_power_domain_driver_api = {
366     .set_state = system_power_set_state,
367     .get_state = system_power_get_state,
368     .reset = system_power_reset,
369     .shutdown = system_power_shutdown
370 };
371 
372 /*
373  * Functions fulfilling the Power Domain module's driver input API
374  */
375 
system_power_report_power_state_transition(fwk_id_t dev_id,unsigned int state)376 static int system_power_report_power_state_transition(fwk_id_t dev_id,
377     unsigned int state)
378 {
379     static unsigned int sys_ppu_transition_count = 0;
380 
381     sys_ppu_transition_count++;
382 
383     if (sys_ppu_transition_count < system_power_ctx.dev_count) {
384         return FWK_SUCCESS;
385     }
386 
387     system_power_ctx.state = system_power_ctx.requested_state;
388 
389     sys_ppu_transition_count = 0;
390 
391     return system_power_ctx.pd_driver_input_api->report_power_state_transition(
392         system_power_ctx.mod_pd_system_id, system_power_ctx.state);
393 }
394 
395 static const struct mod_pd_driver_input_api
396     system_power_power_domain_driver_input_api = {
397     .report_power_state_transition = system_power_report_power_state_transition
398 };
399 
400 /*
401  * Functions fulfilling the framework's module interface
402  */
403 
system_power_mod_init(fwk_id_t module_id,unsigned int element_count,const void * data)404 static int system_power_mod_init(fwk_id_t module_id,
405                                 unsigned int element_count,
406                                 const void *data)
407 {
408     const struct mod_system_power_config *config;
409 
410     fwk_assert(data != NULL);
411     fwk_check(element_count > 0);
412 
413     system_power_ctx.config = config = data;
414     system_power_ctx.mod_pd_system_id = FWK_ID_NONE;
415     system_power_ctx.dev_count = element_count;
416 
417     system_power_ctx.dev_ctx_table =
418         fwk_mm_calloc(element_count, sizeof(struct system_power_dev_ctx));
419 
420     if (system_power_ctx.config->ext_ppus_count > 0) {
421         system_power_ctx.ext_ppu_apis = fwk_mm_calloc(
422             system_power_ctx.config->ext_ppus_count,
423             sizeof(system_power_ctx.ext_ppu_apis[0]));
424     }
425 
426     if (system_power_ctx.config->soc_wakeup_irq != FWK_INTERRUPT_NONE) {
427         return fwk_interrupt_set_isr(system_power_ctx.config->soc_wakeup_irq,
428                                      soc_wakeup_handler);
429     }
430 
431     return FWK_SUCCESS;
432 }
433 
system_power_mod_element_init(fwk_id_t element_id,unsigned int unused,const void * data)434 static int system_power_mod_element_init(fwk_id_t element_id,
435                                          unsigned int unused,
436                                          const void *data)
437 {
438     struct system_power_dev_ctx *dev_ctx;
439 
440     fwk_assert(data != NULL);
441 
442     dev_ctx =
443         system_power_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
444 
445     dev_ctx->config = data;
446 
447     /* Ensure a system state table is provided */
448     if (dev_ctx->config->sys_state_table == NULL) {
449         return FWK_E_DATA;
450     }
451 
452     return FWK_SUCCESS;
453 }
454 
system_power_bind(fwk_id_t id,unsigned int round)455 static int system_power_bind(fwk_id_t id, unsigned int round)
456 {
457     int status;
458     unsigned int i;
459     const struct mod_system_power_config *config;
460     struct system_power_dev_ctx *dev_ctx;
461 
462     if (round == 1) {
463         if (!fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
464             return FWK_SUCCESS;
465         }
466 
467         /*
468          * During the first round of binding, the power domain module should
469          * have bound to the power domain driver API provided by the present
470          * module. Bind back to the power domain driver input API provided by
471          * the system_power_ctx.mod_pd_system_id power domain module element to
472          * report power state transitions of the system power domains.
473          */
474         return fwk_module_bind(
475             system_power_ctx.mod_pd_system_id,
476             mod_pd_api_id_driver_input,
477             &system_power_ctx.pd_driver_input_api);
478     }
479 
480     if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
481 
482         config = system_power_ctx.config;
483 
484         for (i = 0; i < config->ext_ppus_count; i++) {
485             status = fwk_module_bind(
486                 config->ext_ppus[i].ppu_id,
487                 config->ext_ppus[i].api_id,
488                 &system_power_ctx.ext_ppu_apis[i]);
489             if (status != FWK_SUCCESS) {
490                 return status;
491             }
492         }
493 
494         status = fwk_module_bind(config->driver_id,
495             config->driver_api_id,
496             &system_power_ctx.driver_api);
497         if (status != FWK_SUCCESS) {
498             return status;
499         }
500 
501         return fwk_module_bind(
502             fwk_module_id_power_domain,
503             mod_pd_api_id_restricted,
504             &system_power_ctx.pd_restricted_api);
505     }
506 
507     dev_ctx = system_power_ctx.dev_ctx_table + fwk_id_get_element_idx(id);
508 
509     return fwk_module_bind(dev_ctx->config->sys_ppu_id,
510                            dev_ctx->config->api_id,
511                            &dev_ctx->sys_ppu_api);
512 }
513 
system_power_process_bind_request(fwk_id_t requester_id,fwk_id_t pd_id,fwk_id_t api_id,const void ** api)514 static int system_power_process_bind_request(fwk_id_t requester_id,
515                                              fwk_id_t pd_id,
516                                              fwk_id_t api_id,
517                                              const void **api)
518 {
519     unsigned int dev_idx;
520     struct system_power_dev_ctx *dev_ctx;
521 
522     if (fwk_id_is_equal(api_id, mod_system_power_api_id_pd_driver)) {
523         if (!fwk_id_is_equal(
524                 fwk_id_build_module_id(requester_id),
525                 fwk_module_id_power_domain)) {
526             return FWK_E_ACCESS;
527         }
528 
529         *api = &system_power_power_domain_driver_api;
530          system_power_ctx.mod_pd_system_id = requester_id;
531     } else {
532         for (dev_idx = 0; dev_idx < system_power_ctx.dev_count; dev_idx++) {
533             dev_ctx = &system_power_ctx.dev_ctx_table[dev_idx];
534 
535             /*
536              * If requester_id refers to a system PPU configured by any one of
537              * our elements, break when dev_idx reaches that element.
538              */
539             if (fwk_id_is_equal(requester_id, dev_ctx->config->sys_ppu_id)) {
540                 break;
541             }
542         }
543         if (dev_idx >= system_power_ctx.dev_count) {
544             /* Requester_id does not refer to any configured system PPU */
545              return FWK_E_ACCESS;
546         }
547 
548         *api = &system_power_power_domain_driver_input_api;
549     }
550 
551     return FWK_SUCCESS;
552 }
553 
system_power_start(fwk_id_t id)554 static int system_power_start(fwk_id_t id)
555 {
556     int status;
557 
558     if (system_power_ctx.driver_api->platform_interrupts != NULL) {
559         status = system_power_ctx.driver_api->platform_interrupts(
560             MOD_SYSTEM_POWER_PLATFORM_INTERRUPT_CMD_INIT);
561         if (status != FWK_SUCCESS) {
562             return status;
563         }
564     }
565 
566     /* Configure initial power state */
567     system_power_ctx.state =
568         (unsigned int)system_power_ctx.config->initial_system_power_state;
569 
570     return FWK_SUCCESS;
571 }
572 
573 const struct fwk_module module_system_power = {
574     .type = FWK_MODULE_TYPE_DRIVER,
575     .api_count = (unsigned int)MOD_SYSTEM_POWER_API_COUNT,
576     .init = system_power_mod_init,
577     .element_init = system_power_mod_element_init,
578     .bind = system_power_bind,
579     .start = system_power_start,
580     .process_bind_request = system_power_process_bind_request,
581 };
582