1 /*
2 * Copyright (c) 2021 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_npcx_itim_timer
8
9 /**
10 * @file
11 * @brief Nuvoton NPCX kernel device driver for "system clock driver" interface
12 *
13 * This file contains a kernel device driver implemented by the internal
14 * 64/32-bit timers in Nuvoton NPCX series. Via these two kinds of timers, the
15 * driver provides an standard "system clock driver" interface.
16 *
17 * It includes:
18 * - A system timer based on an ITIM64 (Internal 64-bit timer) instance, clocked
19 * by APB2 which freq is the same as CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC.
20 * - Provide a 64-bit cycles reading and ticks computation based on it.
21 * - Its prescaler is set to 1 and provide the kernel cycles reading without
22 * handling overflow mechanism.
23 * - After ec entered "sleep/deep sleep" power state which is used for better
24 * power consumption, then its clock will stop.
25 *
26 * - A event timer based on an ITIM32 (Internal 32-bit timer) instance, clocked
27 * by LFCLK which frequency is 32KHz and still activated when ec entered
28 * "sleep/deep sleep" power state.
29 * - Provide a system clock timeout notification. In its ISR, the driver informs
30 * the kernel that the specified number of ticks have elapsed.
31 * - Its prescaler is set to 1 and the formula between event timer's cycles and
32 * ticks is 'cycles = (ticks * 32768) / CONFIG_SYS_CLOCK_TICKS_PER_SEC'
33 * - Compensate reading of ITIM64 which clock is gating after ec entered
34 * "sleep/deep sleep" power state if CONFIG_PM is enabled.
35 */
36
37 #include <drivers/clock_control.h>
38 #include <drivers/timer/system_timer.h>
39 #include <sys_clock.h>
40 #include <spinlock.h>
41 #include <soc.h>
42
43 #include <logging/log.h>
44 LOG_MODULE_REGISTER(itim, LOG_LEVEL_ERR);
45
46 #define NPCX_ITIM32_MAX_CNT 0xffffffff
47 #define NPCX_ITIM64_MAX_HALF_CNT 0xffffffff
48 #define EVT_CYCLES_PER_SEC LFCLK /* 32768 Hz */
49 #define SYS_CYCLES_PER_TICK (sys_clock_hw_cycles_per_sec() \
50 / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
51 #define SYS_CYCLES_PER_USEC (sys_clock_hw_cycles_per_sec() / 1000000)
52 #define EVT_CYCLES_FROM_TICKS(ticks) \
53 ceiling_fraction(ticks * EVT_CYCLES_PER_SEC, \
54 CONFIG_SYS_CLOCK_TICKS_PER_SEC)
55 #define NPCX_ITIM_CLK_SEL_DELAY 92 /* Delay for clock selection (Unit:us) */
56 /* Timeout for enabling ITIM module: 100us (Unit:cycles) */
57 #define NPCX_ITIM_EN_TIMEOUT_CYCLES (100 * SYS_CYCLES_PER_USEC)
58
59 /* Instance of system and event timers */
60 static struct itim64_reg *const sys_tmr = (struct itim64_reg *)
61 DT_INST_REG_ADDR_BY_NAME(0, sys_itim);
62 static struct itim32_reg *const evt_tmr = (struct itim32_reg *)
63 DT_INST_REG_ADDR_BY_NAME(0, evt_itim);
64
65 static const struct npcx_clk_cfg itim_clk_cfg[] = NPCX_DT_CLK_CFG_ITEMS_LIST(0);
66
67 static struct k_spinlock lock;
68 /* Announced cycles in system timer before executing sys_clock_announce() */
69 static uint64_t cyc_sys_announced;
70 /* Current target cycles of time-out signal in event timer */
71 static uint32_t cyc_evt_timeout;
72 /* Total cycles of system timer stopped in "sleep/deep sleep" mode */
73 __unused static uint64_t cyc_sys_compensated;
74 /* Current cycles in event timer when ec entered "sleep/deep sleep" mode */
75 __unused static uint32_t cyc_evt_enter_deep_idle;
76
77 /* ITIM local inline functions */
npcx_itim_get_sys_cyc64(void)78 static inline uint64_t npcx_itim_get_sys_cyc64(void)
79 {
80 uint32_t cnt64h, cnt64h_check, cnt64l;
81
82 /* Read 64-bit counter value from two 32-bit registers */
83 do {
84 cnt64h_check = sys_tmr->ITCNT64H;
85 cnt64l = sys_tmr->ITCNT64L;
86 cnt64h = sys_tmr->ITCNT64H;
87 } while (cnt64h != cnt64h_check);
88
89 cnt64h = NPCX_ITIM64_MAX_HALF_CNT - cnt64h;
90 cnt64l = NPCX_ITIM64_MAX_HALF_CNT - cnt64l + 1;
91
92 /* Return current value of 64-bit counter value of system timer */
93 if (IS_ENABLED(CONFIG_PM)) {
94 return ((((uint64_t)cnt64h) << 32) | cnt64l) +
95 cyc_sys_compensated;
96 } else {
97 return (((uint64_t)cnt64h) << 32) | cnt64l;
98 }
99 }
100
npcx_itim_evt_enable(void)101 static inline int npcx_itim_evt_enable(void)
102 {
103 uint64_t cyc_start;
104
105 /* Enable event timer and wait for it to take effect */
106 evt_tmr->ITCTS32 |= BIT(NPCX_ITCTSXX_ITEN);
107
108 /*
109 * Usually, it need one clock (30.5 us) to take effect since
110 * asynchronization between core and itim32's source clock (LFCLK).
111 */
112 cyc_start = npcx_itim_get_sys_cyc64();
113 while (!IS_BIT_SET(evt_tmr->ITCTS32, NPCX_ITCTSXX_ITEN)) {
114 if (npcx_itim_get_sys_cyc64() - cyc_start >
115 NPCX_ITIM_EN_TIMEOUT_CYCLES) {
116 /* ITEN bit is still unset? */
117 if (!IS_BIT_SET(evt_tmr->ITCTS32, NPCX_ITCTSXX_ITEN)) {
118 LOG_ERR("Timeout: enabling EVT timer!");
119 return -ETIMEDOUT;
120 }
121 }
122 }
123
124 return 0;
125 }
126
npcx_itim_evt_disable(void)127 static inline void npcx_itim_evt_disable(void)
128 {
129 /* Disable event timer and no need to wait for it to take effect */
130 evt_tmr->ITCTS32 &= ~BIT(NPCX_ITCTSXX_ITEN);
131 }
132
133 /* ITIM local functions */
npcx_itim_start_evt_tmr_by_tick(int32_t ticks)134 static int npcx_itim_start_evt_tmr_by_tick(int32_t ticks)
135 {
136 /*
137 * Get desired cycles of event timer from the requested ticks which
138 * round up to next tick boundary.
139 */
140 if (ticks == K_TICKS_FOREVER) {
141 cyc_evt_timeout = NPCX_ITIM32_MAX_CNT;
142 } else {
143 if (ticks <= 0) {
144 ticks = 1;
145 }
146 cyc_evt_timeout = MIN(EVT_CYCLES_FROM_TICKS(ticks),
147 NPCX_ITIM32_MAX_CNT);
148 }
149 LOG_DBG("ticks %x, cyc_evt_timeout %x", ticks, cyc_evt_timeout);
150
151 /* Disable event timer if needed before configuring counter */
152 if (IS_BIT_SET(evt_tmr->ITCTS32, NPCX_ITCTSXX_ITEN)) {
153 npcx_itim_evt_disable();
154 }
155
156 /* Upload counter of event timer */
157 evt_tmr->ITCNT32 = MAX(cyc_evt_timeout - 1, 1);
158
159 /* Enable event timer and start ticking */
160 return npcx_itim_evt_enable();
161
162 }
163
npcx_itim_evt_isr(const struct device * dev)164 static void npcx_itim_evt_isr(const struct device *dev)
165 {
166 ARG_UNUSED(dev);
167
168 /* Disable ITIM event module first */
169 npcx_itim_evt_disable();
170 /* Clear timeout status for event */
171 evt_tmr->ITCTS32 |= BIT(NPCX_ITCTSXX_TO_STS);
172
173 if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
174 k_spinlock_key_t key = k_spin_lock(&lock);
175 uint32_t delta_ticks = (uint32_t)((npcx_itim_get_sys_cyc64() -
176 cyc_sys_announced) / SYS_CYCLES_PER_TICK);
177
178 /* Store announced cycles of system timer */
179 cyc_sys_announced = npcx_itim_get_sys_cyc64();
180 k_spin_unlock(&lock, key);
181
182 /* Informs kernel that specified number of ticks have elapsed */
183 sys_clock_announce(delta_ticks);
184 } else {
185 /* Enable event timer for ticking and wait to it take effect */
186 npcx_itim_evt_enable();
187
188 /* Informs kernel that one tick has elapsed */
189 sys_clock_announce(1);
190 }
191 }
192
193 #if defined(CONFIG_PM)
npcx_itim_get_evt_cyc32(void)194 static inline uint32_t npcx_itim_get_evt_cyc32(void)
195 {
196 uint32_t cnt1, cnt2;
197
198 cnt1 = evt_tmr->ITCNT32;
199 /*
200 * Wait for two consecutive equal values are read since the source clock
201 * of event timer is 32KHz.
202 */
203 while ((cnt2 = evt_tmr->ITCNT32) != cnt1)
204 cnt1 = cnt2;
205
206 /* Return current value of 32-bit counter of event timer */
207 return cnt2;
208 }
209
npcx_itim_evt_elapsed_cyc32(void)210 static uint32_t npcx_itim_evt_elapsed_cyc32(void)
211 {
212 uint32_t cnt1 = npcx_itim_get_evt_cyc32();
213 uint8_t sys_cts = evt_tmr->ITCTS32;
214 uint16_t cnt2 = npcx_itim_get_evt_cyc32();
215
216 /* Event has been triggered but timer ISR doesn't handle it */
217 if (IS_BIT_SET(sys_cts, NPCX_ITCTSXX_TO_STS) || (cnt2 > cnt1)) {
218 cnt2 = cyc_evt_timeout;
219 } else {
220 cnt2 = cyc_evt_timeout - cnt2;
221 }
222
223 /* Return elapsed cycles of 32-bit counter of event timer */
224 return cnt2;
225 }
226 #endif /* CONFIG_PM */
227
228 /* System timer api functions */
sys_clock_set_timeout(int32_t ticks,bool idle)229 void sys_clock_set_timeout(int32_t ticks, bool idle)
230 {
231 ARG_UNUSED(idle);
232
233 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
234 /* Only for tickless kernel system */
235 return;
236 }
237
238 LOG_DBG("timeout is %d", ticks);
239 /* Start a event timer in ticks */
240 npcx_itim_start_evt_tmr_by_tick(ticks);
241 }
242
sys_clock_elapsed(void)243 uint32_t sys_clock_elapsed(void)
244 {
245 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
246 /* Always return 0 for tickful kernel system */
247 return 0;
248 }
249
250 k_spinlock_key_t key = k_spin_lock(&lock);
251 uint64_t current = npcx_itim_get_sys_cyc64();
252
253 k_spin_unlock(&lock, key);
254
255 /* Return how many ticks elapsed since last sys_clock_announce() call */
256 return (uint32_t)((current - cyc_sys_announced) / SYS_CYCLES_PER_TICK);
257 }
258
sys_clock_cycle_get_32(void)259 uint32_t sys_clock_cycle_get_32(void)
260 {
261 k_spinlock_key_t key = k_spin_lock(&lock);
262 uint64_t current = npcx_itim_get_sys_cyc64();
263
264 k_spin_unlock(&lock, key);
265
266 /* Return how many cycles since system kernel timer start counting */
267 return (uint32_t)(current);
268 }
269
sys_clock_driver_init(const struct device * dev)270 int sys_clock_driver_init(const struct device *dev)
271 {
272 ARG_UNUSED(dev);
273 int ret;
274 uint32_t sys_tmr_rate;
275 const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
276
277 /* Turn on all itim module clocks used for counting */
278 for (int i = 0; i < ARRAY_SIZE(itim_clk_cfg); i++) {
279 ret = clock_control_on(clk_dev, (clock_control_subsys_t *)
280 &itim_clk_cfg[i]);
281 if (ret < 0) {
282 LOG_ERR("Turn on timer %d clock failed.", i);
283 return ret;
284 }
285 }
286
287 /*
288 * In npcx series, we use ITIM64 as system kernel timer. Its source
289 * clock frequency must equal to CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC.
290 */
291 ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t *)
292 &itim_clk_cfg[1], &sys_tmr_rate);
293 if (ret < 0) {
294 LOG_ERR("Get ITIM64 clock rate failed %d", ret);
295 return ret;
296 }
297
298 if (sys_tmr_rate != CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) {
299 LOG_ERR("CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC doesn't match "
300 "ITIM64 clock frequency %d", sys_tmr_rate);
301 return -EINVAL;
302 }
303
304 /*
305 * Step 1. Use a ITIM64 timer as system kernel timer for counting.
306 * Configure 64-bit timer counter and its prescaler to 1 first.
307 */
308 sys_tmr->ITPRE64 = 0;
309 sys_tmr->ITCNT64L = NPCX_ITIM64_MAX_HALF_CNT;
310 sys_tmr->ITCNT64H = NPCX_ITIM64_MAX_HALF_CNT;
311 /*
312 * Select APB2 clock which freq is CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC,
313 * and clear timeout status bit before enabling the whole module.
314 */
315 sys_tmr->ITCTS64 = BIT(NPCX_ITCTSXX_TO_STS);
316 /* Enable 64-bit timer and start ticking */
317 sys_tmr->ITCTS64 |= BIT(NPCX_ITCTSXX_ITEN);
318
319 /*
320 * Step 2. Use a ITIM32 timer for event handling (ex. timeout event).
321 * Configure 32-bit timer's prescaler to 1 first.
322 */
323 evt_tmr->ITPRE32 = 0;
324 /*
325 * Select low frequency clock source (The freq is 32kHz), enable its
326 * interrupt/wake-up sources, and clear timeout status bit before
327 * enabling it.
328 */
329 evt_tmr->ITCTS32 = BIT(NPCX_ITCTSXX_CKSEL) | BIT(NPCX_ITCTSXX_TO_WUE)
330 | BIT(NPCX_ITCTSXX_TO_IE) | BIT(NPCX_ITCTSXX_TO_STS);
331
332 /* A delay for ITIM source clock selection */
333 k_busy_wait(NPCX_ITIM_CLK_SEL_DELAY);
334
335 /* Configure event timer's ISR */
336 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
337 npcx_itim_evt_isr, NULL, 0);
338 /* Enable event timer interrupt */
339 irq_enable(DT_INST_IRQN(0));
340
341 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
342 /* Start a event timer in one tick */
343 ret = npcx_itim_start_evt_tmr_by_tick(1);
344 if (ret < 0) {
345 return ret;
346 }
347 }
348
349 return 0;
350 }
351
352 /* Platform specific systme timer functions */
353 #if defined(CONFIG_PM)
npcx_clock_capture_low_freq_timer(void)354 void npcx_clock_capture_low_freq_timer(void)
355 {
356 cyc_evt_enter_deep_idle = npcx_itim_evt_elapsed_cyc32();
357 }
358
npcx_clock_compensate_system_timer(void)359 void npcx_clock_compensate_system_timer(void)
360 {
361 uint32_t cyc_evt_elapsed_in_deep = npcx_itim_evt_elapsed_cyc32() -
362 cyc_evt_enter_deep_idle;
363
364 cyc_sys_compensated += ((uint64_t)cyc_evt_elapsed_in_deep *
365 sys_clock_hw_cycles_per_sec()) / EVT_CYCLES_PER_SEC;
366 }
367
368 #if defined(CONFIG_SOC_POWER_MANAGEMENT_TRACE)
npcx_clock_get_sleep_ticks(void)369 uint64_t npcx_clock_get_sleep_ticks(void)
370 {
371 return cyc_sys_compensated / SYS_CYCLES_PER_TICK;
372 }
373 #endif /* CONFIG_SOC_POWER_MANAGEMENT_TRACE */
374 #endif /* CONFIG_PM */
375