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