1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include "nsi_cpu0_interrupts.h"
10 #include "irq_ctrl.h"
11 #include "nsi_tasks.h"
12 #include "nsi_hws_models_if.h"
13 
14 static uint64_t hw_counter_timer;
15 
16 static bool counter_running;
17 static uint64_t counter_value;
18 static uint64_t counter_target;
19 static uint64_t counter_period;
20 
21 /**
22  * Initialize the counter with prescaler of HW
23  */
hw_counter_init(void)24 static void hw_counter_init(void)
25 {
26 	hw_counter_timer = NSI_NEVER;
27 	counter_target = NSI_NEVER;
28 	counter_value = 0;
29 	counter_running = false;
30 	counter_period = NSI_NEVER;
31 }
32 
33 NSI_TASK(hw_counter_init, HW_INIT, 10);
34 
hw_counter_triggered(void)35 static void hw_counter_triggered(void)
36 {
37 	if (!counter_running) {
38 		hw_counter_timer = NSI_NEVER;
39 		return;
40 	}
41 
42 	hw_counter_timer = nsi_hws_get_time() + counter_period;
43 	counter_value = counter_value + 1;
44 
45 	if (counter_value == counter_target) {
46 		hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ);
47 	}
48 }
49 
50 NSI_HW_EVENT(hw_counter_timer, hw_counter_triggered, 20);
51 
52 /**
53  * Configures the counter period.
54  * The counter will be incremented every 'period' microseconds.
55  */
hw_counter_set_period(uint64_t period)56 void hw_counter_set_period(uint64_t period)
57 {
58 	counter_period = period;
59 }
60 
61 /**
62  * Starts the counter. It must be previously configured with
63  * hw_counter_set_period() and hw_counter_set_target().
64  */
hw_counter_start(void)65 void hw_counter_start(void)
66 {
67 	if (counter_running) {
68 		return;
69 	}
70 
71 	counter_running = true;
72 
73 	hw_counter_timer = nsi_hws_get_time() + counter_period;
74 	nsi_hws_find_next_event();
75 }
76 
77 /**
78  * Stops the counter at current value.
79  * On the next call to hw_counter_start, the counter will
80  * start from the value at which it was stopped.
81  */
hw_counter_stop(void)82 void hw_counter_stop(void)
83 {
84 	counter_running = false;
85 	hw_counter_timer = NSI_NEVER;
86 	nsi_hws_find_next_event();
87 }
88 
89 /**
90  * Returns the current counter value.
91  */
hw_counter_get_value(void)92 uint64_t hw_counter_get_value(void)
93 {
94 	return counter_value;
95 }
96 
97 /**
98  * Configures the counter to generate an interrupt
99  * when its count value reaches target.
100  */
hw_counter_set_target(uint64_t target)101 void hw_counter_set_target(uint64_t target)
102 {
103 	counter_target = target;
104 }
105