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