1 /*
2  * Copyright (c) 2021 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_os_timer
8 
9 #include <limits.h>
10 
11 #include <zephyr/init.h>
12 #include <zephyr/drivers/timer/system_timer.h>
13 #include <zephyr/irq.h>
14 #include <zephyr/sys_clock.h>
15 #include <zephyr/spinlock.h>
16 #include "fsl_ostimer.h"
17 #include "fsl_power.h"
18 
19 #define CYC_PER_TICK ((uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec()	\
20 			      / (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
21 #define MAX_CYC INT_MAX
22 #define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
23 #define MIN_DELAY 1000
24 
25 #define TICKLESS IS_ENABLED(CONFIG_TICKLESS_KERNEL)
26 
27 static struct k_spinlock lock;
28 static uint64_t last_count;
29 static OSTIMER_Type *base;
30 
mcux_lpc_ostick_isr(const void * arg)31 void mcux_lpc_ostick_isr(const void *arg)
32 {
33 	ARG_UNUSED(arg);
34 
35 	k_spinlock_key_t key = k_spin_lock(&lock);
36 	uint64_t now = OSTIMER_GetCurrentTimerValue(base);
37 	uint32_t dticks = (uint32_t)((now - last_count) / CYC_PER_TICK);
38 
39 	/* Clear interrupt flag by writing 1. */
40 	base->OSEVENT_CTRL &= ~OSTIMER_OSEVENT_CTRL_OSTIMER_INTENA_MASK;
41 
42 	last_count += dticks * CYC_PER_TICK;
43 
44 	if (!TICKLESS) {
45 		uint64_t next = last_count + CYC_PER_TICK;
46 
47 		if ((int64_t)(next - now) < MIN_DELAY) {
48 			next += CYC_PER_TICK;
49 		}
50 		OSTIMER_SetMatchValue(base, next, NULL);
51 	}
52 
53 	k_spin_unlock(&lock, key);
54 	sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1);
55 }
56 
sys_clock_set_timeout(int32_t ticks,bool idle)57 void sys_clock_set_timeout(int32_t ticks, bool idle)
58 {
59 	ARG_UNUSED(idle);
60 
61 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
62 		/* Only for tickless kernel system */
63 		return;
64 	}
65 
66 	ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks;
67 	ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS);
68 
69 	k_spinlock_key_t key = k_spin_lock(&lock);
70 	uint64_t now = OSTIMER_GetCurrentTimerValue(base);
71 	uint32_t adj, cyc = ticks * CYC_PER_TICK;
72 
73 	/* Round up to next tick boundary. */
74 	adj = (uint32_t)(now - last_count) + (CYC_PER_TICK - 1);
75 	if (cyc <= MAX_CYC - adj) {
76 		cyc += adj;
77 	} else {
78 		cyc = MAX_CYC;
79 	}
80 	cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
81 
82 	if ((int32_t)(cyc + last_count - now) < MIN_DELAY) {
83 		cyc += CYC_PER_TICK;
84 	}
85 
86 	OSTIMER_SetMatchValue(base, cyc + last_count, NULL);
87 
88 	k_spin_unlock(&lock, key);
89 }
90 
sys_clock_elapsed(void)91 uint32_t sys_clock_elapsed(void)
92 {
93 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
94 		/* Always return 0 for tickful kernel system */
95 		return 0;
96 	}
97 
98 	k_spinlock_key_t key = k_spin_lock(&lock);
99 	uint32_t ret = ((uint32_t)OSTIMER_GetCurrentTimerValue(base) -
100 					(uint32_t)last_count) / CYC_PER_TICK;
101 
102 	k_spin_unlock(&lock, key);
103 	return ret;
104 }
105 
sys_clock_cycle_get_32(void)106 uint32_t sys_clock_cycle_get_32(void)
107 {
108 	return (uint32_t)OSTIMER_GetCurrentTimerValue(base);
109 }
110 
sys_clock_cycle_get_64(void)111 uint64_t sys_clock_cycle_get_64(void)
112 {
113 	return OSTIMER_GetCurrentTimerValue(base);
114 }
115 
sys_clock_driver_init(void)116 static int sys_clock_driver_init(void)
117 {
118 
119 	/* Configure event timer's ISR */
120 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
121 					mcux_lpc_ostick_isr, NULL, 0);
122 
123 	base = (OSTIMER_Type *)DT_INST_REG_ADDR(0);
124 
125 #if (DT_INST_PROP(0, wakeup_source))
126 	EnableDeepSleepIRQ(DT_INST_IRQN(0));
127 #endif
128 
129 	/* Initialize the OS timer, setting clock configuration. */
130 	OSTIMER_Init(base);
131 
132 	last_count = OSTIMER_GetCurrentTimerValue(base);
133 	OSTIMER_SetMatchValue(base, last_count + CYC_PER_TICK, NULL);
134 
135 	/* Enable event timer interrupt */
136 	irq_enable(DT_INST_IRQN(0));
137 
138 	return 0;
139 }
140 
141 SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
142 	 CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
143