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