summaryrefslogtreecommitdiff
path: root/chip/npcx/hwtimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/npcx/hwtimer.c')
-rw-r--r--chip/npcx/hwtimer.c255
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);
+}