/* arcv2_timer0.c - ARC timer 0 device driver */ /* * Copyright (c) 2014-2015 Wind River Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3) Neither the name of Wind River Systems nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* DESCRIPTION This module implements a kernel device driver for the ARCv2 processor timer 0 and provides the standard "system clock driver" interfaces. \INTERNAL IMPLEMENTATION DETAILS The ARCv2 processor timer provides a 32-bit incrementing, wrap-to-zero counter. */ #include #include #include #include #include #include #include #include /* * A board support package's board.h header must provide definitions for the * following constants: * * CONFIG_ARCV2_TIMER0_CLOCK_FREQ * * This is the sysTick input clock frequency. */ #include #define _ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ #define _ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ #define _ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ #define _ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ /* running total of timer count */ static uint32_t clock_accumulated_count = 0; /** * * @brief Enable the timer with the given limit/countup value * * This routine sets up the timer for operation by: * - setting value to which the timer will count up to; * - setting the timer's start value to zero; and * - enabling interrupt generation. * * @return N/A * * \NOMANUAL */ static ALWAYS_INLINE void enable( uint32_t count /* interrupt triggers when up-counter reaches this value */ ) { _arc_v2_aux_reg_write(_ARC_V2_TMR0_LIMIT, count); /* write limit value */ /* count only when not halted for debug and enable interrupts */ _arc_v2_aux_reg_write(_ARC_V2_TMR0_CONTROL, _ARC_V2_TMR_CTRL_NH | _ARC_V2_TMR_CTRL_IE); _arc_v2_aux_reg_write(_ARC_V2_TMR0_COUNT, 0); /* write the start value */ } /** * * @brief Get the current counter value * * This routine gets the value from the timer's count register. This * value is the 'time' elapsed from the starting count (assumed to be 0). * * @return the current counter value * * \NOMANUAL */ static ALWAYS_INLINE uint32_t count_get(void) { return _arc_v2_aux_reg_read(_ARC_V2_TMR0_COUNT); } /** * * @brief Get the limit/countup value * * This routine gets the value from the timer's limit register, which is the * value to which the timer will count up to. * * @return the limit value * * \NOMANUAL */ static ALWAYS_INLINE uint32_t limit_get(void) { return _arc_v2_aux_reg_read(_ARC_V2_TMR0_LIMIT); } /** * * @brief System clock periodic tick handler * * This routine handles the system clock periodic tick interrupt. A TICK_EVENT * event is pushed onto the microkernel stack. * * @return N/A * * \NOMANUAL */ void _timer_int_handler(void *unused) { uint32_t zero_ip_bit = _ARC_V2_TMR_CTRL_NH | _ARC_V2_TMR_CTRL_IE; ARG_UNUSED(unused); /* clear the interrupt by writing 0 to IP bit of the control register */ _arc_v2_aux_reg_write(_ARC_V2_TMR0_CONTROL, zero_ip_bit); clock_accumulated_count += sys_clock_hw_cycles_per_tick; _sys_clock_tick_announce(); } /** * * @brief Initialize and enable the system clock * * This routine is used to program the ARCv2 timer to deliver interrupts at the * rate specified via the 'sys_clock_us_per_tick' global variable. * * @return 0 */ int _sys_clock_driver_init(struct device *device) { int irq = CONFIG_ARCV2_TIMER0_INT_LVL; int prio = CONFIG_ARCV2_TIMER0_INT_PRI; ARG_UNUSED(device); /* ensure that the timer will not generate interrupts */ _arc_v2_aux_reg_write(_ARC_V2_TMR0_CONTROL, 0); _arc_v2_aux_reg_write(_ARC_V2_TMR0_COUNT, 0); /* clear the count value */ (void)irq_connect(irq, prio, _timer_int_handler, 0); /* * Set the reload value to achieve the configured tick rate, enable the * counter and interrupt generation. */ enable(sys_clock_hw_cycles_per_tick - 1); /* everything has been configured: safe to enable the interrupt */ irq_enable(CONFIG_ARCV2_TIMER0_INT_LVL); return 0; } /** * * @brief Read the platform's timer hardware * * This routine returns the current time in terms of timer hardware clock * cycles. * * @return up counter of elapsed clock cycles */ uint32_t _sys_clock_cycle_get(void) { return (clock_accumulated_count + count_get()); } FUNC_ALIAS(_sys_clock_cycle_get, nano_cycle_get_32, uint32_t); FUNC_ALIAS(_sys_clock_cycle_get, task_cycle_get_32, uint32_t); #if defined(CONFIG_SYSTEM_TIMER_DISABLE) /** * * @brief Stop announcing ticks into the kernel * * This routine disables timer interrupt generation and delivery. * Note that the timer's counting cannot be stopped by software. * * @return N/A */ void timer_disable(void) { unsigned int key; /* interrupt lock level */ uint32_t ctrl_val; /* timer control register value */ key = irq_lock(); /* disable interrupt generation */ ctrl_val = _arc_v2_aux_reg_read(_ARC_V2_TMR0_CONTROL); _arc_v2_aux_reg_write(_ARC_V2_TMR0_CONTROL, ctrl_val & ~_ARC_V2_TMR_CTRL_IE); irq_unlock(key); /* disable interrupt in the interrupt controller */ irq_disable(CONFIG_ARCV2_TIMER0_INT_LVL); } #endif /* CONFIG_SYSTEM_TIMER_DISABLE */