|  | /* | 
|  | * Copyright (c) 2020 ITE Corporation. All Rights Reserved. | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT ite_it8xxx2_timer | 
|  |  | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/drivers/timer/system_timer.h> | 
|  | #include <zephyr/dt-bindings/interrupt-controller/ite-intc.h> | 
|  | #include <soc.h> | 
|  | #include <zephyr/spinlock.h> | 
|  | #include <zephyr/sys_clock.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/irq.h> | 
|  | LOG_MODULE_REGISTER(timer, LOG_LEVEL_ERR); | 
|  |  | 
|  | BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 32768, | 
|  | "ITE RTOS timer HW frequency is fixed at 32768Hz"); | 
|  |  | 
|  | /* Event timer configurations */ | 
|  | #define EVENT_TIMER		EXT_TIMER_3 | 
|  | #define EVENT_TIMER_IRQ		DT_INST_IRQ_BY_IDX(0, 0, irq) | 
|  | #define EVENT_TIMER_FLAG	DT_INST_IRQ_BY_IDX(0, 0, flags) | 
|  | /* Event timer max count is 512 sec (base on clock source 32768Hz) */ | 
|  | #define EVENT_TIMER_MAX_CNT	0x00FFFFFFUL | 
|  |  | 
|  | /* Busy wait low timer configurations */ | 
|  | #define BUSY_WAIT_L_TIMER	EXT_TIMER_5 | 
|  | #define BUSY_WAIT_L_TIMER_IRQ	DT_INST_IRQ_BY_IDX(0, 2, irq) | 
|  | #define BUSY_WAIT_L_TIMER_FLAG	DT_INST_IRQ_BY_IDX(0, 2, flags) | 
|  |  | 
|  | /* Busy wait high timer configurations */ | 
|  | #define BUSY_WAIT_H_TIMER	EXT_TIMER_6 | 
|  | #define BUSY_WAIT_H_TIMER_IRQ	DT_INST_IRQ_BY_IDX(0, 3, irq) | 
|  | #define BUSY_WAIT_H_TIMER_FLAG	DT_INST_IRQ_BY_IDX(0, 3, flags) | 
|  | /* Busy wait high timer max count is 71.58min (base on clock source 1MHz) */ | 
|  | #define BUSY_WAIT_TIMER_H_MAX_CNT 0xFFFFFFFFUL | 
|  |  | 
|  | #if defined(CONFIG_TEST) | 
|  | const int32_t z_sys_timer_irq_for_test = DT_IRQ_BY_IDX(DT_NODELABEL(timer), 5, irq); | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_SOC_IT8XXX2_PLL_FLASH_48M | 
|  | /* | 
|  | * One shot timer configurations | 
|  | * | 
|  | * NOTE: Timer1/2 register address isn't regular like timer3/4/5/6/7/8, and | 
|  | *       timer1 is used for printing watchdog warning message. So now we use | 
|  | *       timer2 only one shot to wake up chip and change pll. | 
|  | */ | 
|  | #define WDT_BASE		DT_REG_ADDR(DT_NODELABEL(twd0)) | 
|  | #define WDT_REG			(struct wdt_it8xxx2_regs *)(WDT_BASE) | 
|  | #define ONE_SHOT_TIMER_IRQ	DT_IRQ_BY_IDX(DT_NODELABEL(twd0), 1, irq) | 
|  | #define ONE_SHOT_TIMER_FLAG	DT_IRQ_BY_IDX(DT_NODELABEL(twd0), 1, flags) | 
|  | #endif | 
|  |  | 
|  | #define MS_TO_COUNT(hz, ms)	((hz) * (ms) / 1000) | 
|  | /* | 
|  | * One system (kernel) tick is as how much HW timer counts | 
|  | * | 
|  | * NOTE: Event and free run timer individually select the same clock source | 
|  | *       frequency, so they can use the same HW_CNT_PER_SYS_TICK to transform | 
|  | *       unit between HW count and system tick. If clock source frequency is | 
|  | *       different, then we should define another to transform. | 
|  | */ | 
|  | #define HW_CNT_PER_SYS_TICK	(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \ | 
|  | / CONFIG_SYS_CLOCK_TICKS_PER_SEC) | 
|  | /* Event timer max count is as how much system (kernel) tick */ | 
|  | #define EVEN_TIMER_MAX_CNT_SYS_TICK	(EVENT_TIMER_MAX_CNT \ | 
|  | / HW_CNT_PER_SYS_TICK) | 
|  |  | 
|  | static struct k_spinlock lock; | 
|  | /* Last HW count that we called sys_clock_announce() */ | 
|  | static volatile uint32_t last_announced_hw_cnt; | 
|  |  | 
|  | enum ext_timer_raw_cnt { | 
|  | EXT_NOT_RAW_CNT = 0, | 
|  | EXT_RAW_CNT, | 
|  | }; | 
|  |  | 
|  | enum ext_timer_init { | 
|  | EXT_NOT_FIRST_TIME_ENABLE = 0, | 
|  | EXT_FIRST_TIME_ENABLE, | 
|  | }; | 
|  |  | 
|  | enum ext_timer_int { | 
|  | EXT_WITHOUT_TIMER_INT = 0, | 
|  | EXT_WITH_TIMER_INT, | 
|  | }; | 
|  |  | 
|  | enum ext_timer_start { | 
|  | EXT_NOT_START_TIMER = 0, | 
|  | EXT_START_TIMER, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_SOC_IT8XXX2_PLL_FLASH_48M | 
|  | static void timer_5ms_one_shot_isr(const void *unused) | 
|  | { | 
|  | ARG_UNUSED(unused); | 
|  |  | 
|  | /* | 
|  | * We are here because we have completed changing PLL sequence, | 
|  | * so disabled one shot timer interrupt. | 
|  | */ | 
|  | irq_disable(ONE_SHOT_TIMER_IRQ); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This timer is used to wake up chip from sleep mode to complete | 
|  | * changing PLL sequence. | 
|  | */ | 
|  | void timer_5ms_one_shot(void) | 
|  | { | 
|  | struct wdt_it8xxx2_regs *const timer2_reg = WDT_REG; | 
|  | uint32_t hw_cnt; | 
|  |  | 
|  | /* Initialize interrupt handler of one shot timer */ | 
|  | IRQ_CONNECT(ONE_SHOT_TIMER_IRQ, 0, timer_5ms_one_shot_isr, NULL, | 
|  | ONE_SHOT_TIMER_FLAG); | 
|  |  | 
|  | /* Set rising edge triggered of one shot timer */ | 
|  | ite_intc_irq_polarity_set(ONE_SHOT_TIMER_IRQ, ONE_SHOT_TIMER_FLAG); | 
|  |  | 
|  | /* Clear interrupt status of one shot timer */ | 
|  | ite_intc_isr_clear(ONE_SHOT_TIMER_IRQ); | 
|  |  | 
|  | /* Set clock source of one shot timer */ | 
|  | timer2_reg->ET2PSR = EXT_PSR_32P768K; | 
|  |  | 
|  | /* | 
|  | * Set count of one shot timer, | 
|  | * and after write ET2CNTLLR timer will start | 
|  | */ | 
|  | hw_cnt = MS_TO_COUNT(32768, 5/*ms*/); | 
|  | timer2_reg->ET2CNTLH2R = (uint8_t)((hw_cnt >> 16) & 0xff); | 
|  | timer2_reg->ET2CNTLHR = (uint8_t)((hw_cnt >> 8) & 0xff); | 
|  | timer2_reg->ET2CNTLLR = (uint8_t)(hw_cnt & 0xff); | 
|  |  | 
|  | irq_enable(ONE_SHOT_TIMER_IRQ); | 
|  | } | 
|  | #endif /* CONFIG_SOC_IT8XXX2_PLL_FLASH_48M */ | 
|  |  | 
|  | #ifdef CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT | 
|  | void arch_busy_wait(uint32_t usec_to_wait) | 
|  | { | 
|  | if (!usec_to_wait) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Decrease 1us here to calibrate our access registers latency */ | 
|  | usec_to_wait--; | 
|  |  | 
|  | /* | 
|  | * We want to set the bit(1) re-start busy wait timer as soon | 
|  | * as possible, so we directly write 0xb instead of | bit(1). | 
|  | */ | 
|  | IT8XXX2_EXT_CTRLX(BUSY_WAIT_L_TIMER) = IT8XXX2_EXT_ETX_COMB_RST_EN; | 
|  |  | 
|  | for (;;) { | 
|  | uint32_t curr = IT8XXX2_EXT_CNTOX(BUSY_WAIT_H_TIMER); | 
|  |  | 
|  | if (curr >= usec_to_wait) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void evt_timer_enable(void) | 
|  | { | 
|  | /* Enable and re-start event timer */ | 
|  | IT8XXX2_EXT_CTRLX(EVENT_TIMER) |= (IT8XXX2_EXT_ETXEN | | 
|  | IT8XXX2_EXT_ETXRST); | 
|  | } | 
|  |  | 
|  | static void evt_timer_isr(const void *unused) | 
|  | { | 
|  | ARG_UNUSED(unused); | 
|  |  | 
|  | /* Disable event timer */ | 
|  | IT8XXX2_EXT_CTRLX(EVENT_TIMER) &= ~IT8XXX2_EXT_ETXEN; | 
|  | /* W/C event timer interrupt status */ | 
|  | ite_intc_isr_clear(EVENT_TIMER_IRQ); | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | 
|  | /* | 
|  | * Get free run observer count from last time announced and | 
|  | * transform unit to system tick | 
|  | */ | 
|  | uint32_t dticks = (~(IT8XXX2_EXT_CNTOX(FREE_RUN_TIMER)) - | 
|  | last_announced_hw_cnt) / HW_CNT_PER_SYS_TICK; | 
|  | last_announced_hw_cnt += (dticks * HW_CNT_PER_SYS_TICK); | 
|  |  | 
|  | sys_clock_announce(dticks); | 
|  | } else { | 
|  | /* enable event timer */ | 
|  | evt_timer_enable(); | 
|  | /* Informs kernel that one system tick has elapsed */ | 
|  | sys_clock_announce(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void free_run_timer_overflow_isr(const void *unused) | 
|  | { | 
|  | ARG_UNUSED(unused); | 
|  |  | 
|  | /* Read to clear terminal count flag */ | 
|  | __unused uint8_t rc_tc = IT8XXX2_EXT_CTRLX(FREE_RUN_TIMER); | 
|  |  | 
|  | /* | 
|  | * TODO: to increment 32-bit "top half" here for software 64-bit | 
|  | * timer emulation. | 
|  | */ | 
|  | } | 
|  |  | 
|  | void sys_clock_set_timeout(int32_t ticks, bool idle) | 
|  | { | 
|  | uint32_t hw_cnt; | 
|  |  | 
|  | ARG_UNUSED(idle); | 
|  |  | 
|  | if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | 
|  | /* Always return for non-tickless kernel system */ | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Critical section */ | 
|  | k_spinlock_key_t key = k_spin_lock(&lock); | 
|  |  | 
|  | /* Disable event timer */ | 
|  | IT8XXX2_EXT_CTRLX(EVENT_TIMER) &= ~IT8XXX2_EXT_ETXEN; | 
|  |  | 
|  | if (ticks == K_TICKS_FOREVER) { | 
|  | /* | 
|  | * If kernel doesn't have a timeout: | 
|  | * 1.CONFIG_SYSTEM_CLOCK_SLOPPY_IDLE = y (no future timer interrupts | 
|  | *   are expected), kernel pass K_TICKS_FOREVER (0xFFFF FFFF FFFF FFFF), | 
|  | *   we handle this case in here. | 
|  | * 2.CONFIG_SYSTEM_CLOCK_SLOPPY_IDLE = n (schedule timeout as far | 
|  | *   into the future as possible), kernel pass INT_MAX (0x7FFF FFFF), | 
|  | *   we handle it in later else {}. | 
|  | */ | 
|  | k_spin_unlock(&lock, key); | 
|  | return; | 
|  | } else if (ticks <= 1) { | 
|  | /* | 
|  | * Ticks <= 1 means the kernel wants the tick announced | 
|  | * as soon as possible, ideally no more than one system tick | 
|  | * in the future. So set event timer count to 1 system tick or | 
|  | * at least 1 hw count. | 
|  | */ | 
|  | hw_cnt = MAX((1 * HW_CNT_PER_SYS_TICK), 1); | 
|  | } else { | 
|  | /* | 
|  | * Set event timer count to EVENT_TIMER_MAX_CNT, after | 
|  | * interrupt fired the remaining time will be set again | 
|  | * by sys_clock_announce(). | 
|  | */ | 
|  | hw_cnt = MIN((ticks * HW_CNT_PER_SYS_TICK), EVENT_TIMER_MAX_CNT); | 
|  | } | 
|  |  | 
|  | /* Set event timer 24-bit count */ | 
|  | IT8XXX2_EXT_CNTX(EVENT_TIMER) = hw_cnt; | 
|  |  | 
|  | /* W/C event timer interrupt status */ | 
|  | ite_intc_isr_clear(EVENT_TIMER_IRQ); | 
|  |  | 
|  | /* enable event timer */ | 
|  | evt_timer_enable(); | 
|  |  | 
|  | k_spin_unlock(&lock, key); | 
|  |  | 
|  | LOG_DBG("timeout is 0x%x, set hw count 0x%x", ticks, hw_cnt); | 
|  | } | 
|  |  | 
|  | uint32_t sys_clock_elapsed(void) | 
|  | { | 
|  | if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | 
|  | /* Always return 0 for non-tickless kernel system */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Critical section */ | 
|  | k_spinlock_key_t key = k_spin_lock(&lock); | 
|  | /* | 
|  | * Get free run observer count from last time announced and transform | 
|  | * unit to system tick | 
|  | */ | 
|  | uint32_t dticks = (~(IT8XXX2_EXT_CNTOX(FREE_RUN_TIMER)) - | 
|  | last_announced_hw_cnt) / HW_CNT_PER_SYS_TICK; | 
|  | k_spin_unlock(&lock, key); | 
|  |  | 
|  | return dticks; | 
|  | } | 
|  |  | 
|  | uint32_t sys_clock_cycle_get_32(void) | 
|  | { | 
|  | /* | 
|  | * Get free run observer count | 
|  | * | 
|  | * NOTE: Timer is counting down from 0xffffffff. In not combined | 
|  | *       mode, the observer count value is the same as count, so after | 
|  | *       NOT count operation we can get counting up value; In | 
|  | *       combined mode, the observer count value is the same as NOT | 
|  | *       count operation. | 
|  | */ | 
|  | uint32_t dticks = ~(IT8XXX2_EXT_CNTOX(FREE_RUN_TIMER)); | 
|  |  | 
|  | return dticks; | 
|  | } | 
|  |  | 
|  | static int timer_init(enum ext_timer_idx ext_timer, | 
|  | enum ext_clk_src_sel clock_source_sel, | 
|  | enum ext_timer_raw_cnt raw, | 
|  | uint32_t ms, | 
|  | enum ext_timer_init first_time_enable, | 
|  | uint32_t irq_num, | 
|  | uint32_t irq_flag, | 
|  | enum ext_timer_int with_int, | 
|  | enum ext_timer_start start) | 
|  | { | 
|  | uint32_t hw_cnt; | 
|  |  | 
|  | if (raw == EXT_RAW_CNT) { | 
|  | hw_cnt = ms; | 
|  | } else { | 
|  | if (clock_source_sel == EXT_PSR_32P768K) | 
|  | hw_cnt = MS_TO_COUNT(32768, ms); | 
|  | else if (clock_source_sel == EXT_PSR_1P024K) | 
|  | hw_cnt = MS_TO_COUNT(1024, ms); | 
|  | else if (clock_source_sel == EXT_PSR_32) | 
|  | hw_cnt = MS_TO_COUNT(32, ms); | 
|  | else if (clock_source_sel == EXT_PSR_8M) | 
|  | hw_cnt = 8000 * ms; | 
|  | else { | 
|  | LOG_ERR("Timer %d clock source error !", ext_timer); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hw_cnt == 0) { | 
|  | LOG_ERR("Timer %d count shouldn't be 0 !", ext_timer); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (first_time_enable == EXT_FIRST_TIME_ENABLE) { | 
|  | /* Enable and re-start external timer x */ | 
|  | IT8XXX2_EXT_CTRLX(ext_timer) |= (IT8XXX2_EXT_ETXEN | | 
|  | IT8XXX2_EXT_ETXRST); | 
|  | /* Disable external timer x */ | 
|  | IT8XXX2_EXT_CTRLX(ext_timer) &= ~IT8XXX2_EXT_ETXEN; | 
|  | } | 
|  |  | 
|  | /* Set rising edge triggered of external timer x */ | 
|  | ite_intc_irq_polarity_set(irq_num, irq_flag); | 
|  |  | 
|  | /* Clear interrupt status of external timer x */ | 
|  | ite_intc_isr_clear(irq_num); | 
|  |  | 
|  | /* Set clock source of external timer x */ | 
|  | IT8XXX2_EXT_PSRX(ext_timer) = clock_source_sel; | 
|  |  | 
|  | /* Set count of external timer x */ | 
|  | IT8XXX2_EXT_CNTX(ext_timer) = hw_cnt; | 
|  |  | 
|  | /* Disable external timer x */ | 
|  | IT8XXX2_EXT_CTRLX(ext_timer) &= ~IT8XXX2_EXT_ETXEN; | 
|  | if (start == EXT_START_TIMER) | 
|  | /* Enable and re-start external timer x */ | 
|  | IT8XXX2_EXT_CTRLX(ext_timer) |= (IT8XXX2_EXT_ETXEN | | 
|  | IT8XXX2_EXT_ETXRST); | 
|  |  | 
|  | if (with_int == EXT_WITH_TIMER_INT) { | 
|  | irq_enable(irq_num); | 
|  | } else { | 
|  | irq_disable(irq_num); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sys_clock_driver_init(void) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  |  | 
|  | /* Enable 32-bit free run timer overflow interrupt */ | 
|  | IRQ_CONNECT(FREE_RUN_TIMER_IRQ, 0, free_run_timer_overflow_isr, NULL, | 
|  | FREE_RUN_TIMER_FLAG); | 
|  | /* Set 32-bit timer4 for free run*/ | 
|  | ret = timer_init(FREE_RUN_TIMER, EXT_PSR_32P768K, EXT_RAW_CNT, | 
|  | FREE_RUN_TIMER_MAX_CNT, EXT_FIRST_TIME_ENABLE, | 
|  | FREE_RUN_TIMER_IRQ, FREE_RUN_TIMER_FLAG, | 
|  | EXT_WITH_TIMER_INT, EXT_START_TIMER); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Init free run timer failed"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Set 24-bit timer3 for timeout event */ | 
|  | IRQ_CONNECT(EVENT_TIMER_IRQ, 0, evt_timer_isr, NULL, EVENT_TIMER_FLAG); | 
|  | if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | 
|  | ret = timer_init(EVENT_TIMER, EXT_PSR_32P768K, EXT_RAW_CNT, | 
|  | EVENT_TIMER_MAX_CNT, EXT_FIRST_TIME_ENABLE, | 
|  | EVENT_TIMER_IRQ, EVENT_TIMER_FLAG, | 
|  | EXT_WITH_TIMER_INT, EXT_NOT_START_TIMER); | 
|  | } else { | 
|  | /* Start a event timer in one system tick */ | 
|  | ret = timer_init(EVENT_TIMER, EXT_PSR_32P768K, EXT_RAW_CNT, | 
|  | MAX((1 * HW_CNT_PER_SYS_TICK), 1), | 
|  | EXT_FIRST_TIME_ENABLE, EVENT_TIMER_IRQ, | 
|  | EVENT_TIMER_FLAG, EXT_WITH_TIMER_INT, | 
|  | EXT_START_TIMER); | 
|  | } | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Init event timer failed"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)) { | 
|  | /* Set timer5 and timer6 combinational mode for busy wait */ | 
|  | IT8XXX2_EXT_CTRLX(BUSY_WAIT_L_TIMER) |= IT8XXX2_EXT_ETXCOMB; | 
|  |  | 
|  | /* Set 32-bit timer6 to count-- every 1us */ | 
|  | ret = timer_init(BUSY_WAIT_H_TIMER, EXT_PSR_8M, EXT_RAW_CNT, | 
|  | BUSY_WAIT_TIMER_H_MAX_CNT, EXT_FIRST_TIME_ENABLE, | 
|  | BUSY_WAIT_H_TIMER_IRQ, BUSY_WAIT_H_TIMER_FLAG, | 
|  | EXT_WITHOUT_TIMER_INT, EXT_START_TIMER); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Init busy wait high timer failed"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Set 24-bit timer5 to overflow every 1us | 
|  | * NOTE: When the timer5 count down to overflow in combinational | 
|  | *       mode, timer6 counter will automatically decrease one count | 
|  | *       and timer5 will automatically re-start counting down | 
|  | *       from 0x7. Timer5 clock source is 8MHz (=0.125ns), so the | 
|  | *       time period from 0x7 to overflow is 0.125ns * 8 = 1us. | 
|  | */ | 
|  | ret = timer_init(BUSY_WAIT_L_TIMER, EXT_PSR_8M, EXT_RAW_CNT, | 
|  | 0x7, EXT_FIRST_TIME_ENABLE, | 
|  | BUSY_WAIT_L_TIMER_IRQ, BUSY_WAIT_L_TIMER_FLAG, | 
|  | EXT_WITHOUT_TIMER_INT, EXT_START_TIMER); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Init busy wait low timer failed"); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, | 
|  | CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); |