1 /*
2  * Copyright (c) 2017 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * This is a model of a fake HW device which is used by the k_busy_wait() function
10  * replacement. It is a timer which will awake the embedded CPU even if interrupts are
11  * locked (it does not have an associated irq vector, it simply awakes the CPU)
12  *
13  * N instances of this timer are present, one for each embedded CPU.
14  */
15 
16 #include "bs_types.h"
17 #include "irq_ctrl.h"
18 #include "nsi_hw_scheduler.h"
19 #include "nsi_tasks.h"
20 #include "nsi_hws_models_if.h"
21 #include "NHW_common_types.h"
22 #include "NHW_config.h"
23 
24 struct ftimer_status_t {
25   bs_time_t event_time;
26 };
27 
28 static bs_time_t Timer_fake_timer = TIME_NEVER;
29 /* Mapping of peripheral instance to {int controller instance, int number} */
30 static struct nhw_irq_mapping nhw_faketimer_irq_map[NHW_FAKE_TIMER_TOTAL_INST] = NHW_FAKE_TIMER_INT_MAP;
31 static struct ftimer_status_t ftimer_st[NHW_FAKE_TIMER_TOTAL_INST];
32 
33 /*
34  * Initialize all fake timer instances for this SOC
35  */
nhw_fake_timer_init(void)36 static void nhw_fake_timer_init(void)
37 {
38   for (int i = 0; i < NHW_FAKE_TIMER_TOTAL_INST ; i++ ) {
39     ftimer_st[i].event_time = TIME_NEVER;
40   }
41 }
42 
43 NSI_TASK(nhw_fake_timer_init, HW_INIT, 10);
44 
nhw_fake_timer_update_main_timer(void)45 static void nhw_fake_timer_update_main_timer(void)
46 {
47   Timer_fake_timer = ftimer_st[0].event_time;
48 
49 #if (NHW_FAKE_TIMER_TOTAL_INST > 1)
50   for (int i = 1; i < NHW_FAKE_TIMER_TOTAL_INST ; i++ ) {
51     if (ftimer_st[i].event_time < Timer_fake_timer){
52       Timer_fake_timer = ftimer_st[i].event_time;
53     }
54   }
55 #endif
56 
57   nsi_hws_find_next_event();
58 }
59 
60 /**
61  * The timer HW will awake the CPU (without an interrupt) at least when <time>
62  * comes (it may awake it earlier)
63  *
64  * If there was a previous request for an earlier time, the old one will prevail
65  *
66  * This is meant for k_busy_wait() like functionality
67  */
nhw_fake_timer_wake_in_time(unsigned int inst,bs_time_t time)68 void nhw_fake_timer_wake_in_time(unsigned int inst, bs_time_t time)
69 {
70   if (ftimer_st[inst].event_time > time) {
71     ftimer_st[inst].event_time = time;
72     nhw_fake_timer_update_main_timer();
73   }
74 }
75 
nhw_fake_timer_triggered(void)76 static void nhw_fake_timer_triggered(void)
77 {
78   for (int i = 0; i < NHW_FAKE_TIMER_TOTAL_INST ; i++ ) {
79     if (Timer_fake_timer >= ftimer_st[i].event_time) {
80       ftimer_st[i].event_time = TIME_NEVER;
81       hw_irq_ctrl_set_irq(nhw_faketimer_irq_map[i].cntl_inst,
82                           PHONY_HARD_IRQ);
83     }
84   }
85 
86   nhw_fake_timer_update_main_timer();
87 }
88 
89 NSI_HW_EVENT(Timer_fake_timer, nhw_fake_timer_triggered, 0 /* Purposely the first */);
90