timer: hpet: make it support EHL/PSE CPU

Add config macro to set interrupt as level triggered for ARM CPUs
Merge all timer configures into one place, then no need to overwrite
hpet_timer_conf_get/set() functions in SoC layer
Make hpet_timer_comparator_set() as the only register access function
to implemented in the SoC layer

Signed-off-by: Dong Wang <dong.d.wang@intel.com>
This commit is contained in:
Dong Wang 2021-07-22 21:10:05 +08:00 committed by Christopher Friedt
commit a56c42574e
2 changed files with 44 additions and 45 deletions

View file

@ -80,7 +80,6 @@ config APIC_TIMER_IRQ_PRIORITY
config HPET_TIMER config HPET_TIMER
bool "HPET timer" bool "HPET timer"
depends on X86
select IOAPIC if X86 select IOAPIC if X86
select LOAPIC if X86 select LOAPIC if X86
imply TIMER_READS_ITS_FREQUENCY_AT_RUNTIME imply TIMER_READS_ITS_FREQUENCY_AT_RUNTIME

View file

@ -60,26 +60,6 @@
#define TIMER_CONF_FSB_EN BIT(14) /* FSB interrupt delivery */ #define TIMER_CONF_FSB_EN BIT(14) /* FSB interrupt delivery */
/* enable */ /* enable */
/*
* The following MMIO initialization and register access functions
* should work on generic x86 hardware. If the targeted SoC requires
* special handling of HPET registers, these functions will need to be
* implemented in the SoC layer by first defining the macro
* HPET_USE_CUSTOM_REG_ACCESS_FUNCS in soc.h to signal such intent.
*
* This is a list of functions which must be implemented in the SoC
* layer:
* void hpet_mmio_init(void)
* uint32_t hpet_counter_get(void)
* uint32_t hpet_counter_clk_period_get(void)
* uint32_t hpet_gconf_get(void)
* void hpet_gconf_set(uint32_t val)
* void hpet_int_sts_set(uint32_t val)
* uint32_t hpet_timer_conf_get(void)
* void hpet_timer_conf_set(uint32_t val)
* void hpet_timer_comparator_set(uint32_t val)
*/
#ifndef HPET_USE_CUSTOM_REG_ACCESS_FUNCS
DEVICE_MMIO_TOPLEVEL_STATIC(hpet_regs, DT_DRV_INST(0)); DEVICE_MMIO_TOPLEVEL_STATIC(hpet_regs, DT_DRV_INST(0));
#define HPET_REG_ADDR(off) \ #define HPET_REG_ADDR(off) \
@ -105,17 +85,6 @@ DEVICE_MMIO_TOPLEVEL_STATIC(hpet_regs, DT_DRV_INST(0));
#define TIMER0_COMPARATOR_LOW_REG HPET_REG_ADDR(0x108) #define TIMER0_COMPARATOR_LOW_REG HPET_REG_ADDR(0x108)
#define TIMER0_COMPARATOR_HIGH_REG HPET_REG_ADDR(0x10c) #define TIMER0_COMPARATOR_HIGH_REG HPET_REG_ADDR(0x10c)
/**
* @brief Setup memory mappings needed to access HPET registers.
*
* This is called in sys_clock_driver_init() to setup any memory
* mappings needed to access HPET registers.
*/
static inline void hpet_mmio_init(void)
{
DEVICE_MMIO_TOPLEVEL_MAP(hpet_regs, K_MEM_CACHE_NONE);
}
/** /**
* @brief Return the value of the main counter. * @brief Return the value of the main counter.
* *
@ -210,6 +179,19 @@ static inline void hpet_timer_conf_set(uint32_t val)
sys_write32(val, TIMER0_CONF_REG); sys_write32(val, TIMER0_CONF_REG);
} }
/*
* The following register access functions should work on generic x86
* hardware. If the targeted SoC requires special handling of HPET
* registers, these functions will need to be implemented in the SoC
* layer by first defining the macro HPET_USE_CUSTOM_REG_ACCESS_FUNCS
* in soc.h to signal such intent.
*
* This is a list of functions which must be implemented in the SoC
* layer:
* void hpet_timer_comparator_set(uint32_t val)
*/
#ifndef HPET_USE_CUSTOM_REG_ACCESS_FUNCS
/** /**
* @brief Write to the Timer Comparator Value Register * @brief Write to the Timer Comparator Value Register
* *
@ -239,6 +221,21 @@ static inline void hpet_timer_comparator_set(uint64_t val)
#define HPET_CMP_MIN_DELAY (1000) #define HPET_CMP_MIN_DELAY (1000)
#endif #endif
/*
* HPET_INT_LEVEL_TRIGGER is used to set HPET interrupt as level trigger
* for ARM CPU with NVIC like EHL PSE, whose DTS interrupt setting
* has no "sense" cell.
*/
#if (DT_INST_IRQ_HAS_CELL(0, sense))
#ifdef HPET_INT_LEVEL_TRIGGER
__WARN("HPET_INT_LEVEL_TRIGGER has no effect, DTS setting is used instead")
#undef HPET_INT_LEVEL_TRIGGER
#endif
#if ((DT_INST_IRQ(0, sense) & IRQ_TYPE_LEVEL) == IRQ_TYPE_LEVEL)
#define HPET_INT_LEVEL_TRIGGER
#endif
#endif /* (DT_INST_IRQ_HAS_CELL(0, sense)) */
static __pinned_bss struct k_spinlock lock; static __pinned_bss struct k_spinlock lock;
static __pinned_bss uint64_t last_count; static __pinned_bss uint64_t last_count;
@ -260,7 +257,7 @@ static void hpet_isr(const void *arg)
uint64_t now = hpet_counter_get(); uint64_t now = hpet_counter_get();
#if ((DT_INST_IRQ(0, sense) & IRQ_TYPE_LEVEL) == IRQ_TYPE_LEVEL) #ifdef HPET_INT_LEVEL_TRIGGER
/* /*
* Clear interrupt only if level trigger is selected. * Clear interrupt only if level trigger is selected.
* When edge trigger is selected, spec says only 0 can * When edge trigger is selected, spec says only 0 can
@ -300,18 +297,22 @@ static void hpet_isr(const void *arg)
} }
__pinned_func __pinned_func
static void set_timer0_irq(unsigned int irq) static void config_timer0(unsigned int irq)
{ {
uint32_t val = hpet_timer_conf_get(); uint32_t val = hpet_timer_conf_get();
/* 5-bit IRQ field starting at bit 9 */ /* 5-bit IRQ field starting at bit 9 */
val = (val & ~(0x1f << 9)) | ((irq & 0x1f) << 9); val = (val & ~(0x1f << 9)) | ((irq & 0x1f) << 9);
#if ((DT_INST_IRQ(0, sense) & IRQ_TYPE_LEVEL) == IRQ_TYPE_LEVEL) #ifdef HPET_INT_LEVEL_TRIGGER
/* Level trigger */ /* Set level trigger if selected */
val |= TIMER_CONF_INT_LEVEL; val |= TIMER_CONF_INT_LEVEL;
#endif #endif
val &= ~((uint32_t)(TIMER_CONF_MODE32 | TIMER_CONF_PERIODIC |
TIMER_CONF_FSB_EN));
val |= TIMER_CONF_INT_ENABLE;
hpet_timer_conf_set(val); hpet_timer_conf_set(val);
} }
@ -325,12 +326,18 @@ int sys_clock_driver_init(const struct device *dev)
ARG_UNUSED(hz); ARG_UNUSED(hz);
ARG_UNUSED(z_clock_hw_cycles_per_sec); ARG_UNUSED(z_clock_hw_cycles_per_sec);
hpet_mmio_init(); DEVICE_MMIO_TOPLEVEL_MAP(hpet_regs, K_MEM_CACHE_NONE);
#if DT_INST_IRQ_HAS_CELL(0, sense)
IRQ_CONNECT(DT_INST_IRQN(0), IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority), DT_INST_IRQ(0, priority),
hpet_isr, 0, DT_INST_IRQ(0, sense)); hpet_isr, 0, DT_INST_IRQ(0, sense));
set_timer0_irq(DT_INST_IRQN(0)); #else
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
hpet_isr, 0, 0);
#endif
config_timer0(DT_INST_IRQN(0));
irq_enable(DT_INST_IRQN(0)); irq_enable(DT_INST_IRQN(0));
#ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME #ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
@ -349,13 +356,6 @@ int sys_clock_driver_init(const struct device *dev)
reg |= GCONF_LR | GCONF_ENABLE; reg |= GCONF_LR | GCONF_ENABLE;
hpet_gconf_set(reg); hpet_gconf_set(reg);
reg = hpet_timer_conf_get();
reg &= ~TIMER_CONF_PERIODIC;
reg &= ~TIMER_CONF_FSB_EN;
reg &= ~TIMER_CONF_MODE32;
reg |= TIMER_CONF_INT_ENABLE;
hpet_timer_conf_set(reg);
last_count = hpet_counter_get(); last_count = hpet_counter_get();
if (cyc_per_tick >= HPET_CMP_MIN_DELAY) { if (cyc_per_tick >= HPET_CMP_MIN_DELAY) {
hpet_timer_comparator_set(last_count + cyc_per_tick); hpet_timer_comparator_set(last_count + cyc_per_tick);