diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 7fe9247e5a2..dd9966e2d34 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_sources_ifdef(CONFIG_ARM_ARCH_TIMER arm_arch_timer.c) zephyr_sources_ifdef(CONFIG_LOAPIC_TIMER loapic_timer.c) zephyr_sources_ifdef(CONFIG_APIC_TIMER apic_timer.c) zephyr_sources_ifdef(CONFIG_ALTERA_AVALON_TIMER altera_avalon_timer_hal.c) +zephyr_sources_ifdef(CONFIG_ITE_IT8XXX2_TIMER ite_it8xxx2_timer.c) zephyr_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c) zephyr_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) zephyr_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 97fa77a1a2d..bcfab7a6933 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -145,6 +145,13 @@ config ALTERA_AVALON_TIMER with Nios II and possibly other Altera soft CPUs. It provides the standard "system clock driver" interfaces. +config ITE_IT8XXX2_TIMER + bool "ITE it8xxx2 timer driver" + depends on SOC_IT8XXX2 + help + This module implements a kernel device driver for the ITE it8xxx2 + HW timer model + config NRF_RTC_TIMER bool "nRF Real Time Counter (NRF_RTC1) Timer" depends on CLOCK_CONTROL diff --git a/drivers/timer/ite_it8xxx2_timer.c b/drivers/timer/ite_it8xxx2_timer.c new file mode 100644 index 00000000000..ffad161a89f --- /dev/null +++ b/drivers/timer/ite_it8xxx2_timer.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 ITE Corporation. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +/** + * Macro Define + */ +#define EXT_TIMER_BASE (DT_REG_ADDR_BY_IDX(DT_NODELABEL(timer), 0)) +#define EXT_CTL_B (EXT_TIMER_BASE + 0x10) +#define EXT_PSC_B (EXT_TIMER_BASE + 0x11) +#define EXT_LLR_B (EXT_TIMER_BASE + 0x14) +#define EXT_LHR_B (EXT_TIMER_BASE + 0x15) +#define EXT_LH2R_B (EXT_TIMER_BASE + 0x16) +#define EXT_LH3R_B (EXT_TIMER_BASE + 0x17) +#define EXT_CNTO_B (EXT_TIMER_BASE + 0x48) + +#define CTIMER_HW_TIMER_INDEX EXT_TIMER_3 +#define ETIMER_HW_TIMER_INDEX EXT_TIMER_5 +#define RTIMER_HW_TIMER_INDEX EXT_TIMER_7 +#define CYC_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \ + / CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#define MAX_TICKS ((0x00ffffffu - CYC_PER_TICK) / CYC_PER_TICK) + +#define MAX_TIMER_NUM 8 +#define REG_ADDR_OFFSET(idx) (idx * MAX_TIMER_NUM) +#define IDX_SHIFT(idx, rsh, lsh) ((idx >> rsh) << lsh) + +enum _EXT_TIMER_PRESCALE_TYPE_ { + ET_PSR_32K, + ET_PSR_1K, + ET_PSR_32, + ET_PSR_8M, +}; +enum _EXT_TIMER_IDX_ { + EXT_TIMER_3 = 0, /* ctimer */ + EXT_TIMER_4, /* ctimer */ + EXT_TIMER_5, /* etimer */ + EXT_TIMER_6, /* NULL */ + EXT_TIMER_7, /* rtimer */ + EXT_TIMER_8 /* NULL */ +}; + +/* Be careful of overflow issue */ +#define MILLI_SEC_TO_COUNT(hz, ms) ((hz) * (ms) / 1000) +#define MICRO_SEC_TO_COUNT(hz, us) ((hz) * (us) / 1000000) + +/** + * ITE timer control api + */ +static void ite_timer_reload(uint8_t idx, uint32_t cnt) +{ + /* timer_start */ + sys_set_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); + sys_write8(((cnt >> 24) & 0xFF), (EXT_LH3R_B + REG_ADDR_OFFSET(idx))); + sys_write8(((cnt >> 16) & 0xFF), (EXT_LH2R_B + REG_ADDR_OFFSET(idx))); + sys_write8(((cnt >> 8) & 0xFF), (EXT_LHR_B + REG_ADDR_OFFSET(idx))); + sys_write8(((cnt >> 0) & 0xFF), (EXT_LLR_B + REG_ADDR_OFFSET(idx))); +} + +/* The following function: + * disable, enable, check_flag, clear_flag, wait, + * can be used only for Timer #3 ~ #7 + */ +static void ite_timer_disable(uint8_t idx) +{ + CLEAR_MASK(IER19, (BIT(3 + (idx)))); +} + +static void ite_timer_enable(uint8_t idx) +{ + SET_MASK(IER19, (BIT(3 + (idx)))); +} + +static void ite_timer_clear_flag(uint8_t idx) +{ + ISR19 = BIT(3 + (idx)); +} + +/** + * timer_init() + */ +static int timer_init(uint8_t idx, uint8_t psr, uint8_t initial_state, + uint8_t enable_isr, uint32_t cnt) +{ + /* Setup Triggered Mode -> Rising-Edge Trig. */ + if (idx != EXT_TIMER_8) { + IELMR19 |= BIT(3 + idx); + IPOLR19 &= (~(BIT(3 + idx))); + } else { + IELMR10 |= BIT(0); + IPOLR10 &= (~(BIT(0))); + } + + /* Setup prescaler */ + sys_write8(psr, (EXT_PSC_B + REG_ADDR_OFFSET(idx))); + + /* Reload counter */ + ite_timer_reload(idx, cnt); + + /* Start counting or not */ + if (initial_state) { + /* timer restart */ + /* timer_stop */ + sys_clear_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); + + /* timer_start */ + sys_set_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); + } else { + /* timer_stop */ + sys_clear_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); + } + + /* Enable ISR or not & Clear flag */ + if (idx != EXT_TIMER_8) { + if (enable_isr) { + ite_timer_enable(idx); + } else { + ite_timer_disable(idx); + } + ite_timer_clear_flag(idx); + } else { + if (enable_isr) { + SET_MASK(IER10, BIT(0)); + } else { + CLEAR_MASK(IER10, BIT(0)); + } + ISR10 = BIT(0); + } + return 0; +} + +static int timer_init_ms(uint8_t idx, uint8_t psr, uint8_t initial_state, + uint8_t enable_isr, uint32_t u32MilliSec) +{ + uint32_t cnt; + + if (psr == ET_PSR_32K) { + cnt = MILLI_SEC_TO_COUNT(32768, u32MilliSec); + } else if (psr == ET_PSR_1K) { + cnt = MILLI_SEC_TO_COUNT(1024, u32MilliSec); + } else if (psr == ET_PSR_32) { + cnt = MILLI_SEC_TO_COUNT(32, u32MilliSec); + } else if (psr == ET_PSR_8M) { + cnt = u32MilliSec * 8000; /* fixed overflow issue */ + } else { + return -1; + } + + /* 24-bits only */ + if (cnt >> 24) { + return -2; + } + return timer_init(idx, psr, initial_state, enable_isr, cnt); +} + +static void timer_init_combine(uint8_t idx, uint8_t bEnable) +{ + if (bEnable) { + sys_set_bit((EXT_CTL_B + IDX_SHIFT(idx, 1, (1 + 3))), 3); + } else { + sys_clear_bit((EXT_CTL_B + IDX_SHIFT(idx, 1, (1 + 3))), 3); + } +} + +static uint32_t get_timer_combine_count(uint8_t idx) +{ + return sys_read32(EXT_CNTO_B + ((IDX_SHIFT(idx, 1, 1) + 1) * 4)); +} + +static void timer_count_reset(uint8_t idx, uint32_t cnt) +{ + /* Reload counter */ + ite_timer_reload(idx, cnt); + + /* Start counting or not */ + /* timer_stop */ + sys_clear_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); + /* timer_start */ + sys_set_bit((EXT_CTL_B + REG_ADDR_OFFSET(idx)), 0); +} + +static struct k_spinlock lock; +static volatile uint32_t accumulated_cycle_count; +static void timer_isr(const void *unused) +{ + ARG_UNUSED(unused); + + k_spinlock_key_t key = k_spin_lock(&lock); + + /* timer_stop */ + sys_clear_bit((EXT_CTL_B + ((ETIMER_HW_TIMER_INDEX) * MAX_TIMER_NUM)), + 0); + uint32_t dticks = (get_timer_combine_count(CTIMER_HW_TIMER_INDEX) + - accumulated_cycle_count) / CYC_PER_TICK; + accumulated_cycle_count += dticks * CYC_PER_TICK; + k_spin_unlock(&lock, key); + z_clock_announce(dticks); +} + +int z_clock_driver_init(const struct device *device) +{ + timer_init_combine(CTIMER_HW_TIMER_INDEX, TRUE); + timer_init(CTIMER_HW_TIMER_INDEX, ET_PSR_32K, TRUE, FALSE, 0); + irq_connect_dynamic(DT_IRQ_BY_IDX(DT_NODELABEL(timer), 5, irq), + 0, timer_isr, NULL, + DT_IRQ_BY_IDX(DT_NODELABEL(timer), 5, flags)); + timer_init_ms(ETIMER_HW_TIMER_INDEX, ET_PSR_32K, FALSE, TRUE, 0); + return 0; +} + +void z_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + k_spinlock_key_t key = k_spin_lock(&lock); + + ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : ticks; + ticks = MAX(MIN(ticks, (int32_t)MAX_TICKS), 1); + timer_count_reset(ETIMER_HW_TIMER_INDEX, ticks * CYC_PER_TICK); + k_spin_unlock(&lock, key); +} + +uint32_t z_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return 0; + } + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t ret = (get_timer_combine_count(CTIMER_HW_TIMER_INDEX) + - accumulated_cycle_count) / CYC_PER_TICK; + k_spin_unlock(&lock, key); + return ret; +} + +uint32_t z_timer_cycle_get_32(void) +{ + return get_timer_combine_count(CTIMER_HW_TIMER_INDEX); +}