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_psu.h>
9 
10 #include <fwk_assert.h>
11 #include <fwk_core.h>
12 #include <fwk_event.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 #include <fwk_string.h>
19 
20 enum mod_psu_state {
21     MOD_PSU_STATE_IDLE,
22     MOD_PSU_STATE_BUSY,
23 };
24 
25 struct mod_psu_operation {
26     enum mod_psu_state state;
27 
28     unsigned int cookie;
29 };
30 
31 enum mod_psu_impl_event_idx {
32     MOD_PSU_IMPL_EVENT_IDX_RESPONSE = MOD_PSU_EVENT_IDX_COUNT,
33 
34     MOD_PSU_IMPL_EVENT_IDX_COUNT,
35 };
36 
37 static const fwk_id_t mod_psu_impl_event_id_response =
38     FWK_ID_EVENT_INIT(FWK_MODULE_IDX_PSU, MOD_PSU_IMPL_EVENT_IDX_RESPONSE);
39 
40 static struct mod_psu_mod_ctx {
41     struct mod_psu_element_ctx {
42         const struct mod_psu_driver_api *driver;
43 
44         struct mod_psu_operation op;
45     } *elements;
46 } mod_psu_ctx;
47 
mod_psu_get_element_ctx(fwk_id_t element_id)48 static struct mod_psu_element_ctx *mod_psu_get_element_ctx(fwk_id_t element_id)
49 {
50     unsigned int element_idx = fwk_id_get_element_idx(element_id);
51 
52     return &mod_psu_ctx.elements[element_idx];
53 }
54 
mod_psu_get_cfg_ctx(fwk_id_t element_id,const struct mod_psu_element_cfg ** cfg,struct mod_psu_element_ctx ** ctx)55 static int mod_psu_get_cfg_ctx(
56     fwk_id_t element_id,
57     const struct mod_psu_element_cfg **cfg,
58     struct mod_psu_element_ctx **ctx)
59 {
60     if (fwk_id_get_module_idx(element_id) != FWK_MODULE_IDX_PSU) {
61         return FWK_E_PARAM;
62     }
63 
64     if (ctx != NULL) {
65         *ctx = mod_psu_get_element_ctx(element_id);
66     }
67 
68     if (cfg != NULL) {
69         *cfg = fwk_module_get_data(element_id);
70     }
71 
72     return FWK_SUCCESS;
73 }
74 
mod_psu_get_enabled(fwk_id_t element_id,bool * enabled)75 static int mod_psu_get_enabled(fwk_id_t element_id, bool *enabled)
76 {
77     int status;
78 
79     const struct mod_psu_element_cfg *cfg;
80     struct mod_psu_element_ctx *ctx;
81 
82     status = mod_psu_get_cfg_ctx(element_id, &cfg, &ctx);
83     if (status != FWK_SUCCESS) {
84         goto exit;
85     }
86 
87     if (ctx->op.state != MOD_PSU_STATE_IDLE) {
88         status = FWK_E_BUSY;
89 
90         goto exit;
91     }
92 
93     status = ctx->driver->get_enabled(cfg->driver_id, enabled);
94     if (status == FWK_PENDING) {
95         struct fwk_event_light request = {
96             .id = mod_psu_event_id_get_enabled,
97             .target_id = element_id,
98 
99             .response_requested = true,
100         };
101 
102         status = fwk_put_event(&request);
103         if (status == FWK_SUCCESS) {
104             ctx->op.state = MOD_PSU_STATE_BUSY;
105 
106             status = FWK_PENDING;
107         } else {
108             status = FWK_E_STATE;
109         }
110     } else if (status != FWK_SUCCESS) {
111         status = FWK_E_HANDLER;
112     }
113 
114 exit:
115     return status;
116 }
117 
mod_psu_set_enabled(fwk_id_t element_id,bool enabled)118 static int mod_psu_set_enabled(fwk_id_t element_id, bool enabled)
119 {
120     int status;
121 
122     const struct mod_psu_element_cfg *cfg;
123     struct mod_psu_element_ctx *ctx;
124 
125     status = mod_psu_get_cfg_ctx(element_id, &cfg, &ctx);
126     if (status != FWK_SUCCESS) {
127         goto exit;
128     }
129 
130     if (ctx->op.state != MOD_PSU_STATE_IDLE) {
131         status = FWK_E_BUSY;
132 
133         goto exit;
134     }
135 
136     status = ctx->driver->set_enabled(cfg->driver_id, enabled);
137     if (status == FWK_PENDING) {
138         struct fwk_event_light request = {
139             .id = mod_psu_event_id_set_enabled,
140             .target_id = element_id,
141 
142             .response_requested = true,
143         };
144 
145         status = fwk_put_event(&request);
146         if (status == FWK_SUCCESS) {
147             ctx->op.state = MOD_PSU_STATE_BUSY;
148 
149             status = FWK_PENDING;
150         } else {
151             status = FWK_E_STATE;
152         }
153     } else if (status != FWK_SUCCESS) {
154         status = FWK_E_HANDLER;
155     }
156 
157 exit:
158     return status;
159 }
160 
mod_psu_get_voltage(fwk_id_t element_id,uint32_t * voltage)161 static int mod_psu_get_voltage(fwk_id_t element_id, uint32_t *voltage)
162 {
163     int status;
164 
165     const struct mod_psu_element_cfg *cfg;
166     struct mod_psu_element_ctx *ctx;
167 
168     status = mod_psu_get_cfg_ctx(element_id, &cfg, &ctx);
169     if (status != FWK_SUCCESS) {
170         goto exit;
171     }
172 
173     if (ctx->op.state != MOD_PSU_STATE_IDLE) {
174         status = FWK_E_BUSY;
175 
176         goto exit;
177     }
178 
179     status = ctx->driver->get_voltage(cfg->driver_id, voltage);
180     if (status == FWK_PENDING) {
181         struct fwk_event_light request = {
182             .id = mod_psu_event_id_get_voltage,
183             .target_id = element_id,
184 
185             .response_requested = true,
186         };
187 
188         status = fwk_put_event(&request);
189         if (status == FWK_SUCCESS) {
190             ctx->op.state = MOD_PSU_STATE_BUSY;
191 
192             status = FWK_PENDING;
193         } else {
194             status = FWK_E_STATE;
195         }
196     } else if (status != FWK_SUCCESS) {
197         status = FWK_E_HANDLER;
198     }
199 
200 exit:
201     return status;
202 }
203 
mod_psu_set_voltage(fwk_id_t element_id,uint32_t voltage)204 static int mod_psu_set_voltage(fwk_id_t element_id, uint32_t voltage)
205 {
206     int status;
207 
208     const struct mod_psu_element_cfg *cfg;
209     struct mod_psu_element_ctx *ctx;
210 
211     status = mod_psu_get_cfg_ctx(element_id, &cfg, &ctx);
212     if (status != FWK_SUCCESS) {
213         goto exit;
214     }
215 
216     if (ctx->op.state != MOD_PSU_STATE_IDLE) {
217         status = FWK_E_BUSY;
218 
219         goto exit;
220     }
221 
222     status = ctx->driver->set_voltage(cfg->driver_id, voltage);
223     if (status == FWK_PENDING) {
224         struct fwk_event_light request = {
225             .id = mod_psu_event_id_set_voltage,
226             .target_id = element_id,
227 
228             .response_requested = true,
229         };
230 
231         status = fwk_put_event(&request);
232         if (status == FWK_SUCCESS) {
233             ctx->op.state = MOD_PSU_STATE_BUSY;
234 
235             status = FWK_PENDING;
236         } else {
237             status = FWK_E_STATE;
238         }
239     } else if (status != FWK_SUCCESS) {
240         status = FWK_E_HANDLER;
241     }
242 
243 exit:
244     return status;
245 }
246 
247 static const struct mod_psu_device_api psu_device_api = {
248     .get_enabled = mod_psu_get_enabled,
249     .set_enabled = mod_psu_set_enabled,
250     .get_voltage = mod_psu_get_voltage,
251     .set_voltage = mod_psu_set_voltage,
252 };
253 
mod_psu_respond(fwk_id_t element_id,struct mod_psu_driver_response response)254 static void mod_psu_respond(
255     fwk_id_t element_id,
256     struct mod_psu_driver_response response)
257 {
258     int status;
259 
260     const struct mod_psu_element_cfg *cfg;
261     struct mod_psu_element_ctx *ctx;
262 
263     struct fwk_event event;
264 
265     status = mod_psu_get_cfg_ctx(element_id, &cfg, &ctx);
266     if (!fwk_expect(status == FWK_SUCCESS)) {
267         return;
268     }
269 
270     event = (struct fwk_event){
271         .id = mod_psu_impl_event_id_response,
272 
273         .source_id = cfg->driver_id,
274         .target_id = element_id,
275     };
276 
277     fwk_str_memcpy(event.params, &response, sizeof(response));
278 
279     status = fwk_put_event(&event);
280     if (!fwk_expect(status == FWK_SUCCESS)) {
281         ctx->op.state = MOD_PSU_STATE_IDLE;
282     }
283 }
284 
285 static const struct mod_psu_driver_response_api psu_driver_response_api = {
286     .respond = mod_psu_respond,
287 };
288 
mod_psu_init(fwk_id_t module_id,unsigned int element_count,const void * data)289 static int mod_psu_init(
290     fwk_id_t module_id,
291     unsigned int element_count,
292     const void *data)
293 {
294     fwk_check(data == NULL);
295 
296     mod_psu_ctx.elements =
297         fwk_mm_calloc(element_count, sizeof(mod_psu_ctx.elements[0]));
298 
299     return FWK_SUCCESS;
300 }
301 
mod_psu_element_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)302 static int mod_psu_element_init(
303     fwk_id_t element_id,
304     unsigned int sub_element_count,
305     const void *data)
306 {
307     struct mod_psu_element_ctx *ctx;
308 
309     fwk_check(sub_element_count == 0);
310 
311     ctx = mod_psu_get_element_ctx(element_id);
312 
313     *ctx = (struct mod_psu_element_ctx){
314         .op = (struct mod_psu_operation){
315             .state = MOD_PSU_STATE_IDLE,
316         },
317     };
318 
319     return FWK_SUCCESS;
320 }
321 
mod_psu_bind_element(fwk_id_t element_id,unsigned int round)322 static int mod_psu_bind_element(fwk_id_t element_id, unsigned int round)
323 {
324     int status = FWK_SUCCESS;
325 
326     const struct mod_psu_element_ctx *ctx;
327     const struct mod_psu_element_cfg *cfg;
328 
329     if (round > 0) {
330         goto exit;
331     }
332 
333     ctx = mod_psu_get_element_ctx(element_id);
334     cfg = fwk_module_get_data(element_id);
335 
336     status = fwk_module_bind(cfg->driver_id, cfg->driver_api_id, &ctx->driver);
337     if (status != FWK_SUCCESS) {
338         goto exit;
339     }
340 
341     if ((ctx->driver->set_enabled == NULL) ||
342         (ctx->driver->get_enabled == NULL) ||
343         (ctx->driver->set_voltage == NULL) ||
344         (ctx->driver->get_voltage == NULL)) {
345         status = FWK_E_PANIC;
346     }
347 
348 exit:
349     return status;
350 }
351 
mod_psu_bind(fwk_id_t id,unsigned int round)352 static int mod_psu_bind(fwk_id_t id, unsigned int round)
353 {
354     if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
355         return mod_psu_bind_element(id, round);
356     }
357 
358     return FWK_SUCCESS;
359 }
360 
mod_psu_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)361 static int mod_psu_process_bind_request(
362     fwk_id_t source_id,
363     fwk_id_t target_id,
364     fwk_id_t api_id,
365     const void **api)
366 {
367     enum mod_psu_api_idx event_id_type;
368 
369     if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
370         return FWK_E_PARAM;
371     }
372 
373     event_id_type = (enum mod_psu_api_idx)fwk_id_get_api_idx(api_id);
374 
375     switch (event_id_type) {
376     case MOD_PSU_API_IDX_DEVICE:
377         *api = &psu_device_api;
378 
379         break;
380 
381     case MOD_PSU_API_IDX_DRIVER_RESPONSE:
382         *api = &psu_driver_response_api;
383 
384         break;
385 
386     default:
387         return FWK_E_PARAM;
388     }
389 
390     return FWK_SUCCESS;
391 }
392 
mod_psu_process_event(const struct fwk_event * event,struct fwk_event * resp_event)393 static int mod_psu_process_event(
394     const struct fwk_event *event,
395     struct fwk_event *resp_event)
396 {
397     int status = FWK_SUCCESS;
398 
399     struct fwk_event hal_event;
400 
401     enum mod_psu_event_idx hal_event_id_type;
402 
403     const struct mod_psu_driver_response *params =
404         (struct mod_psu_driver_response *)event->params;
405     struct mod_psu_response *resp_params =
406         (struct mod_psu_response *)resp_event->params;
407     struct mod_psu_response *hal_params =
408         (struct mod_psu_response *)&hal_event.params;
409 
410     const struct mod_psu_element_cfg *cfg;
411     struct mod_psu_element_ctx *ctx;
412 
413     resp_params->status =
414         mod_psu_get_cfg_ctx(event->target_id, &cfg, &ctx);
415     if (resp_params->status != FWK_SUCCESS) {
416         goto exit;
417     }
418 
419     switch (fwk_id_get_event_idx(event->id)) {
420     case (unsigned int)MOD_PSU_EVENT_IDX_GET_ENABLED:
421     case (unsigned int)MOD_PSU_EVENT_IDX_GET_VOLTAGE:
422     case (unsigned int)MOD_PSU_EVENT_IDX_SET_ENABLED:
423     case (unsigned int)MOD_PSU_EVENT_IDX_SET_VOLTAGE:
424         ctx->op.cookie = event->cookie;
425 
426         resp_event->is_delayed_response = true;
427 
428         break;
429 
430     case (unsigned int)MOD_PSU_IMPL_EVENT_IDX_RESPONSE:
431         ctx->op.state = MOD_PSU_STATE_IDLE;
432 
433         status = fwk_get_delayed_response(
434             event->target_id, ctx->op.cookie, &hal_event);
435         if (status != FWK_SUCCESS) {
436             return status;
437         }
438 
439         *hal_params = (struct mod_psu_response){
440             .status = params->status,
441         };
442 
443         hal_event_id_type =
444             (enum mod_psu_event_idx)fwk_id_get_event_idx(hal_event.id);
445 
446         switch (hal_event_id_type) {
447         case MOD_PSU_EVENT_IDX_GET_ENABLED:
448             hal_params->enabled = params->enabled;
449 
450             break;
451 
452         case MOD_PSU_EVENT_IDX_GET_VOLTAGE:
453             hal_params->voltage = params->voltage;
454 
455             break;
456 
457         default:
458             break;
459         }
460 
461         status = fwk_put_event(&hal_event);
462 
463         break;
464 
465     default:
466         fwk_unreachable();
467     }
468 
469 exit:
470     return status;
471 }
472 
473 const struct fwk_module module_psu = {
474     .type = FWK_MODULE_TYPE_HAL,
475 
476     .init = mod_psu_init,
477     .element_init = mod_psu_element_init,
478     .bind = mod_psu_bind,
479 
480     .api_count = (unsigned int)MOD_PSU_API_IDX_COUNT,
481     .process_bind_request = mod_psu_process_bind_request,
482 
483     .event_count = (unsigned int)MOD_PSU_IMPL_EVENT_IDX_COUNT,
484     .process_event = mod_psu_process_event,
485 };
486