diff options
Diffstat (limited to 'chip/npcx/hwtimer.c')
-rw-r--r-- | chip/npcx/hwtimer.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/chip/npcx/hwtimer.c b/chip/npcx/hwtimer.c new file mode 100644 index 0000000000..80bb903e67 --- /dev/null +++ b/chip/npcx/hwtimer.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Hardware timers driver */ + +#include "clock.h" +#include "clock_chip.h" +#include "common.h" +#include "hooks.h" +#include "hwtimer.h" +#include "hwtimer_chip.h" +#include "registers.h" +#include "task.h" +#include "timer.h" + +/* (2^TICK_ITIM_DEPTH us) between 2 ticks of timer */ +#define TICK_ITIM_DEPTH 16 /* Depth of ITIM Unit: bits */ +#define TICK_INTERVAL (1 << TICK_ITIM_DEPTH) /* Unit: us */ +#define TICK_INTERVAL_MASK (TICK_INTERVAL - 1) /* Mask of interval */ +#define TICK_ITIM_MAX_CNT (TICK_INTERVAL - 1) /* Maximum counter value */ + +/* 32-bits counter value */ +static volatile uint32_t cur_cnt_us; +static volatile uint32_t pre_cnt_us; +/* Time when event will be expired unit:us */ +static volatile uint32_t evt_expired_us; +/* 32-bits event counter */ +static volatile uint32_t evt_cnt; +/* Debugger information */ +#if DEBUG_TMR +static volatile uint32_t evt_cnt_us_dbg; +static volatile uint32_t cur_cnt_us_dbg; +#endif + +/*****************************************************************************/ +/* Internal functions */ +void init_hw_timer(int itim_no, enum ITIM16_SOURCE_CLOCK_T source) +{ + /* Use internal 32K clock/APB2 for ITIM16 */ + UPDATE_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_CKSEL, + source != ITIM16_SOURCE_CLOCK_APB2); + + /* Clear timeout status */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_STS); + + /* ITIM timeout interrupt enable */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_IE); + + /* ITIM timeout wake-up enable */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_WUE); +} + +/*****************************************************************************/ +/* HWTimer event handlers */ +void __hw_clock_event_set(uint32_t deadline) +{ + float inv_evt_tick = INT_32K_CLOCK/(float)SECOND; + uint32_t evt_cnt_us; + /* Is deadline min value? */ + if (evt_expired_us != 0 && evt_expired_us < deadline) + return; + + /* mark min event value */ + evt_expired_us = deadline; + evt_cnt_us = deadline - __hw_clock_source_read(); +#if DEBUG_TMR + evt_cnt_us_dbg = deadline - __hw_clock_source_read(); +#endif + + /* Event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + /* + * ITIM count down : event expired : Unit: 1/32768 sec + * It must exceed evt_expired_us for process_timers function + */ + evt_cnt = ((uint32_t)(evt_cnt_us*inv_evt_tick)+1)-1; + if (evt_cnt > TICK_ITIM_MAX_CNT) + evt_cnt = TICK_ITIM_MAX_CNT; + NPCX_ITCNT16(ITIM_EVENT_NO) = evt_cnt; + + /* Event module enable */ + SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Enable interrupt of ITIM */ + task_enable_irq(ITIM16_INT(ITIM_EVENT_NO)); +} + +/* Returns the time-stamp of the next programmed event */ +uint32_t __hw_clock_event_get(void) +{ + return evt_expired_us; +} + +/* Returns time delay cause of deep idle */ +uint32_t __hw_clock_get_sleep_time(void) +{ + float evt_tick = SECOND/(float)INT_32K_CLOCK; + uint32_t sleep_time; + uint32_t cnt = NPCX_ITCNT16(ITIM_EVENT_NO); + + interrupt_disable(); + /* Event has been triggered but timer ISR dosen't handle it */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_TO_STS)) + sleep_time = (uint32_t) (evt_cnt+1)*evt_tick; + /* Event hasn't been triggered */ + else + sleep_time = (uint32_t) (evt_cnt+1 - cnt)*evt_tick; + interrupt_enable(); + + return sleep_time; +} + +/* Cancel the next event programmed by __hw_clock_event_set */ +void __hw_clock_event_clear(void) +{ + /* ITIM event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Disable interrupt of Event */ + task_disable_irq(ITIM16_INT(ITIM_EVENT_NO)); + + /* Clear event parameters */ + evt_expired_us = 0; + evt_cnt = 0; +} + +/* Irq for hwtimer event */ +void __hw_clock_event_irq(void) +{ + int delay; + /* Clear timeout status for event */ + SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_TO_STS); + + /* ITIM event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Disable interrupt of event */ + task_disable_irq(ITIM16_INT(ITIM_EVENT_NO)); + + /* Workaround for tick interrupt latency */ + delay = evt_expired_us - __hw_clock_source_read(); + if (delay > 0) + cur_cnt_us += delay; + + /* Clear event parameters */ + evt_expired_us = 0; + evt_cnt = 0; + + /* handle upper driver */ + process_timers(0); +} +DECLARE_IRQ(ITIM16_INT(ITIM_EVENT_NO) , __hw_clock_event_irq, 1); + + +/*****************************************************************************/ +/* HWTimer tick handlers */ + +/* Returns the value of the free-running counter used as clock. */ +uint32_t __hw_clock_source_read(void) +{ + uint32_t us; + uint32_t cnt = NPCX_ITCNT16(ITIM_TIME_NO); + /* Is timeout expired? - but timer ISR dosen't handle it */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS)) + us = TICK_INTERVAL; + else + us = TICK_INTERVAL - cnt; + +#if DEBUG_TMR + cur_cnt_us_dbg = cur_cnt_us + us; +#endif + return cur_cnt_us + us; +} + +/* Override the current value of the hardware counter */ +void __hw_clock_source_set(uint32_t ts) +{ + /* Set current time */ + cur_cnt_us = ts; +} + +/* Irq for hwtimer tick */ +void __hw_clock_source_irq(void) +{ + /* Is timeout trigger trigger? */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS)) { + /* Clear timeout status*/ + SET_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS); + + /* Store previous time counter value */ + pre_cnt_us = cur_cnt_us; + /* Increase TICK_INTERVAL unit:us */ + cur_cnt_us += TICK_INTERVAL; + + /* Is 32-bits timer count overflow? */ + if (pre_cnt_us > cur_cnt_us) + process_timers(1); + + } else { /* Handle soft trigger */ + process_timers(0); + } +} +DECLARE_IRQ(NPCX_IRQ_ITIM16_1, __hw_clock_source_irq, 1); + +static void update_prescaler(void) +{ + /* + * prescaler to time tick + * Ttick_unit = (PRE_8+1) * Tapb2_clk + * PRE_8 = (Ttick_unit/Tapb2_clk) -1 + */ + NPCX_ITPRE(ITIM_TIME_NO) = (clock_get_apb2_freq() / SECOND) - 1; + /* Set event tick unit = 1/32768 sec */ + NPCX_ITPRE(ITIM_EVENT_NO) = 0; + +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); + +int __hw_clock_source_init(uint32_t start_t) +{ + /* + * 1. Use ITIM16-1 as internal time reading + * 2. Use ITIM16-2 for event handling + */ + + /* Enable clock for ITIM peripheral */ + clock_enable_peripheral(CGC_OFFSET_TIMER, CGC_TIMER_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + + /* init tick & event timer first */ + init_hw_timer(ITIM_TIME_NO, ITIM16_SOURCE_CLOCK_APB2); + init_hw_timer(ITIM_EVENT_NO, ITIM16_SOURCE_CLOCK_32K); + + /* Set initial prescaler */ + update_prescaler(); + + /* ITIM count down : TICK_INTERVAL expired*/ + NPCX_ITCNT16(ITIM_TIME_NO) = TICK_ITIM_MAX_CNT; + + /* + * Override the count with the start value now that counting has + * started. + */ + __hw_clock_source_set(start_t); + + /* ITIM module enable */ + SET_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_ITEN); + + /* Enable interrupt of ITIM */ + task_enable_irq(ITIM16_INT(ITIM_TIME_NO)); + + return ITIM16_INT(ITIM_TIME_NO); +} |