mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #20613 from maribu/cpu/msp430/pm
cpu/msp430: implement power management
This commit is contained in:
commit
59956fd371
@ -20,9 +20,11 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "atomic_utils.h"
|
||||
#include "busy_wait.h"
|
||||
#include "macros/math.h"
|
||||
#include "macros/units.h"
|
||||
@ -38,6 +40,7 @@
|
||||
#endif
|
||||
|
||||
uint32_t msp430_dco_freq;
|
||||
static uint8_t msp430_clock_refcounts[MSP430_CLOCK_NUMOF];
|
||||
|
||||
static inline bool is_dco_in_use(const msp430_clock_params_t *params)
|
||||
{
|
||||
@ -361,3 +364,57 @@ uint32_t PURE msp430_auxiliary_clock_freq(void)
|
||||
uint16_t shift = (clock_params.auxiliary_clock_divier >> 4) & 0x3;
|
||||
return clock_params.lfxt1_frequency >> shift;
|
||||
}
|
||||
|
||||
void msp430_clock_acquire(msp430_clock_t clock)
|
||||
{
|
||||
assume((unsigned)clock < MSP430_CLOCK_NUMOF);
|
||||
uint8_t before = atomic_fetch_add_u8(&msp430_clock_refcounts[clock], 1);
|
||||
(void)before;
|
||||
assert(before < UINT8_MAX);
|
||||
}
|
||||
|
||||
void msp430_clock_release(msp430_clock_t clock)
|
||||
{
|
||||
assume((unsigned)clock < MSP430_CLOCK_NUMOF);
|
||||
uint8_t before = atomic_fetch_sub_u8(&msp430_clock_refcounts[clock], 1);
|
||||
(void)before;
|
||||
assert(before > 0);
|
||||
}
|
||||
|
||||
void pm_set_lowest(void)
|
||||
{
|
||||
/* disable IRQs, wait two cycles for this to take effect, backup
|
||||
* state register */
|
||||
uint16_t state;
|
||||
__asm__ volatile(
|
||||
"bic %[gie], SR" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"mov.w SR, %[state]" "\n\t"
|
||||
: [state] "=r"(state)
|
||||
: [gie] "i"(GIE)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
/* When applying the power safe mode, we want to be able to wake up again.
|
||||
* So set global interrupt enable then. */
|
||||
state |= GIE;
|
||||
/* disabling CPU works always, even when keeping the clocks running */
|
||||
state |= CPUOFF | SCG0;
|
||||
|
||||
if (msp430_clock_refcounts[MSP430_CLOCK_SUBMAIN] == 0) {
|
||||
state |= SCG1;
|
||||
}
|
||||
|
||||
if (msp430_clock_refcounts[MSP430_CLOCK_AUXILIARY] == 0) {
|
||||
state |= OSCOFF;
|
||||
}
|
||||
|
||||
/* write new state */
|
||||
__asm__ volatile(
|
||||
"mov.w %[state], SR" "\n\t"
|
||||
: /* no outputs */
|
||||
: [state] "r"(state)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ extern "C" {
|
||||
*/
|
||||
#define WORDSIZE 16
|
||||
|
||||
/**
|
||||
* @brief MSP430 has power management support
|
||||
*/
|
||||
#define PROVIDES_PM_SET_LOWEST
|
||||
|
||||
/**
|
||||
* @brief Macro for defining interrupt service routines
|
||||
*/
|
||||
@ -94,6 +99,14 @@ static inline void __attribute__((always_inline)) __restore_context(void)
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) __enter_isr(void)
|
||||
{
|
||||
/* modify state register pushed to stack to not got to power saving
|
||||
* mode right again */
|
||||
__asm__ volatile(
|
||||
"bic %[mask], 0(SP)" "\n\t"
|
||||
: /* no outputs */
|
||||
: [mask] "i"(CPUOFF | SCG0 | SCG1 | OSCOFF)
|
||||
: "memory"
|
||||
);
|
||||
extern char __stack; /* defined by linker script to end of RAM */
|
||||
__save_context();
|
||||
__asm__("mov.w %0,r1" : : "i"(&__stack));
|
||||
|
@ -44,8 +44,8 @@ __attribute__((always_inline)) static inline unsigned int irq_disable(void)
|
||||
{
|
||||
unsigned int state;
|
||||
__asm__ volatile(
|
||||
"mov.w r2, %[state]" "\n\t"
|
||||
"bic %[gie], r2" "\n\t"
|
||||
"mov.w SR, %[state]" "\n\t"
|
||||
"bic %[gie], SR" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"and %[gie], %[state]" "\n\t"
|
||||
: [state] "=r"(state)
|
||||
@ -60,9 +60,9 @@ __attribute__((always_inline)) static inline unsigned int irq_enable(void)
|
||||
{
|
||||
unsigned int state;
|
||||
__asm__ volatile(
|
||||
"mov.w r2, %[state]" "\n\t"
|
||||
"mov.w SR, %[state]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"bis %[gie], r2" "\n\t"
|
||||
"bis %[gie], SR" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"and %[gie], %[state]" "\n\t"
|
||||
: [state] "=r"(state)
|
||||
@ -76,7 +76,7 @@ __attribute__((always_inline)) static inline unsigned int irq_enable(void)
|
||||
__attribute__((always_inline)) static inline void irq_restore(unsigned int state)
|
||||
{
|
||||
__asm__ volatile(
|
||||
"bis %[state], r2" "\n\t"
|
||||
"bis %[state], SR" "\n\t"
|
||||
"nop" "\n\t"
|
||||
: /* no outputs */
|
||||
: [state] "r"(state)
|
||||
@ -93,7 +93,7 @@ __attribute__((always_inline)) static inline bool irq_is_enabled(void)
|
||||
{
|
||||
unsigned int state;
|
||||
__asm__ volatile(
|
||||
"mov.w r2,%[state]" "\n\t"
|
||||
"mov.w SR,%[state]" "\n\t"
|
||||
: [state] "=r"(state)
|
||||
: /* no inputs */
|
||||
: "memory"
|
||||
|
@ -311,6 +311,17 @@ typedef enum {
|
||||
TIMER_CLOCK_SOURCE_INCLK = TXSSEL_INCLK, /**< External INCLK as clock source */
|
||||
} msp430_timer_clock_source_t;
|
||||
|
||||
/**
|
||||
* @brief IDs of the different clock domains on the MSP430
|
||||
*
|
||||
* These can be used as internal clock sources for peripherals
|
||||
*/
|
||||
typedef enum {
|
||||
MSP430_CLOCK_SUBMAIN, /**< Subsystem main clock */
|
||||
MSP430_CLOCK_AUXILIARY, /**< Auxiliary clock */
|
||||
MSP430_CLOCK_NUMOF, /**< Number of clock domains */
|
||||
} msp430_clock_t;
|
||||
|
||||
/**
|
||||
* @brief Timer configuration on an MSP430 timer
|
||||
*/
|
||||
@ -367,6 +378,28 @@ uint32_t PURE msp430_submain_clock_freq(void);
|
||||
*/
|
||||
uint32_t PURE msp430_auxiliary_clock_freq(void);
|
||||
|
||||
/**
|
||||
* @brief Increase the refcount of the given clock
|
||||
*
|
||||
* @param[in] clock clock domain to acquire
|
||||
*
|
||||
* @warning This is an internal function and must only be called from
|
||||
* peripheral drivers
|
||||
* @note An assertion will blow when the count exceeds capacity
|
||||
*/
|
||||
void msp430_clock_acquire(msp430_clock_t clock);
|
||||
|
||||
/**
|
||||
* @brief Decrease the refcount of the subsystem main clock
|
||||
*
|
||||
* @param[in] clock clock domain to acquire
|
||||
*
|
||||
* @warning This is an internal function and must only be called from
|
||||
* peripheral drivers
|
||||
* @note An assertion will blow when the count drops below zero
|
||||
*/
|
||||
void msp430_clock_release(msp430_clock_t clock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -30,8 +30,6 @@
|
||||
#include "compiler_hints.h"
|
||||
#include "cpu.h"
|
||||
#include "periph/timer.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
/**
|
||||
* @brief Interrupt context for each configured timer
|
||||
@ -114,8 +112,8 @@ int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
||||
for (unsigned i = 0; i < timer_query_channel_numof(dev); i++) {
|
||||
msptimer->CCTL[i] = 0;
|
||||
}
|
||||
/* start the timer in continuous mode */
|
||||
msptimer->CTL = ctl | TXMC_CONT;
|
||||
|
||||
timer_start(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -157,6 +155,18 @@ void timer_start(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
/* acquire clock */
|
||||
switch (timer_conf[dev].clock_source) {
|
||||
case TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK:
|
||||
msp430_clock_acquire(MSP430_CLOCK_SUBMAIN);
|
||||
break;
|
||||
case TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK:
|
||||
msp430_clock_acquire(MSP430_CLOCK_AUXILIARY);
|
||||
break;
|
||||
default:
|
||||
/* external clock source, safe to disable internal clocks */
|
||||
break;
|
||||
}
|
||||
msptimer->CTL |= TXMC_CONT;
|
||||
}
|
||||
|
||||
@ -164,7 +174,20 @@ void timer_stop(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
msptimer->CTL &= ~(TXMC_MASK);
|
||||
msptimer->CTL &= ~(TXMC_CONT);
|
||||
|
||||
/* release clock */
|
||||
switch (timer_conf[dev].clock_source) {
|
||||
case TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK:
|
||||
msp430_clock_release(MSP430_CLOCK_SUBMAIN);
|
||||
break;
|
||||
case TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK:
|
||||
msp430_clock_release(MSP430_CLOCK_AUXILIARY);
|
||||
break;
|
||||
default:
|
||||
/* external clock source, nothing to release */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((pure))
|
||||
|
@ -64,9 +64,12 @@ static mutex_t usart_locks[USART_NUMOF] = {
|
||||
MUTEX_INIT,
|
||||
};
|
||||
|
||||
/* store the clock acquired by each USART, so it can be release again */
|
||||
static msp430_usart_clk_t _clocks_acquired[USART_NUMOF];
|
||||
|
||||
void msp430_usart_acquire(const msp430_usart_params_t *params,
|
||||
const msp430_usart_conf_t *conf,
|
||||
uint8_t enable_mask)
|
||||
const msp430_usart_conf_t *conf,
|
||||
uint8_t enable_mask)
|
||||
{
|
||||
assume(params->num < USART_NUMOF);
|
||||
|
||||
@ -74,6 +77,19 @@ void msp430_usart_acquire(const msp430_usart_params_t *params,
|
||||
msp430_usart_t *dev = params->dev;
|
||||
msp430_usart_sfr_t *sfr = params->sfr;
|
||||
|
||||
_clocks_acquired[params->num] = conf->prescaler.clk_source;
|
||||
switch (_clocks_acquired[params->num]) {
|
||||
case USART_CLK_SUBMAIN:
|
||||
msp430_clock_acquire(MSP430_CLOCK_SUBMAIN);
|
||||
break;
|
||||
case USART_CLK_AUX:
|
||||
msp430_clock_acquire(MSP430_CLOCK_AUXILIARY);
|
||||
break;
|
||||
default:
|
||||
/* external clock from GPIO, safe to disable internal clocks */
|
||||
break;
|
||||
}
|
||||
|
||||
/* first, make sure USART is off before reconfiguring it */
|
||||
sfr->ME = 0;
|
||||
/* reset USART */
|
||||
@ -105,6 +121,18 @@ void msp430_usart_release(const msp430_usart_params_t *params)
|
||||
sfr->IE = 0;
|
||||
sfr->IFG = 0;
|
||||
|
||||
switch (_clocks_acquired[params->num]) {
|
||||
case USART_CLK_SUBMAIN:
|
||||
msp430_clock_release(MSP430_CLOCK_SUBMAIN);
|
||||
break;
|
||||
case USART_CLK_AUX:
|
||||
msp430_clock_release(MSP430_CLOCK_AUXILIARY);
|
||||
break;
|
||||
default:
|
||||
/* external clock from GPIO, not managed here */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Release mutex */
|
||||
mutex_unlock(&usart_locks[params->num]);
|
||||
}
|
||||
|
@ -93,6 +93,8 @@ static mutex_t _usci_locks[MSP430_USCI_ID_NUMOF] = {
|
||||
MUTEX_INIT,
|
||||
};
|
||||
|
||||
static uint8_t _auxiliary_clock_acquired;
|
||||
|
||||
void msp430_usci_acquire(const msp430_usci_params_t *params,
|
||||
const msp430_usci_conf_t *conf)
|
||||
{
|
||||
@ -101,6 +103,23 @@ void msp430_usci_acquire(const msp430_usci_params_t *params,
|
||||
mutex_lock(&_usci_locks[params->id]);
|
||||
msp430_usci_b_t *dev = params->dev;
|
||||
|
||||
/* We only need to acquire the auxiliary (low frequency) clock domain, as
|
||||
* the subsystem main clock (SMCLK) will be acquired on-demand when activity
|
||||
* is detected on RXD, as per datasheet:
|
||||
*
|
||||
* > The USCI module provides automatic clock activation for SMCLK for use
|
||||
* > with low-power modes.
|
||||
*/
|
||||
switch (conf->prescaler.clk_source) {
|
||||
case USCI_CLK_AUX:
|
||||
msp430_clock_acquire(MSP430_CLOCK_AUXILIARY);
|
||||
_auxiliary_clock_acquired |= 1U << params->id;
|
||||
break;
|
||||
default:
|
||||
_auxiliary_clock_acquired &= ~(1U << params->id);
|
||||
break;
|
||||
}
|
||||
|
||||
/* put device in disabled/reset state */
|
||||
dev->CTL1 = UCSWRST;
|
||||
|
||||
@ -133,6 +152,10 @@ void msp430_usci_release(const msp430_usci_params_t *params)
|
||||
unsigned irq_mask = irq_disable();
|
||||
*params->interrupt_enable &= clear_irq_mask;
|
||||
*params->interrupt_flag &= clear_irq_mask;
|
||||
|
||||
if (_auxiliary_clock_acquired & (1U << params->id)) {
|
||||
msp430_clock_release(MSP430_CLOCK_AUXILIARY);
|
||||
}
|
||||
irq_restore(irq_mask);
|
||||
|
||||
/* Release mutex */
|
||||
|
@ -6,8 +6,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
@ -17,6 +15,5 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
telosb \
|
||||
weact-g030f6 \
|
||||
#
|
||||
|
@ -8,8 +8,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
e104-bt5011a-tb \
|
||||
gd32vf103c-start \
|
||||
lsn50 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
@ -32,5 +30,4 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
slstk3400a \
|
||||
stk3200 \
|
||||
stm32f0discovery \
|
||||
telosb \
|
||||
#
|
||||
|
@ -9,8 +9,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
bluepill-stm32f030c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
|
@ -1,8 +1,5 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
chronos \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f031k6 \
|
||||
telosb \
|
||||
#
|
||||
|
@ -11,6 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atxmega-a3bu-xplained \
|
||||
bluepill-stm32f030c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
|
@ -8,8 +8,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
bluepill-stm32f030c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
|
@ -8,8 +8,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
|
@ -23,6 +23,7 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
stm32f0discovery \
|
||||
stm32g0316-disco \
|
||||
stm32l0538-disco \
|
||||
telosb \
|
||||
waspmote-pro \
|
||||
weact-g030f6 \
|
||||
#
|
||||
|
@ -9,8 +9,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
blackpill-stm32f103c8 \
|
||||
bluepill-stm32f103c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
|
@ -6,8 +6,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
|
@ -11,5 +11,4 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
samd10-xmini \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
telosb \
|
||||
#
|
||||
|
@ -5,8 +5,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
bluepill-stm32f103cb \
|
||||
gd32vf103c-start \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
@ -28,5 +26,4 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
slstk3400a \
|
||||
stk3200 \
|
||||
stm32f0discovery \
|
||||
telosb \
|
||||
#
|
||||
|
@ -7,5 +7,4 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
nucleo-l011k4 \
|
||||
stm32f030f4-demo \
|
||||
telosb \
|
||||
#
|
||||
|
@ -4,8 +4,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
i-nucleo-lrwan1 \
|
||||
im880b \
|
||||
microbit \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nrf51dongle \
|
||||
nrf6310 \
|
||||
nucleo-f030r8 \
|
||||
@ -24,6 +22,5 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
stm32l0538-disco \
|
||||
telosb \
|
||||
yunjia-nrf51822 \
|
||||
#
|
||||
|
@ -22,6 +22,5 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
stm32f0discovery \
|
||||
stm32g0316-disco \
|
||||
stm32l0538-disco \
|
||||
telosb \
|
||||
weact-g030f6 \
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-uno \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
|
@ -2,8 +2,6 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
bluepill-stm32f030c8 \
|
||||
chronos \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
|
@ -1,8 +1,6 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
chronos \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-l011k4 \
|
||||
|
Loading…
Reference in New Issue
Block a user