From d8efa8428c352628fc36cee4c5b2105006e7f1aa Mon Sep 17 00:00:00 2001 From: Ulf Knoblich Date: Tue, 12 May 2015 14:06:40 +0200 Subject: [PATCH] cc2538: Allow for configuration of processor speed --- cpu/cc2538/clock.c | 35 +++++++++++++++++++++++------------ cpu/cc2538/dev/i2c.c | 12 ++---------- cpu/cc2538/dev/spi.c | 4 ++-- cpu/cc2538/dev/sys-ctrl.c | 26 ++++++++++++++++++++------ cpu/cc2538/dev/sys-ctrl.h | 35 +++++++++++++++++++++++++++++++++++ cpu/cc2538/dev/uart.c | 2 +- cpu/cc2538/lpm.c | 31 ++++++++++++++++++++++++------- 7 files changed, 107 insertions(+), 38 deletions(-) diff --git a/cpu/cc2538/clock.c b/cpu/cc2538/clock.c index c0abed38b..953f06646 100644 --- a/cpu/cc2538/clock.c +++ b/cpu/cc2538/clock.c @@ -37,12 +37,12 @@ * 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 16 MHz and we 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. + * 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 @@ -62,7 +62,19 @@ #include /*---------------------------------------------------------------------------*/ #define RTIMER_CLOCK_TICK_RATIO (RTIMER_SECOND / CLOCK_SECOND) -#define RELOAD_VALUE (125000 - 1) /** Fire 128 times / sec */ + +/* 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) + +/* Reload value for SysTick counter */ +#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 RELOAD_VALUE (SYS_CTRL_SYS_CLOCK / CLOCK_SECOND - 1) static volatile uint64_t rt_ticks_startup = 0, rt_ticks_epoch = 0; /*---------------------------------------------------------------------------*/ @@ -74,8 +86,8 @@ static volatile uint64_t rt_ticks_startup = 0, rt_ticks_epoch = 0; * * 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 system clock is at 16MHz giving us 62.5 nano sec ticks for Timer A. - * Prescaled by 16 gives us a very convenient 1 tick per usec + * The prescaler is computed according to the system clock in order to get 1 + * tick per usec. */ void clock_init(void) @@ -98,15 +110,14 @@ clock_init(void) /* 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 by 16 (thus, value 15 in TAPR) */ - REG(GPT_0_BASE + GPTIMER_TAPR) = 0x0F; + /* Prescale depending on system clock used */ + REG(GPT_0_BASE + GPTIMER_TAPR) = PRESCALER_VALUE; } /*---------------------------------------------------------------------------*/ CCIF clock_time_t diff --git a/cpu/cc2538/dev/i2c.c b/cpu/cc2538/dev/i2c.c index a4683bdc5..76cac20f8 100644 --- a/cpu/cc2538/dev/i2c.c +++ b/cpu/cc2538/dev/i2c.c @@ -43,15 +43,7 @@ #include #include "clock.h" -/*---------------------------------------------------------------------------*/ -/* Additional functions */ -static uint32_t -get_sys_clock(void) -{ - /* Get the clock status diviser */ - return SYS_CTRL_32MHZ / - (1 << (REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYS_DIV)); -} +#include "sys-ctrl.h" /*---------------------------------------------------------------------------*/ void i2c_init(uint8_t port_sda, uint8_t pin_sda, uint8_t port_scl, uint8_t pin_scl, @@ -111,7 +103,7 @@ void i2c_set_frequency(uint32_t freq) { /* Peripheral clock setting, using the system clock */ - REG(I2CM_TPR) = ((get_sys_clock() + (2 * 10 * freq) - 1) / + REG(I2CM_TPR) = ((SYS_CTRL_SYS_CLOCK + (2 * 10 * freq) - 1) / (2 * 10 * freq)) - 1; } /*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2538/dev/spi.c b/cpu/cc2538/dev/spi.c index 6b363bd7b..48048a6a3 100644 --- a/cpu/cc2538/dev/spi.c +++ b/cpu/cc2538/dev/spi.c @@ -226,8 +226,8 @@ spix_init(uint8_t spi) /* Start by disabling the peripheral before configuring it */ REG(regs->base + SSI_CR1) = 0; - /* Set the IO clock as the SSI clock */ - REG(regs->base + SSI_CC) = 1; + /* Set the system clock as the SSI clock */ + REG(regs->base + SSI_CC) = 0; /* Set the mux correctly to connect the SSI pins to the correct GPIO pins */ ioc_set_sel(regs->clk.port, diff --git a/cpu/cc2538/dev/sys-ctrl.c b/cpu/cc2538/dev/sys-ctrl.c index 87eabfa44..c2e8777ad 100644 --- a/cpu/cc2538/dev/sys-ctrl.c +++ b/cpu/cc2538/dev/sys-ctrl.c @@ -70,17 +70,18 @@ sys_ctrl_init() * 32KHz source: RC or crystal, according to SYS_CTRL_OSC32K_USE_XTAL * System Clock: 32 MHz * Power Down Unused - * I/O Div: 16MHz - * Sys Div: 16MHz + * I/O Div: according to SYS_CTRL_IO_DIV + * Sys Div: according to SYS_CTRL_SYS_DIV * Rest: Don't care */ val = SYS_CTRL_OSCS | SYS_CTRL_CLOCK_CTRL_OSC_PD - | SYS_CTRL_CLOCK_CTRL_IO_DIV_16MHZ | SYS_CTRL_CLOCK_CTRL_SYS_DIV_16MHZ; + | SYS_CTRL_IO_DIV | SYS_CTRL_SYS_DIV; REG(SYS_CTRL_CLOCK_CTRL) = val; - while((REG(SYS_CTRL_CLOCK_STA) & (SYS_CTRL_CLOCK_STA_OSC32K | - SYS_CTRL_CLOCK_STA_OSC)) != SYS_CTRL_OSCS); + while((REG(SYS_CTRL_CLOCK_STA) + & (SYS_CTRL_CLOCK_STA_OSC32K | SYS_CTRL_CLOCK_STA_OSC)) + != SYS_CTRL_OSCS); #if SYS_CTRL_OSC32K_USE_XTAL /* Wait for the 32-kHz crystal oscillator to stabilize */ @@ -94,7 +95,20 @@ sys_ctrl_reset() { REG(SYS_CTRL_PWRDBG) = SYS_CTRL_PWRDBG_FORCE_WARM_RESET; } - +/*---------------------------------------------------------------------------*/ +uint32_t +sys_ctrl_get_sys_clock(void) +{ + return SYS_CTRL_32MHZ >> (REG(SYS_CTRL_CLOCK_STA) & + SYS_CTRL_CLOCK_STA_SYS_DIV); +} +/*---------------------------------------------------------------------------*/ +uint32_t +sys_ctrl_get_io_clock(void) +{ + return SYS_CTRL_32MHZ >> ((REG(SYS_CTRL_CLOCK_STA) & + SYS_CTRL_CLOCK_STA_IO_DIV) >> 8); +} /** * @} * @} diff --git a/cpu/cc2538/dev/sys-ctrl.h b/cpu/cc2538/dev/sys-ctrl.h index 3c9885a89..a8b61932e 100644 --- a/cpu/cc2538/dev/sys-ctrl.h +++ b/cpu/cc2538/dev/sys-ctrl.h @@ -42,6 +42,8 @@ */ #ifndef SYS_CTRL_H_ #define SYS_CTRL_H_ + +#include /*---------------------------------------------------------------------------*/ /** \name SysCtrl Constants, used by the SYS_DIV and IO_DIV bits of the * SYS_CTRL_CLOCK_CTRL register @@ -242,6 +244,33 @@ #endif /** @} */ /*---------------------------------------------------------------------------*/ +/** \name System clock divisor selection + * @{ + */ +#ifdef SYS_CTRL_CONF_SYS_DIV +#if SYS_CTRL_CONF_SYS_DIV & ~SYS_CTRL_CLOCK_CTRL_SYS_DIV +#error Invalid system clock divisor +#endif +#define SYS_CTRL_SYS_DIV SYS_CTRL_CONF_SYS_DIV +#else +#define SYS_CTRL_SYS_DIV SYS_CTRL_CLOCK_CTRL_SYS_DIV_16MHZ +#endif + +#ifdef SYS_CTRL_CONF_IO_DIV +#if SYS_CTRL_CONF_IO_DIV & ~SYS_CTRL_CLOCK_CTRL_IO_DIV +#error Invalid I/O clock divisor +#endif +#define SYS_CTRL_IO_DIV SYS_CTRL_CONF_IO_DIV +#else +#define SYS_CTRL_IO_DIV SYS_CTRL_CLOCK_CTRL_IO_DIV_16MHZ +#endif + +/* Returns actual system clock in Hz */ +#define SYS_CTRL_SYS_CLOCK (SYS_CTRL_32MHZ >> SYS_CTRL_SYS_DIV) +/* Returns actual I/O clock in Hz */ +#define SYS_CTRL_IO_CLOCK (SYS_CTRL_32MHZ >> (SYS_CTRL_IO_DIV >> 8)) +/** @} */ +/*---------------------------------------------------------------------------*/ /** \name SysCtrl functions * @{ */ @@ -253,6 +282,12 @@ void sys_ctrl_init(); /** \brief Generates a warm reset through the SYS_CTRL_PWRDBG register */ void sys_ctrl_reset(); +/** \brief Returns the actual system clock in Hz */ +uint32_t sys_ctrl_get_sys_clock(); + +/** \brief Returns the actual io clock in Hz */ +uint32_t sys_ctrl_get_io_clock(); + /** @} */ #endif /* SYS_CTRL_H_ */ diff --git a/cpu/cc2538/dev/uart.c b/cpu/cc2538/dev/uart.c index ea6a6a57a..fbcc3c56c 100644 --- a/cpu/cc2538/dev/uart.c +++ b/cpu/cc2538/dev/uart.c @@ -136,7 +136,7 @@ * Baud rate defines used in uart_init() to set the values of UART_IBRD and * UART_FBRD in order to achieve the configured baud rates. */ -#define UART_CLOCK_RATE 16000000 /* 16 MHz */ +#define UART_CLOCK_RATE SYS_CTRL_SYS_CLOCK #define UART_CTL_HSE_VALUE 0 #define UART_CTL_VALUE (UART_CTL_RXE | UART_CTL_TXE | (UART_CTL_HSE_VALUE << 5)) diff --git a/cpu/cc2538/lpm.c b/cpu/cc2538/lpm.c index 6270e51b7..abe75c02e 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -78,8 +78,8 @@ static unsigned long irq_energest = 0; #if LPM_CONF_STATS rtimer_clock_t lpm_stats[3]; -#define LPM_STATS_INIT() do { memset(lpm_stats, 0, sizeof(lpm_stats)); \ - } while(0) +#define LPM_STATS_INIT() \ + do { memset(lpm_stats, 0, sizeof(lpm_stats)); } while(0) #define LPM_STATS_ADD(pm, val) do { lpm_stats[pm] += val; } while(0) #else #define LPM_STATS_INIT() @@ -154,7 +154,7 @@ enter_pm0(void) static void select_32_mhz_xosc(void) { - /*First, make sure there is no ongoing clock source change */ + /* First, make sure there is no ongoing clock source change */ while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SOURCE_CHANGE) != 0); /* Turn on the 32 MHz XOSC and source the system clock on it. */ @@ -163,8 +163,15 @@ select_32_mhz_xosc(void) /* Wait for the switch to take place */ while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_OSC) != 0); - /* Power down the unused oscillator. */ - REG(SYS_CTRL_CLOCK_CTRL) |= SYS_CTRL_CLOCK_CTRL_OSC_PD; + /* Power down the unused oscillator and restore divisors (silicon errata) */ + REG(SYS_CTRL_CLOCK_CTRL) = (REG(SYS_CTRL_CLOCK_CTRL) +#if SYS_CTRL_SYS_DIV == SYS_CTRL_CLOCK_CTRL_SYS_DIV_32MHZ + & ~SYS_CTRL_CLOCK_CTRL_SYS_DIV +#endif +#if SYS_CTRL_IO_DIV == SYS_CTRL_CLOCK_CTRL_IO_DIV_32MHZ + & ~SYS_CTRL_CLOCK_CTRL_IO_DIV +#endif + ) | SYS_CTRL_CLOCK_CTRL_OSC_PD; } /*---------------------------------------------------------------------------*/ static void @@ -172,9 +179,19 @@ select_16_mhz_rcosc(void) { /* * Power up both oscillators in order to speed up the transition to the 32-MHz - * XOSC after wake up. + * XOSC after wake up. In addition, consider CC2538 silicon errata: + * "Possible Incorrect Value of Clock Dividers after PM2 and PM3" and + * set system clock divisor / I/O clock divisor to 16 MHz in case they run + * at full speed (=32 MHz) */ - REG(SYS_CTRL_CLOCK_CTRL) &= ~SYS_CTRL_CLOCK_CTRL_OSC_PD; + REG(SYS_CTRL_CLOCK_CTRL) = (REG(SYS_CTRL_CLOCK_CTRL) +#if SYS_CTRL_SYS_DIV == SYS_CTRL_CLOCK_CTRL_SYS_DIV_32MHZ + | SYS_CTRL_CLOCK_CTRL_SYS_DIV_16MHZ +#endif +#if SYS_CTRL_IO_DIV == SYS_CTRL_CLOCK_CTRL_IO_DIV_32MHZ + | SYS_CTRL_CLOCK_CTRL_IO_DIV_16MHZ +#endif + ) & ~SYS_CTRL_CLOCK_CTRL_OSC_PD; /*First, make sure there is no ongoing clock source change */ while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SOURCE_CHANGE) != 0);