1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2022-2023, Linaro Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <fwk_module.h>
9 #include <fwk_module_idx.h>
10 #include <fwk_assert.h>
11 #include <fwk_log.h>
12 #include <fwk_mm.h>
13 #include <mod_power_domain.h>
14 #include <mod_mock_ppu.h>
15 #ifdef BUILD_HAS_MOD_SYSTEM_POWER
16 #include <mod_system_power.h>
17 #endif
18 
19 /* Power domain context */
20 struct mock_ppu_pd_ctx {
21     /* Power domain configuration data */
22     const struct mod_mock_ppu_pd_config *config;
23 
24     /* PPU registers */
25     uint32_t *ppu;
26 
27     /* Identifier of the entity bound to the power domain driver API */
28     fwk_id_t bound_id;
29 
30     /* Power module driver input API */
31     struct mod_pd_driver_input_api *pd_driver_input_api;
32 };
33 
34 /* Module context */
35 struct mock_ppu_ctx {
36     /* Table of the power domain contexts */
37     struct mock_ppu_pd_ctx *pd_ctx_table;
38 };
39 
40 /*
41  * Internal variables
42  */
43 
44 static struct mock_ppu_ctx mock_ppu_ctx;
45 
46 /*
47  * Power domain driver interface
48  */
pd_set_state(fwk_id_t pd_id,unsigned int state)49 static int pd_set_state(fwk_id_t pd_id, unsigned int state)
50 {
51     int status;
52     struct mock_ppu_pd_ctx *pd_ctx;
53 
54     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
55 
56     fwk_assert(pd_ctx->pd_driver_input_api != NULL);
57 
58     switch (state) {
59     case MOD_PD_STATE_ON:
60         *pd_ctx->ppu = MOD_PD_STATE_ON;
61         status = pd_ctx->pd_driver_input_api->report_power_state_transition(
62             pd_ctx->bound_id, MOD_PD_STATE_ON);
63         assert(status == FWK_SUCCESS);
64         break;
65 
66     case MOD_PD_STATE_OFF:
67         *pd_ctx->ppu = MOD_PD_STATE_OFF;
68         status = pd_ctx->pd_driver_input_api->report_power_state_transition(
69             pd_ctx->bound_id, MOD_PD_STATE_OFF);
70         assert(status == FWK_SUCCESS);
71         break;
72 
73     default:
74         FWK_LOG_ERR("[PD] Requested power state (%i) is not supported.\n", state);
75         return FWK_E_PARAM;
76     }
77 
78     if (status != FWK_SUCCESS)
79         return FWK_E_PANIC;
80 
81     return FWK_SUCCESS;
82 }
83 
pd_get_state(fwk_id_t pd_id,unsigned int * state)84 static int pd_get_state(fwk_id_t pd_id, unsigned int *state)
85 {
86     struct mock_ppu_pd_ctx *pd_ctx;
87 
88     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
89 
90     *state = *pd_ctx->ppu;
91 
92     return FWK_SUCCESS;
93 }
94 
pd_reset(fwk_id_t pd_id)95 static int pd_reset(fwk_id_t pd_id)
96 {
97     struct mock_ppu_pd_ctx *pd_ctx;
98 
99     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
100 
101     *pd_ctx->ppu = MOD_PD_STATE_ON;
102 
103     return FWK_SUCCESS;
104 }
105 
ppu_v0_prepare_core_for_system_suspend(fwk_id_t core_pd_id)106 static int ppu_v0_prepare_core_for_system_suspend(fwk_id_t core_pd_id)
107 {
108     return FWK_SUCCESS;
109 }
110 
111 static const struct mod_pd_driver_api pd_driver = {
112     .set_state = pd_set_state,
113     .get_state = pd_get_state,
114     .reset = pd_reset,
115     .prepare_core_for_system_suspend = ppu_v0_prepare_core_for_system_suspend,
116 };
117 
118 /*
119  * Framework handlers
120  */
121 
mock_ppu_mod_init(fwk_id_t module_id,unsigned int pd_count,const void * unused)122 static int mock_ppu_mod_init(fwk_id_t module_id, unsigned int pd_count,
123                            const void *unused)
124 {
125     mock_ppu_ctx.pd_ctx_table = fwk_mm_calloc(pd_count,
126                                             sizeof(struct mock_ppu_pd_ctx));
127     if (mock_ppu_ctx.pd_ctx_table == NULL)
128         return FWK_E_NOMEM;
129 
130     return FWK_SUCCESS;
131 }
132 
mock_ppu_pd_init(fwk_id_t pd_id,unsigned int unused,const void * data)133 static int mock_ppu_pd_init(fwk_id_t pd_id, unsigned int unused, const void *data)
134 {
135     const struct mod_mock_ppu_pd_config *config = data;
136     struct mock_ppu_pd_ctx *pd_ctx;
137 
138     if (config->pd_type >= MOD_PD_TYPE_COUNT)
139         return FWK_E_DATA;
140 
141     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
142     pd_ctx->config = config;
143     pd_ctx->ppu = (uint32_t *)(config->ppu.reg_base);
144     pd_ctx->bound_id = FWK_ID_NONE;
145 
146     switch (config->pd_type) {
147     case MOD_PD_TYPE_DEVICE:
148     case MOD_PD_TYPE_DEVICE_DEBUG:
149     case MOD_PD_TYPE_SYSTEM:
150         if (config->default_power_on)
151             *pd_ctx->ppu = MOD_PD_STATE_ON;
152 
153         return FWK_SUCCESS;
154 
155     default:
156         return FWK_E_SUPPORT;
157     }
158 }
159 
mock_ppu_bind(fwk_id_t id,unsigned int round)160 static int mock_ppu_bind(fwk_id_t id, unsigned int round)
161 {
162     struct mock_ppu_pd_ctx *pd_ctx;
163 
164     /* Nothing to do during the first round of calls where the power module
165        will bind to the power domains of this module. */
166     if (round == 0)
167         return FWK_SUCCESS;
168 
169     /* In the case of the module, nothing to do */
170     if (fwk_module_is_valid_module_id(id)) {
171         return FWK_SUCCESS;
172     }
173 
174     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(id);
175 
176     if (fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE))
177         return FWK_SUCCESS;
178 
179     switch (fwk_id_get_module_idx(pd_ctx->bound_id)) {
180     #ifdef BUILD_HAS_MOD_POWER_DOMAIN
181     case FWK_MODULE_IDX_POWER_DOMAIN:
182         return fwk_module_bind(pd_ctx->bound_id,
183                                mod_pd_api_id_driver_input,
184                                &pd_ctx->pd_driver_input_api);
185         break;
186     #endif
187 
188     #ifdef BUILD_HAS_MOD_SYSTEM_POWER
189     case FWK_MODULE_IDX_SYSTEM_POWER:
190         return fwk_module_bind(pd_ctx->bound_id,
191                                mod_system_power_api_id_pd_driver_input,
192                                &pd_ctx->pd_driver_input_api);
193         break;
194     #endif
195 
196     default:
197         assert(false);
198         return FWK_E_SUPPORT;
199     }
200 }
201 
mock_ppu_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t not_used,const void ** api)202 static int mock_ppu_process_bind_request(fwk_id_t source_id,
203                                        fwk_id_t target_id, fwk_id_t not_used,
204                                        const void **api)
205 {
206     struct mock_ppu_pd_ctx *pd_ctx;
207 
208     pd_ctx = mock_ppu_ctx.pd_ctx_table + fwk_id_get_element_idx(target_id);
209 
210     switch (pd_ctx->config->pd_type) {
211     case MOD_PD_TYPE_SYSTEM:
212         if (!fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE)) {
213             assert(false);
214             return FWK_E_ACCESS;
215         }
216     /* Fallthrough */
217 
218     case MOD_PD_TYPE_DEVICE:
219     case MOD_PD_TYPE_DEVICE_DEBUG:
220         #ifdef BUILD_HAS_MOD_POWER_DOMAIN
221         if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_POWER_DOMAIN) {
222             pd_ctx->bound_id = source_id;
223             *api = &pd_driver;
224             break;
225         }
226         #endif
227         #ifdef BUILD_HAS_MOD_SYSTEM_POWER
228         if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_SYSTEM_POWER) {
229             pd_ctx->bound_id = source_id;
230             *api = &pd_driver;
231             break;
232         }
233         #endif
234         assert(false);
235         return FWK_E_ACCESS;
236 
237     default:
238         (void)pd_driver;
239         return FWK_E_SUPPORT;
240     }
241 
242     return FWK_SUCCESS;
243 }
244 
245 const struct fwk_module module_mock_ppu = {
246     .type = FWK_MODULE_TYPE_DRIVER,
247     .api_count = 1,
248     .init = mock_ppu_mod_init,
249     .element_init = mock_ppu_pd_init,
250     .bind = mock_ppu_bind,
251     .process_bind_request = mock_ppu_process_bind_request,
252 };
253