1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/init.h>
7 #include <zephyr/drivers/timer/system_timer.h>
8 #include <zephyr/sys_clock.h>
9 #include <zephyr/spinlock.h>
10 #include <zephyr/irq.h>
11 
12 #define TIMER_IRQ UTIL_CAT(XCHAL_TIMER,		\
13 			   UTIL_CAT(CONFIG_XTENSA_TIMER_ID, _INTERRUPT))
14 
15 #define CYC_PER_TICK (sys_clock_hw_cycles_per_sec()	\
16 		      / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
17 #define MAX_CYC 0xffffffffu
18 #define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
19 #define MIN_DELAY 1000
20 
21 static struct k_spinlock lock;
22 static unsigned int last_count;
23 
24 #if defined(CONFIG_TEST)
25 const int32_t z_sys_timer_irq_for_test = UTIL_CAT(XCHAL_TIMER,
26 					 UTIL_CAT(CONFIG_XTENSA_TIMER_ID, _INTERRUPT));
27 #endif
28 
set_ccompare(uint32_t val)29 static void set_ccompare(uint32_t val)
30 {
31 	__asm__ volatile ("wsr.CCOMPARE" STRINGIFY(CONFIG_XTENSA_TIMER_ID) " %0"
32 			  :: "r"(val));
33 }
34 
ccount(void)35 static uint32_t ccount(void)
36 {
37 	uint32_t val;
38 
39 	__asm__ volatile ("rsr.CCOUNT %0" : "=r"(val));
40 	return val;
41 }
42 
ccompare_isr(const void * arg)43 static void ccompare_isr(const void *arg)
44 {
45 	ARG_UNUSED(arg);
46 
47 	k_spinlock_key_t key = k_spin_lock(&lock);
48 	uint32_t curr = ccount();
49 	uint32_t dticks = (curr - last_count) / CYC_PER_TICK;
50 
51 	last_count += dticks * CYC_PER_TICK;
52 
53 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
54 		uint32_t next = last_count + CYC_PER_TICK;
55 
56 		if ((int32_t)(next - curr) < MIN_DELAY) {
57 			next += CYC_PER_TICK;
58 		}
59 		set_ccompare(next);
60 	}
61 
62 	k_spin_unlock(&lock, key);
63 	sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1);
64 }
65 
sys_clock_set_timeout(int32_t ticks,bool idle)66 void sys_clock_set_timeout(int32_t ticks, bool idle)
67 {
68 	ARG_UNUSED(idle);
69 
70 #if defined(CONFIG_TICKLESS_KERNEL)
71 	ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks;
72 	ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS);
73 
74 	k_spinlock_key_t key = k_spin_lock(&lock);
75 	uint32_t curr = ccount(), cyc, adj;
76 
77 	/* Round up to next tick boundary */
78 	cyc = ticks * CYC_PER_TICK;
79 	adj = (curr - last_count) + (CYC_PER_TICK - 1);
80 	if (cyc <= MAX_CYC - adj) {
81 		cyc += adj;
82 	} else {
83 		cyc = MAX_CYC;
84 	}
85 	cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
86 	cyc += last_count;
87 
88 	if ((cyc - curr) < MIN_DELAY) {
89 		cyc += CYC_PER_TICK;
90 	}
91 
92 	set_ccompare(cyc);
93 	k_spin_unlock(&lock, key);
94 #endif
95 }
96 
sys_clock_elapsed(void)97 uint32_t sys_clock_elapsed(void)
98 {
99 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
100 		return 0;
101 	}
102 
103 	k_spinlock_key_t key = k_spin_lock(&lock);
104 	uint32_t ret = (ccount() - last_count) / CYC_PER_TICK;
105 
106 	k_spin_unlock(&lock, key);
107 	return ret;
108 }
109 
sys_clock_cycle_get_32(void)110 uint32_t sys_clock_cycle_get_32(void)
111 {
112 	return ccount();
113 }
114 
115 #ifdef CONFIG_SMP
smp_timer_init(void)116 void smp_timer_init(void)
117 {
118 	set_ccompare(ccount() + CYC_PER_TICK);
119 	irq_enable(TIMER_IRQ);
120 }
121 #endif
122 
sys_clock_driver_init(void)123 static int sys_clock_driver_init(void)
124 {
125 
126 	IRQ_CONNECT(TIMER_IRQ, 0, ccompare_isr, 0, 0);
127 	set_ccompare(ccount() + CYC_PER_TICK);
128 	irq_enable(TIMER_IRQ);
129 	return 0;
130 }
131 
132 SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
133 	 CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
134