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:
parent
eeb15aa393
commit
a56c42574e
2 changed files with 44 additions and 45 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue