1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <mod_mock_psu.h>
9 #include <mod_psu.h>
10 #include <mod_timer.h>
11 
12 #include <fwk_assert.h>
13 #include <fwk_id.h>
14 #include <fwk_mm.h>
15 #include <fwk_module.h>
16 #include <fwk_module_idx.h>
17 #include <fwk_status.h>
18 
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 
23 enum mod_mock_psu_request {
24     MOD_MOCK_PSU_REQUEST_NONE,
25 
26     MOD_MOCK_PSU_REQUEST_GET_ENABLED,
27     MOD_MOCK_PSU_REQUEST_SET_ENABLED,
28     MOD_MOCK_PSU_REQUEST_GET_VOLTAGE,
29     MOD_MOCK_PSU_REQUEST_SET_VOLTAGE,
30 };
31 
32 struct mod_mock_psu_operation {
33     enum mod_mock_psu_request request;
34 
35     union {
36         bool enabled;
37         uint32_t voltage;
38     };
39 };
40 
41 static struct mod_mock_psu_mod_ctx {
42     struct mod_mock_psu_element_ctx {
43         struct {
44             bool enabled;
45             uint32_t voltage;
46         } state;
47 
48         struct mod_mock_psu_operation op;
49 
50         struct {
51             const struct mod_timer_alarm_api *alarm;
52             const struct mod_psu_driver_response_api *hal;
53         } apis;
54     } *elements;
55 } mod_mock_psu_ctx;
56 
mod_mock_psu_get_ctx(fwk_id_t element_id)57 static struct mod_mock_psu_element_ctx *mod_mock_psu_get_ctx(
58     fwk_id_t element_id)
59 {
60     unsigned int element_idx = fwk_id_get_element_idx(element_id);
61 
62     return &mod_mock_psu_ctx.elements[element_idx];
63 }
64 
65 
mod_mock_psu_get_cfg_ctx(fwk_id_t element_id,const struct mod_mock_psu_element_cfg ** cfg,struct mod_mock_psu_element_ctx ** ctx)66 static int mod_mock_psu_get_cfg_ctx(
67     fwk_id_t element_id,
68     const struct mod_mock_psu_element_cfg **cfg,
69     struct mod_mock_psu_element_ctx **ctx)
70 {
71     if (fwk_id_get_module_idx(element_id) != FWK_MODULE_IDX_MOCK_PSU) {
72         return FWK_E_PARAM;
73     }
74 
75     if (ctx != NULL) {
76         *ctx = mod_mock_psu_get_ctx(element_id);
77     }
78 
79     if (cfg != NULL) {
80         *cfg = fwk_module_get_data(element_id);
81     }
82 
83     return FWK_SUCCESS;
84 }
85 
mod_mock_psu_alarm_callback(uintptr_t element_idx)86 static void mod_mock_psu_alarm_callback(uintptr_t element_idx)
87 {
88     int status;
89 
90     fwk_id_t element_id = fwk_id_build_element_id(
91         fwk_module_id_mock_psu, element_idx);
92 
93     const struct mod_mock_psu_element_cfg *cfg;
94     struct mod_mock_psu_element_ctx *ctx;
95     struct mod_psu_driver_response response;
96 
97     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
98     if (!fwk_expect(status == FWK_SUCCESS)) {
99         return;
100     }
101 
102     response = (struct mod_psu_driver_response){
103         .status = FWK_SUCCESS,
104     };
105 
106     switch (ctx->op.request) {
107     case MOD_MOCK_PSU_REQUEST_GET_ENABLED:
108         response.enabled = ctx->state.enabled;
109 
110         break;
111 
112     case MOD_MOCK_PSU_REQUEST_SET_ENABLED:
113         ctx->state.enabled = ctx->op.enabled;
114 
115         break;
116 
117     case MOD_MOCK_PSU_REQUEST_GET_VOLTAGE:
118         response.voltage = ctx->state.voltage;
119 
120         break;
121 
122     case MOD_MOCK_PSU_REQUEST_SET_VOLTAGE:
123         ctx->state.voltage = ctx->op.voltage;
124 
125         break;
126 
127     default:
128         fwk_unreachable();
129     }
130 
131     ctx->op.request = MOD_MOCK_PSU_REQUEST_NONE;
132 
133     ctx->apis.hal->respond(cfg->async_response_id, response);
134 }
135 
mod_mock_psu_trigger(fwk_id_t element_id,struct mod_mock_psu_operation op)136 static int mod_mock_psu_trigger(
137     fwk_id_t element_id,
138     struct mod_mock_psu_operation op)
139 {
140     static const unsigned int ASYNC_DELAY = 1 /* ms */;
141 
142     int status;
143 
144     const struct mod_mock_psu_element_cfg *cfg;
145     struct mod_mock_psu_element_ctx *ctx;
146 
147     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
148     if (status != FWK_SUCCESS) {
149         goto exit;
150     }
151 
152     status = ctx->apis.alarm->start(
153         cfg->async_alarm_id,
154         ASYNC_DELAY,
155         MOD_TIMER_ALARM_TYPE_ONCE,
156         mod_mock_psu_alarm_callback,
157         fwk_id_get_element_idx(element_id));
158 
159     if (status == FWK_SUCCESS) {
160         status = FWK_PENDING;
161 
162         ctx->op = op;
163     } else {
164         status = FWK_E_HANDLER;
165     }
166 
167 exit:
168     return status;
169 }
170 
mod_mock_psu_get_enabled(fwk_id_t element_id,bool * enabled)171 static int mod_mock_psu_get_enabled(
172     fwk_id_t element_id,
173     bool *enabled)
174 {
175     int status;
176 
177     const struct mod_mock_psu_element_cfg *cfg;
178     struct mod_mock_psu_element_ctx *ctx;
179 
180     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
181     if (status != FWK_SUCCESS) {
182         goto exit;
183     }
184 
185     if (ctx->apis.alarm == NULL) {
186         *enabled = ctx->state.enabled;
187 
188         goto exit;
189     }
190 
191     if (ctx->op.request == MOD_MOCK_PSU_REQUEST_NONE) {
192         struct mod_mock_psu_operation op = {
193             .request = MOD_MOCK_PSU_REQUEST_GET_ENABLED,
194         };
195 
196         status = mod_mock_psu_trigger(element_id, op);
197     } else {
198         status = FWK_E_BUSY;
199     }
200 
201 exit:
202     return status;
203 }
204 
mod_mock_psu_set_enabled(fwk_id_t element_id,bool enabled)205 static int mod_mock_psu_set_enabled(
206     fwk_id_t element_id,
207     bool enabled)
208 {
209     int status;
210 
211     const struct mod_mock_psu_element_cfg *cfg;
212     struct mod_mock_psu_element_ctx *ctx;
213 
214     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
215     if (status != FWK_SUCCESS) {
216         goto exit;
217     }
218 
219     if (ctx->apis.alarm == NULL) {
220         ctx->state.enabled = enabled;
221 
222         goto exit;
223     }
224 
225     if (ctx->op.request == MOD_MOCK_PSU_REQUEST_NONE) {
226         struct mod_mock_psu_operation op = {
227             .request = MOD_MOCK_PSU_REQUEST_SET_ENABLED,
228             .enabled = enabled,
229         };
230 
231         status = mod_mock_psu_trigger(element_id, op);
232     } else {
233         status = FWK_E_BUSY;
234     }
235 
236 exit:
237     return status;
238 }
239 
mod_mock_psu_get_voltage(fwk_id_t element_id,uint32_t * voltage)240 static int mod_mock_psu_get_voltage(fwk_id_t element_id, uint32_t *voltage)
241 {
242     int status;
243 
244     const struct mod_mock_psu_element_cfg *cfg;
245     struct mod_mock_psu_element_ctx *ctx;
246 
247     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
248     if (status != FWK_SUCCESS) {
249         goto exit;
250     }
251 
252     if (ctx->apis.alarm == NULL) {
253         *voltage = ctx->state.voltage;
254 
255         goto exit;
256     }
257 
258     if (ctx->op.request == MOD_MOCK_PSU_REQUEST_NONE) {
259         struct mod_mock_psu_operation op = {
260             .request = MOD_MOCK_PSU_REQUEST_GET_VOLTAGE,
261         };
262 
263         status = mod_mock_psu_trigger(element_id, op);
264     } else {
265         status = FWK_E_BUSY;
266     }
267 
268 exit:
269     return status;
270 }
271 
mod_mock_psu_set_voltage(fwk_id_t element_id,uint32_t voltage)272 static int mod_mock_psu_set_voltage(fwk_id_t element_id, uint32_t voltage)
273 {
274     int status;
275 
276     const struct mod_mock_psu_element_cfg *cfg;
277     struct mod_mock_psu_element_ctx *ctx;
278 
279     status = mod_mock_psu_get_cfg_ctx(element_id, &cfg, &ctx);
280     if (status != FWK_SUCCESS) {
281         goto exit;
282     }
283 
284     if (ctx->apis.alarm == NULL) {
285         ctx->state.voltage = voltage;
286 
287         goto exit;
288     }
289 
290     if (ctx->op.request == MOD_MOCK_PSU_REQUEST_NONE) {
291         struct mod_mock_psu_operation op = {
292             .request = MOD_MOCK_PSU_REQUEST_SET_VOLTAGE,
293             .voltage = voltage,
294         };
295 
296         status = mod_mock_psu_trigger(element_id, op);
297     } else {
298         status = FWK_E_BUSY;
299     }
300 
301 exit:
302     return status;
303 }
304 
305 static const struct mod_psu_driver_api mod_mock_psu_driver_api = {
306     .get_enabled = mod_mock_psu_get_enabled,
307     .set_enabled = mod_mock_psu_set_enabled,
308     .get_voltage = mod_mock_psu_get_voltage,
309     .set_voltage = mod_mock_psu_set_voltage,
310 };
311 
mod_mock_psu_init(fwk_id_t module_id,unsigned int element_count,const void * data)312 static int mod_mock_psu_init(
313     fwk_id_t module_id,
314     unsigned int element_count,
315     const void *data)
316 {
317     fwk_check(data == NULL);
318 
319     mod_mock_psu_ctx.elements =
320         fwk_mm_calloc(element_count, sizeof(mod_mock_psu_ctx.elements[0]));
321 
322     return FWK_SUCCESS;
323 }
324 
mod_mock_psu_element_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)325 static int mod_mock_psu_element_init(
326     fwk_id_t element_id,
327     unsigned int sub_element_count,
328     const void *data)
329 {
330     struct mod_mock_psu_element_ctx *ctx;
331     const struct mod_mock_psu_element_cfg *cfg = data;
332 
333     fwk_check(sub_element_count == 0);
334 
335     ctx = mod_mock_psu_get_ctx(element_id);
336 
337     *ctx = (struct mod_mock_psu_element_ctx) {
338         .state = {
339             .enabled = cfg->default_enabled,
340             .voltage = cfg->default_voltage,
341         },
342 
343         .op = {
344             .request = MOD_MOCK_PSU_REQUEST_NONE,
345         },
346     };
347 
348     return FWK_SUCCESS;
349 }
350 
mod_mock_psu_bind(fwk_id_t id,unsigned int round)351 static int mod_mock_psu_bind(fwk_id_t id, unsigned int round)
352 {
353     int status = FWK_SUCCESS;
354 
355     if ((round == 0) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
356         const struct mod_mock_psu_element_cfg *cfg = fwk_module_get_data(id);
357 
358         if (!fwk_id_is_equal(cfg->async_alarm_id, FWK_ID_NONE)) {
359             struct mod_mock_psu_element_ctx *ctx =
360                 mod_mock_psu_get_ctx(id);
361 
362             status = fwk_module_bind(
363                 cfg->async_alarm_id,
364                 cfg->async_alarm_api_id,
365                 &ctx->apis.alarm);
366             if (status != FWK_SUCCESS) {
367                 goto exit;
368             }
369 
370             status = fwk_module_bind(
371                 cfg->async_response_id,
372                 cfg->async_response_api_id,
373                 &ctx->apis.hal);
374         }
375     }
376 
377 exit:
378     return status;
379 }
380 
mod_mock_psu_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)381 static int mod_mock_psu_process_bind_request(
382     fwk_id_t source_id,
383     fwk_id_t target_id,
384     fwk_id_t api_id,
385     const void **api)
386 {
387     const struct mod_mock_psu_element_cfg *cfg;
388 
389     if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
390         return FWK_E_ACCESS;
391     }
392 
393     if (!fwk_id_is_equal(api_id, mod_mock_psu_api_id_driver)) {
394         return FWK_E_ACCESS;
395     }
396 
397     cfg = fwk_module_get_data(target_id);
398 
399     if (!fwk_id_is_equal(cfg->async_alarm_id, FWK_ID_NONE)) {
400         /*
401          * If we are configured to respond asynchronously, the only entity we
402          * accept bind requests from is the entity that we have configured to be
403          * our designated driver respondee.
404          */
405         if (!fwk_id_is_equal(source_id, cfg->async_response_id)) {
406             return FWK_E_ACCESS;
407         }
408     }
409 
410     *api = &mod_mock_psu_driver_api;
411 
412     return FWK_SUCCESS;
413 }
414 
415 const struct fwk_module module_mock_psu = {
416     .type = FWK_MODULE_TYPE_DRIVER,
417 
418     .init = mod_mock_psu_init,
419     .element_init = mod_mock_psu_element_init,
420     .bind = mod_mock_psu_bind,
421 
422     .api_count = (unsigned int)MOD_MOCK_PSU_API_IDX_COUNT,
423     .process_bind_request = mod_mock_psu_process_bind_request,
424 };
425