Skip to content

Commit

Permalink
Merge pull request #1065 from uknoblic/cc2538_speed
Browse files Browse the repository at this point in the history
CC2538: Allow for configuration of processor clocks
  • Loading branch information
bthebaudeau committed May 19, 2015
2 parents d1f9768 + d8efa84 commit a6c5a49
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 38 deletions.
35 changes: 23 additions & 12 deletions cpu/cc2538/clock.c
Expand Up @@ -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
Expand All @@ -62,7 +62,19 @@
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#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;
/*---------------------------------------------------------------------------*/
Expand All @@ -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)
Expand All @@ -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
Expand Down
12 changes: 2 additions & 10 deletions cpu/cc2538/dev/i2c.c
Expand Up @@ -43,15 +43,7 @@

#include <stdint.h>
#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,
Expand Down Expand Up @@ -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;
}
/*---------------------------------------------------------------------------*/
Expand Down
4 changes: 2 additions & 2 deletions cpu/cc2538/dev/spi.c
Expand Up @@ -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,
Expand Down
26 changes: 20 additions & 6 deletions cpu/cc2538/dev/sys-ctrl.c
Expand Up @@ -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 */
Expand All @@ -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);
}
/**
* @}
* @}
Expand Down
35 changes: 35 additions & 0 deletions cpu/cc2538/dev/sys-ctrl.h
Expand Up @@ -42,6 +42,8 @@
*/
#ifndef SYS_CTRL_H_
#define SYS_CTRL_H_

#include <stdint.h>
/*---------------------------------------------------------------------------*/
/** \name SysCtrl Constants, used by the SYS_DIV and IO_DIV bits of the
* SYS_CTRL_CLOCK_CTRL register
Expand Down Expand Up @@ -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
* @{
*/
Expand All @@ -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_ */
Expand Down
2 changes: 1 addition & 1 deletion cpu/cc2538/dev/uart.c
Expand Up @@ -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))

Expand Down
31 changes: 24 additions & 7 deletions cpu/cc2538/lpm.c
Expand Up @@ -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()
Expand Down Expand Up @@ -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. */
Expand All @@ -163,18 +163,35 @@ 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
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);
Expand Down

0 comments on commit a6c5a49

Please sign in to comment.