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 static uint64_t counter_wrap;
21 
22 /**
23  * Initialize the counter with prescaler of HW
24  */
hw_counter_init(void)25 static void hw_counter_init(void)
26 {
27 	hw_counter_timer = NSI_NEVER;
28 	counter_target = NSI_NEVER;
29 	counter_value = 0;
30 	counter_running = false;
31 	counter_period = NSI_NEVER;
32 	counter_wrap = NSI_NEVER;
33 }
34 
35 NSI_TASK(hw_counter_init, HW_INIT, 10);
36 
hw_counter_triggered(void)37 static void hw_counter_triggered(void)
38 {
39 	if (!counter_running) {
40 		hw_counter_timer = NSI_NEVER;
41 		return;
42 	}
43 
44 	hw_counter_timer = nsi_hws_get_time() + counter_period;
45 	counter_value = (counter_value + 1) % counter_wrap;
46 
47 	if (counter_value == counter_target) {
48 		hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ);
49 	}
50 }
51 
52 NSI_HW_EVENT(hw_counter_timer, hw_counter_triggered, 20);
53 
54 /**
55  * Configures the counter period.
56  * The counter will be incremented every 'period' microseconds.
57  */
hw_counter_set_period(uint64_t period)58 void hw_counter_set_period(uint64_t period)
59 {
60 	counter_period = period;
61 }
62 
63 /*
64  * Set the count value at which the counter will wrap
65  * The counter will count up to  (counter_wrap-1), i.e.:
66  * 0, 1, 2,.., (counter_wrap - 1), 0
67  */
hw_counter_set_wrap_value(uint64_t wrap_value)68 void hw_counter_set_wrap_value(uint64_t wrap_value)
69 {
70 	counter_wrap = wrap_value;
71 }
72 
73 /**
74  * Starts the counter. It must be previously configured with
75  * hw_counter_set_period() and hw_counter_set_target().
76  */
hw_counter_start(void)77 void hw_counter_start(void)
78 {
79 	if (counter_running) {
80 		return;
81 	}
82 
83 	counter_running = true;
84 
85 	hw_counter_timer = nsi_hws_get_time() + counter_period;
86 	nsi_hws_find_next_event();
87 }
88 
89 /**
90  * Stops the counter at current value.
91  * On the next call to hw_counter_start, the counter will
92  * start from the value at which it was stopped.
93  */
hw_counter_stop(void)94 void hw_counter_stop(void)
95 {
96 	counter_running = false;
97 	hw_counter_timer = NSI_NEVER;
98 	nsi_hws_find_next_event();
99 }
100 
hw_counter_is_started(void)101 bool hw_counter_is_started(void)
102 {
103 	return counter_running;
104 }
105 
106 /**
107  * Returns the current counter value.
108  */
hw_counter_get_value(void)109 uint64_t hw_counter_get_value(void)
110 {
111 	return counter_value;
112 }
113 
114 /**
115  * Resets the counter value.
116  */
hw_counter_reset(void)117 void hw_counter_reset(void)
118 {
119 	counter_value = 0;
120 }
121 
122 /**
123  * Configures the counter to generate an interrupt
124  * when its count value reaches target.
125  */
hw_counter_set_target(uint64_t target)126 void hw_counter_set_target(uint64_t target)
127 {
128 	counter_target = target;
129 }
130