1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Implementation of Timer module
9 */
10
11 #include <mod_timer.h>
12
13 #include <fwk_assert.h>
14 #include <fwk_dlist.h>
15 #include <fwk_id.h>
16 #include <fwk_interrupt.h>
17 #include <fwk_list.h>
18 #include <fwk_log.h>
19 #include <fwk_macros.h>
20 #include <fwk_mm.h>
21 #include <fwk_module.h>
22 #include <fwk_module_idx.h>
23 #include <fwk_status.h>
24
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <string.h>
28
29 /* Timer device context (element) */
30 struct timer_dev_ctx {
31 /* Pointer to the device's configuration */
32 const struct mod_timer_dev_config *config;
33 /* Pointer to an API provided by the driver that controls the device */
34 struct mod_timer_driver_api *driver;
35 /* Identifier of the driver that controls the device */
36 fwk_id_t driver_dev_id;
37 /* Storage for all alarms */
38 struct alarm_sub_element_ctx *alarm_pool;
39 /* Queue of active alarms */
40 struct fwk_dlist alarms_active;
41 };
42
43 /* Alarm item context (sub-element) */
44 struct alarm_sub_element_ctx {
45 /* List node */
46 struct fwk_dlist_node node;
47 /* Time between starting this alarm and it triggering */
48 uint32_t microseconds;
49 /* Timestamp of the time this alarm will trigger */
50 uint64_t timestamp;
51 /* Pointer to the callback function */
52 void (*callback)(uintptr_t param);
53 /* Parameter of the callback function */
54 uintptr_t param;
55 /* Flag indicating if this alarm if periodic */
56 bool periodic;
57 /* Flag indicating if this alarm is in the active queue */
58 bool activated;
59 /* Flag indicating if this alarm has been bound to */
60 bool bound;
61 /* Flag indicating if this alarm is started */
62 bool started;
63 };
64
65 /* Table of timer device context structures */
66 static struct timer_dev_ctx *ctx_table;
67
68 /*
69 * Forward declarations
70 */
71
72 static void timer_isr(uintptr_t ctx_ptr);
73
74 /*
75 * Internal functions
76 */
77
_time_to_timestamp(struct timer_dev_ctx * ctx,uint32_t microseconds,uint64_t * timestamp)78 static int _time_to_timestamp(
79 struct timer_dev_ctx *ctx,
80 uint32_t microseconds,
81 uint64_t *timestamp)
82 {
83 int status;
84 uint32_t frequency;
85
86 fwk_assert(ctx != NULL);
87 fwk_assert(timestamp != NULL);
88
89 status = ctx->driver->get_frequency(ctx->driver_dev_id, &frequency);
90 if (status != FWK_SUCCESS) {
91 return status;
92 }
93
94 *timestamp = ((uint64_t)frequency * microseconds) / 1000000;
95
96 return FWK_SUCCESS;
97 }
98
_timestamp_from_now(struct timer_dev_ctx * ctx,uint32_t microseconds,uint64_t * timestamp)99 static int _timestamp_from_now(
100 struct timer_dev_ctx *ctx,
101 uint32_t microseconds,
102 uint64_t *timestamp)
103 {
104 int status;
105 uint64_t counter;
106
107 fwk_assert(ctx != NULL);
108 fwk_assert(timestamp != NULL);
109
110 status = _time_to_timestamp(ctx, microseconds, timestamp);
111 if (status != FWK_SUCCESS) {
112 return status;
113 }
114
115 status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
116 if (status != FWK_SUCCESS) {
117 return status;
118 }
119
120 *timestamp += counter;
121
122 return FWK_SUCCESS;
123 }
124
_remaining(const struct timer_dev_ctx * ctx,uint64_t timestamp,uint64_t * remaining_ticks)125 static int _remaining(
126 const struct timer_dev_ctx *ctx,
127 uint64_t timestamp,
128 uint64_t *remaining_ticks)
129 {
130 int status;
131 uint64_t counter;
132
133 fwk_assert(ctx != NULL);
134 fwk_assert(remaining_ticks != NULL);
135
136 status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
137 if (!fwk_expect(status == FWK_SUCCESS)) {
138 return status;
139 }
140
141 /* If timestamp is in the past, remaining_ticks is set to zero. */
142 if (timestamp < counter) {
143 *remaining_ticks = 0;
144 } else {
145 *remaining_ticks = timestamp - counter;
146 }
147
148 return FWK_SUCCESS;
149 }
150
_configure_timer_with_next_alarm(struct timer_dev_ctx * ctx)151 static void _configure_timer_with_next_alarm(struct timer_dev_ctx *ctx)
152 {
153 int status;
154 struct alarm_sub_element_ctx *alarm_head;
155
156 fwk_assert(ctx != NULL);
157
158 alarm_head =
159 (struct alarm_sub_element_ctx *)fwk_list_head(&ctx->alarms_active);
160 if (alarm_head != NULL) {
161 /* Configure timer device */
162 status =
163 ctx->driver->set_timer(ctx->driver_dev_id, alarm_head->timestamp);
164 if (status != FWK_SUCCESS) {
165 FWK_LOG_DEBUG("[Timer] %s @%d", __func__, __LINE__);
166 }
167
168 status = ctx->driver->enable(ctx->driver_dev_id);
169 if (status != FWK_SUCCESS) {
170 FWK_LOG_DEBUG("[Timer] %s @%d", __func__, __LINE__);
171 }
172 }
173 }
174
_insert_alarm_ctx_into_active_queue(struct timer_dev_ctx * ctx,struct alarm_sub_element_ctx * alarm_new)175 static void _insert_alarm_ctx_into_active_queue(
176 struct timer_dev_ctx *ctx,
177 struct alarm_sub_element_ctx *alarm_new)
178 {
179 struct fwk_dlist_node *alarm_node;
180 struct alarm_sub_element_ctx *alarm;
181
182 fwk_assert(ctx != NULL);
183 fwk_assert(alarm_new != NULL);
184
185 /*
186 * Search though the active queue to find the correct place to insert the
187 * new alarm item
188 */
189 alarm_node = fwk_list_head(&ctx->alarms_active);
190 alarm = FWK_LIST_GET(alarm_node, struct alarm_sub_element_ctx, node);
191
192 while ((alarm_node != NULL) && (alarm_new->timestamp > alarm->timestamp)) {
193 alarm_node = fwk_list_next(&ctx->alarms_active, alarm_node);
194 alarm = FWK_LIST_GET(alarm_node, struct alarm_sub_element_ctx, node);
195 }
196
197 /* Insert alarm_new just BEFORE the alarm that was found */
198 fwk_list_insert(&ctx->alarms_active,
199 &(alarm_new->node),
200 alarm_node);
201
202 alarm_new->activated = true;
203 }
204
205
206 /*
207 * Functions fulfilling the timer API
208 */
209
get_frequency(fwk_id_t dev_id,uint32_t * frequency)210 static int get_frequency(fwk_id_t dev_id, uint32_t *frequency)
211 {
212 struct timer_dev_ctx *ctx;
213
214 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
215
216 if (frequency == NULL) {
217 return FWK_E_PARAM;
218 }
219
220 return ctx->driver->get_frequency(ctx->driver_dev_id, frequency);
221 }
222
time_to_timestamp(fwk_id_t dev_id,uint32_t microseconds,uint64_t * timestamp)223 static int time_to_timestamp(fwk_id_t dev_id,
224 uint32_t microseconds,
225 uint64_t *timestamp)
226 {
227 struct timer_dev_ctx *ctx;
228
229 if (timestamp == NULL) {
230 return FWK_E_PARAM;
231 }
232
233 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
234
235 return _time_to_timestamp(ctx, microseconds, timestamp);
236 }
237
get_counter(fwk_id_t dev_id,uint64_t * counter)238 static int get_counter(fwk_id_t dev_id, uint64_t *counter)
239 {
240 struct timer_dev_ctx *ctx;
241
242 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
243
244 if (counter == NULL) {
245 return FWK_E_PARAM;
246 }
247
248 /* Read counter */
249 return ctx->driver->get_counter(ctx->driver_dev_id, counter);
250 }
251
delay(fwk_id_t dev_id,uint32_t microseconds)252 static int delay(fwk_id_t dev_id, uint32_t microseconds)
253 {
254 int status;
255 struct timer_dev_ctx *ctx;
256 uint64_t counter, counter_limit;
257
258 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
259
260 status = _timestamp_from_now(ctx, microseconds, &counter_limit);
261 if (status != FWK_SUCCESS) {
262 return status;
263 }
264
265 do {
266 status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
267 if (status != FWK_SUCCESS) {
268 return status;
269 }
270 } while (counter < counter_limit);
271
272 return FWK_SUCCESS;
273 }
274
wait(fwk_id_t dev_id,uint32_t microseconds,bool (* cond)(void *),void * data)275 static int wait(fwk_id_t dev_id,
276 uint32_t microseconds,
277 bool (*cond)(void*),
278 void *data)
279 {
280 struct timer_dev_ctx *ctx;
281 int status;
282 uint64_t counter, counter_limit;
283
284 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
285
286 status = _timestamp_from_now(ctx, microseconds, &counter_limit);
287 if (status != FWK_SUCCESS) {
288 return status;
289 }
290
291 while (true) {
292 if (cond(data)) {
293 return FWK_SUCCESS;
294 }
295
296 status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
297 if (status != FWK_SUCCESS) {
298 return FWK_E_DEVICE;
299 }
300
301 /*
302 * If the time to wait is over, check condition one last time.
303 */
304 if (counter > counter_limit) {
305 if (cond(data)) {
306 return FWK_SUCCESS;
307 } else {
308 return FWK_E_TIMEOUT;
309 }
310 }
311 }
312 }
313
remaining(fwk_id_t dev_id,uint64_t timestamp,uint64_t * remaining_ticks)314 static int remaining(fwk_id_t dev_id,
315 uint64_t timestamp,
316 uint64_t *remaining_ticks)
317 {
318 struct timer_dev_ctx *ctx;
319
320 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
321
322 if (remaining_ticks == NULL) {
323 return FWK_E_PARAM;
324 }
325
326 return _remaining(ctx, timestamp, remaining_ticks);
327 }
328
get_next_alarm_remaining(fwk_id_t dev_id,bool * has_alarm,uint64_t * remaining_ticks)329 static int get_next_alarm_remaining(fwk_id_t dev_id,
330 bool *has_alarm,
331 uint64_t *remaining_ticks)
332 {
333 int status, exit_status;
334 const struct timer_dev_ctx *ctx;
335 const struct alarm_sub_element_ctx *alarm_ctx;
336 const struct fwk_dlist_node *alarm_ctx_node;
337 if (has_alarm == NULL) {
338 return FWK_E_PARAM;
339 }
340
341 if (remaining_ticks == NULL) {
342 return FWK_E_PARAM;
343 }
344
345 ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
346
347 /*
348 * The timer interrupt is disabled to ensure that the alarm list is not
349 * modified while we are trying to read it below.
350 */
351 status = ctx->driver->disable(ctx->driver_dev_id);
352 if (status != FWK_SUCCESS) {
353 return FWK_E_DEVICE;
354 }
355
356 *has_alarm = !fwk_list_is_empty(&ctx->alarms_active);
357
358 if (*has_alarm) {
359 alarm_ctx_node = fwk_list_head(&ctx->alarms_active);
360 alarm_ctx =
361 FWK_LIST_GET(alarm_ctx_node, struct alarm_sub_element_ctx, node);
362
363 exit_status = _remaining(ctx, alarm_ctx->timestamp, remaining_ticks);
364 } else {
365 exit_status = FWK_E_PARAM;
366 }
367
368 status = ctx->driver->enable(ctx->driver_dev_id);
369 if (status != FWK_SUCCESS) {
370 return FWK_E_DEVICE;
371 }
372
373 return exit_status;
374 }
375
376 static const struct mod_timer_api timer_api = {
377 .get_frequency = get_frequency,
378 .time_to_timestamp = time_to_timestamp,
379 .get_counter = get_counter,
380 .delay = delay,
381 .wait = wait,
382 .remaining = remaining,
383 .get_next_alarm_remaining = get_next_alarm_remaining,
384 };
385
386 /*
387 * Functions fulfilling the alarm API
388 */
389
alarm_stop(fwk_id_t alarm_id)390 static int alarm_stop(fwk_id_t alarm_id)
391 {
392 int status;
393 struct timer_dev_ctx *ctx;
394 struct alarm_sub_element_ctx *alarm;
395 unsigned int interrupt;
396
397 fwk_assert(fwk_module_is_valid_sub_element_id(alarm_id));
398
399 ctx = &ctx_table[fwk_id_get_element_idx(alarm_id)];
400
401 status = fwk_interrupt_get_current(&interrupt);
402 switch (status) {
403 case FWK_E_STATE:
404 /* Not within an ISR */
405 break;
406
407 case FWK_SUCCESS:
408 /* Within an ISR */
409
410 if (interrupt == ctx->config->timer_irq) {
411 /*
412 * The interrupt handler is the interrupt handler for the alarm's
413 * timer
414 */
415 break;
416 }
417 /* Fall-through */
418
419 default:
420 return FWK_E_ACCESS;
421 }
422
423 alarm = &ctx->alarm_pool[fwk_id_get_sub_element_idx(alarm_id)];
424
425 /* Prevent possible data races with the timer interrupt */
426 status = ctx->driver->disable(ctx->driver_dev_id);
427 if (status != FWK_SUCCESS) {
428 return FWK_E_DEVICE;
429 }
430
431 if (!alarm->started) {
432 status = ctx->driver->enable(ctx->driver_dev_id);
433 if (status != FWK_SUCCESS) {
434 return FWK_E_DEVICE;
435 } else {
436 return FWK_E_STATE;
437 }
438 }
439
440 alarm->started = false;
441
442 if (!alarm->activated) {
443 return FWK_SUCCESS;
444 }
445
446 /*
447 * If the alarm is stopped while the interrupts are globally disabled, an
448 * interrupt may be pending because the alarm being stopped here has
449 * triggered. If the interrupt is not cleared then when the interrupts are
450 * re-enabled, the timer ISR will be executed but the alarm, cause of the
451 * interrupt, will have disappeared. To avoid that, the timer interrupt is
452 * cleared here. If the interrupt was triggered by another alarm, it will be
453 * re-triggered when the timer interrupt is re-enabled.
454 */
455 status = fwk_interrupt_clear_pending(ctx->config->timer_irq);
456 if (status != FWK_SUCCESS) {
457 return status;
458 }
459
460 fwk_list_remove(&ctx->alarms_active, (struct fwk_dlist_node *)alarm);
461 alarm->activated = false;
462
463 _configure_timer_with_next_alarm(ctx);
464
465 return FWK_SUCCESS;
466 }
467
alarm_start(fwk_id_t alarm_id,unsigned int milliseconds,enum mod_timer_alarm_type type,void (* callback)(uintptr_t param),uintptr_t param)468 static int alarm_start(fwk_id_t alarm_id,
469 unsigned int milliseconds,
470 enum mod_timer_alarm_type type,
471 void (*callback)(uintptr_t param),
472 uintptr_t param)
473 {
474 int status;
475 struct timer_dev_ctx *ctx;
476 struct alarm_sub_element_ctx *alarm;
477 unsigned int interrupt;
478
479 fwk_assert(fwk_module_is_valid_sub_element_id(alarm_id));
480
481 status = fwk_interrupt_get_current(&interrupt);
482 if (status != FWK_E_STATE) {
483 /*
484 * Could not attain call context OR this function is called from an
485 * interrupt handler.
486 */
487 return FWK_E_ACCESS;
488 }
489
490 ctx = ctx_table + fwk_id_get_element_idx(alarm_id);
491 alarm = &ctx->alarm_pool[fwk_id_get_sub_element_idx(alarm_id)];
492
493 if (alarm->started) {
494 status = alarm_stop(alarm_id);
495 if (status != FWK_SUCCESS) {
496 return status;
497 }
498 }
499
500 alarm->started = true;
501
502 /* Cap to ensure value will not overflow when stored as microseconds */
503 milliseconds = FWK_MIN(milliseconds, UINT32_MAX / 1000);
504
505 /* Populate alarm item */
506 alarm->callback = callback;
507 alarm->param = param;
508 alarm->periodic =
509 (type == MOD_TIMER_ALARM_TYPE_PERIODIC ? true : false);
510 alarm->microseconds = milliseconds * 1000;
511 status = _timestamp_from_now(ctx,
512 alarm->microseconds,
513 &alarm->timestamp);
514 if (status != FWK_SUCCESS) {
515 return status;
516 }
517
518 /* Disable timer interrupts to work with the active queue */
519 status = ctx->driver->disable(ctx->driver_dev_id);
520 if (status != FWK_SUCCESS) {
521 return FWK_E_DEVICE;
522 }
523
524 _insert_alarm_ctx_into_active_queue(ctx, alarm);
525
526 _configure_timer_with_next_alarm(ctx);
527
528 return FWK_SUCCESS;
529 }
530
531 static const struct mod_timer_alarm_api alarm_api = {
532 .start = alarm_start,
533 .stop = alarm_stop,
534 };
535
timer_isr(uintptr_t ctx_ptr)536 static void timer_isr(uintptr_t ctx_ptr)
537 {
538 int status;
539 struct alarm_sub_element_ctx *alarm;
540 struct timer_dev_ctx *ctx = (struct timer_dev_ctx *)ctx_ptr;
541 uint64_t timestamp = 0;
542
543 fwk_assert(ctx != NULL);
544
545 /* Disable timer interrupts to work with the active queue */
546 status = ctx->driver->disable(ctx->driver_dev_id);
547 if (status != FWK_SUCCESS) {
548 FWK_LOG_DEBUG("[Timer] %s @%d", __func__, __LINE__);
549 }
550
551 status = fwk_interrupt_clear_pending(ctx->config->timer_irq);
552 if (status != FWK_SUCCESS) {
553 FWK_LOG_DEBUG("[Timer] %s @%d", __func__, __LINE__);
554 }
555
556 alarm =
557 (struct alarm_sub_element_ctx *)fwk_list_pop_head(&ctx->alarms_active);
558
559 if (alarm == NULL) {
560 /* Timer interrupt triggered without any alarm in the active queue */
561 fwk_unexpected();
562 return;
563 }
564
565 alarm->activated = false;
566
567 /* Execute the callback function */
568 alarm->callback(alarm->param);
569
570 if (alarm->periodic && alarm->started) {
571 /* Put this alarm back into the active queue */
572 status = _time_to_timestamp(ctx, alarm->microseconds, ×tamp);
573
574 if (status == FWK_SUCCESS) {
575 alarm->timestamp += timestamp;
576 _insert_alarm_ctx_into_active_queue(ctx, alarm);
577 } else {
578 FWK_LOG_ERR(
579 "[Timer] Error: Periodic alarm could not be added "
580 "back into queue.");
581 }
582 }
583
584 _configure_timer_with_next_alarm(ctx);
585 }
586
587 /*
588 * Functions fulfilling the framework's module interface
589 */
590
timer_init(fwk_id_t module_id,unsigned int element_count,const void * data)591 static int timer_init(fwk_id_t module_id,
592 unsigned int element_count,
593 const void *data)
594 {
595 ctx_table = fwk_mm_calloc(element_count, sizeof(struct timer_dev_ctx));
596
597 return FWK_SUCCESS;
598 }
599
timer_device_init(fwk_id_t element_id,unsigned int alarm_count,const void * data)600 static int timer_device_init(fwk_id_t element_id, unsigned int alarm_count,
601 const void *data)
602 {
603 struct timer_dev_ctx *ctx;
604
605 fwk_assert(data != NULL);
606
607 ctx = ctx_table + fwk_id_get_element_idx(element_id);
608 ctx->config = data;
609
610 if (alarm_count > 0) {
611 ctx->alarm_pool =
612 fwk_mm_calloc(alarm_count, sizeof(struct alarm_sub_element_ctx));
613 }
614
615 return FWK_SUCCESS;
616 }
617
timer_bind(fwk_id_t id,unsigned int round)618 static int timer_bind(fwk_id_t id, unsigned int round)
619 {
620 int status;
621 struct timer_dev_ctx *ctx;
622 struct mod_timer_driver_api *driver = NULL;
623 unsigned int driver_module_idx;
624
625 /* Nothing to do after the initial round. */
626 if (round > 0) {
627 return FWK_SUCCESS;
628 }
629
630 if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
631 return FWK_SUCCESS;
632 }
633
634 ctx = ctx_table + fwk_id_get_element_idx(id);
635 ctx->driver_dev_id = ctx->config->id;
636
637 /* Bind to the driver API for the current device */
638 driver_module_idx = fwk_id_get_module_idx(ctx->driver_dev_id);
639 status = fwk_module_bind(ctx->driver_dev_id,
640 FWK_ID_API(driver_module_idx, 0),
641 &driver);
642 if (status != FWK_SUCCESS) {
643 return status;
644 }
645
646 /* Check that the driver API is completely fulfilled */
647 if (driver->enable == NULL || driver->disable == NULL ||
648 driver->get_counter == NULL || driver->get_frequency == NULL) {
649 return FWK_E_DEVICE;
650 }
651
652 ctx->driver = driver;
653
654 return FWK_SUCCESS;
655 }
656
timer_process_bind_request(fwk_id_t requester_id,fwk_id_t id,fwk_id_t api_id,const void ** api)657 static int timer_process_bind_request(fwk_id_t requester_id,
658 fwk_id_t id,
659 fwk_id_t api_id,
660 const void **api)
661 {
662 struct timer_dev_ctx *ctx;
663 struct alarm_sub_element_ctx *alarm_ctx;
664
665 if (fwk_id_is_equal(api_id, MOD_TIMER_API_ID_TIMER)) {
666 if (!fwk_module_is_valid_element_id(id)) {
667 fwk_unexpected();
668 return FWK_E_PARAM;
669 }
670
671 *api = &timer_api;
672 return FWK_SUCCESS;
673 }
674
675 /* Alarm API requested */
676
677 if (!fwk_module_is_valid_sub_element_id(id)) {
678 fwk_unexpected();
679 return FWK_E_PARAM;
680 }
681
682 ctx = ctx_table + fwk_id_get_element_idx(id);
683 alarm_ctx = &ctx->alarm_pool[fwk_id_get_sub_element_idx(id)];
684
685 if (alarm_ctx->bound) {
686 fwk_unexpected();
687 return FWK_E_STATE;
688 }
689
690 alarm_ctx->bound = true;
691
692 *api = &alarm_api;
693 return FWK_SUCCESS;
694 }
695
timer_start(fwk_id_t id)696 static int timer_start(fwk_id_t id)
697 {
698 int status;
699 struct timer_dev_ctx *ctx;
700
701 if (!fwk_module_is_valid_element_id(id)) {
702 return FWK_SUCCESS;
703 }
704
705 ctx = ctx_table + fwk_id_get_element_idx(id);
706
707 fwk_list_init(&ctx->alarms_active);
708
709 status = fwk_interrupt_set_isr_param(
710 ctx->config->timer_irq, timer_isr, (uintptr_t)ctx);
711 if (status != FWK_SUCCESS) {
712 return status;
713 }
714
715 return fwk_interrupt_enable(ctx->config->timer_irq);
716 }
717
718 /* Module descriptor */
719 const struct fwk_module module_timer = {
720 .api_count = (unsigned int)MOD_TIMER_API_COUNT,
721 .type = FWK_MODULE_TYPE_HAL,
722 .init = timer_init,
723 .element_init = timer_device_init,
724 .bind = timer_bind,
725 .process_bind_request = timer_process_bind_request,
726 .start = timer_start,
727 };
728