/* * Copyright (c) 2012, Texas Instruments Incorporated - http://www.ti.com/ * All rights reserved. * * 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 the copyright holder 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. */ /** * \addtogroup cc2538 * @{ * * \defgroup cc2538-clock cc2538 Clock * * Implementation of the clock module for the cc2538 * * To implement the clock functionality, we use the SysTick peripheral on the * cortex-M3. We run the system clock at a configurable speed and set the * SysTick to give us 128 interrupts / sec. However, the Sleep Timer counter * value is used for the number of elapsed ticks in order to avoid a * significant time drift caused by PM1/2. Contrary to the Sleep Timer, the * SysTick peripheral is indeed frozen during PM1/2, so adjusting upon wake-up * a tick counter based on this peripheral would hardly be accurate. * @{ * * \file * Clock driver implementation for the TI cc2538 */ #include "contiki.h" #include "cc2538_cm3.h" #include "reg.h" #include "cpu.h" #include "dev/gptimer.h" #include "dev/sys-ctrl.h" #include "sys/energest.h" #include "sys/etimer.h" #include "sys/rtimer.h" #include /*---------------------------------------------------------------------------*/ #define RTIMER_CLOCK_TICK_RATIO (RTIMER_SECOND / CLOCK_SECOND) /* Prescaler for GPT0:Timer A used for clock_delay_usec(). */ #if SYS_CTRL_SYS_CLOCK < SYS_CTRL_1MHZ #error System clock speeds below 1MHz are not supported #endif #define PRESCALER_VALUE (SYS_CTRL_SYS_CLOCK / SYS_CTRL_1MHZ - 1) /* Period of the SysTick counter expressed as a number of ticks */ #if SYS_CTRL_SYS_CLOCK % CLOCK_SECOND /* Too low clock speeds will lead to reduced accurracy */ #error System clock speed too slow for CLOCK_SECOND, accuracy reduced #endif #define SYSTICK_PERIOD (SYS_CTRL_SYS_CLOCK / CLOCK_SECOND) static volatile uint64_t rt_ticks_startup = 0, rt_ticks_epoch = 0; /*---------------------------------------------------------------------------*/ /** * \brief Arch-specific implementation of clock_init for the cc2538 * * We initialise the SysTick to fire 128 interrupts per second, giving us a * value of 128 for CLOCK_SECOND * * We also initialise GPT0:Timer A, which is used by clock_delay_usec(). * We use 16-bit range (individual), count-down, one-shot, no interrupts. * The prescaler is computed according to the system clock in order to get 1 * tick per usec. */ void clock_init(void) { SysTick_Config(SYSTICK_PERIOD); /* * Remove the clock gate to enable GPT0 and then initialise it * We only use GPT0 for clock_delay_usec. We initialise it here so we can * have it ready when it's needed */ REG(SYS_CTRL_RCGCGPT) |= SYS_CTRL_RCGCGPT_GPT0; /* Make sure GPT0 is off */ REG(GPT_0_BASE + GPTIMER_CTL) = 0; /* 16-bit */ REG(GPT_0_BASE + GPTIMER_CFG) = 0x04; /* One-Shot, Count Down, No Interrupts */ REG(GPT_0_BASE + GPTIMER_TAMR) = GPTIMER_TAMR_TAMR_ONE_SHOT; /* Prescale depending on system clock used */ REG(GPT_0_BASE + GPTIMER_TAPR) = PRESCALER_VALUE; } /*---------------------------------------------------------------------------*/ clock_time_t clock_time(void) { return rt_ticks_startup / RTIMER_CLOCK_TICK_RATIO; } /*---------------------------------------------------------------------------*/ void clock_set_seconds(unsigned long sec) { rt_ticks_epoch = (uint64_t)sec * RTIMER_SECOND; } /*---------------------------------------------------------------------------*/ unsigned long clock_seconds(void) { return rt_ticks_epoch / RTIMER_SECOND; } /*---------------------------------------------------------------------------*/ void clock_wait(clock_time_t i) { clock_time_t start; start = clock_time(); while(clock_time() - start < (clock_time_t)i); } /*---------------------------------------------------------------------------*/ /* * Arch-specific implementation of clock_delay_usec for the cc2538 * * See clock_init() for GPT0 Timer A's configuration */ void clock_delay_usec(uint16_t dt) { REG(GPT_0_BASE + GPTIMER_TAILR) = dt; REG(GPT_0_BASE + GPTIMER_CTL) |= GPTIMER_CTL_TAEN; /* One-Shot mode: TAEN will be cleared when the timer reaches 0 */ while(REG(GPT_0_BASE + GPTIMER_CTL) & GPTIMER_CTL_TAEN); } /*---------------------------------------------------------------------------*/ /** * \brief Obsolete delay function but we implement it here since some code * still uses it */ void clock_delay(unsigned int i) { clock_delay_usec(i); } /*---------------------------------------------------------------------------*/ /** * \brief Update the software clock ticks and seconds * * This function is used to update the software tick counters whenever the * system clock might have changed, which can occur upon a SysTick ISR or upon * wake-up from PM1/2. * * For the software clock ticks counter, the Sleep Timer counter value is used * as the base tick value, and extended to a 64-bit value thanks to a detection * of wraparounds. * * For the seconds counter, the changes of the Sleep Timer counter value are * added to the reference time, which is either the startup time or the value * passed to clock_set_seconds(). * * This function polls the etimer process if an etimer has expired. */ static void update_ticks(void) { rtimer_clock_t now; uint64_t prev_rt_ticks_startup, cur_rt_ticks_startup; uint32_t cur_rt_ticks_startup_hi; now = RTIMER_NOW(); prev_rt_ticks_startup = rt_ticks_startup; cur_rt_ticks_startup_hi = prev_rt_ticks_startup >> 32; if(now < (rtimer_clock_t)prev_rt_ticks_startup) { cur_rt_ticks_startup_hi++; } cur_rt_ticks_startup = (uint64_t)cur_rt_ticks_startup_hi << 32 | now; rt_ticks_startup = cur_rt_ticks_startup; rt_ticks_epoch += cur_rt_ticks_startup - prev_rt_ticks_startup; /* * Inform the etimer library that the system clock has changed and that an * etimer might have expired. */ if(etimer_pending()) { etimer_request_poll(); } } /*---------------------------------------------------------------------------*/ /** * \brief Adjust the clock following missed SysTick ISRs * * This function is useful when coming out of PM1/2, during which the system * clock is stopped. We adjust the clock counters like after any SysTick ISR. * * \note This function is only meant to be used by lpm_exit(). Applications * should really avoid calling this */ void clock_adjust(void) { /* Halt the SysTick while adjusting */ SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; update_ticks(); /* Re-Start the SysTick */ SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } /*---------------------------------------------------------------------------*/ /** * \brief The clock Interrupt Service Routine * * It polls the etimer process if an etimer has expired. It also updates the * software clock tick and seconds counter. */ void clock_isr(void) { update_ticks(); } /*---------------------------------------------------------------------------*/ /** * @} * @} */