1
0
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:
Marian Buschsieweke 2024-05-07 06:13:50 +00:00 committed by GitHub
commit 59956fd371
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 193 additions and 46 deletions

View File

@ -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"
);
}

View File

@ -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));

View File

@ -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"

View File

@ -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

View File

@ -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))

View File

@ -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]);
}

View File

@ -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 */

View File

@ -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 \
#

View File

@ -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 \
#

View File

@ -9,8 +9,6 @@ BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
bluepill-stm32f030c8 \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \

View File

@ -1,8 +1,5 @@
BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
chronos \
msb-430 \
msb-430h \
nucleo-f031k6 \
telosb \
#

View File

@ -11,6 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := \
atxmega-a3bu-xplained \
bluepill-stm32f030c8 \
i-nucleo-lrwan1 \
msb-430 \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \

View File

@ -8,8 +8,6 @@ BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
bluepill-stm32f030c8 \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \

View File

@ -8,8 +8,6 @@ BOARD_INSUFFICIENT_MEMORY := \
atmega328p-xplained-mini \
atmega8 \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \

View File

@ -23,6 +23,7 @@ BOARD_INSUFFICIENT_MEMORY := \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
telosb \
waspmote-pro \
weact-g030f6 \
#

View File

@ -9,8 +9,6 @@ BOARD_INSUFFICIENT_MEMORY := \
blackpill-stm32f103c8 \
bluepill-stm32f103c8 \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \

View File

@ -6,8 +6,6 @@ BOARD_INSUFFICIENT_MEMORY := \
atmega328p \
atmega328p-xplained-mini \
atmega8 \
msb-430 \
msb-430h \
nucleo-c031c6 \
nucleo-f031k6 \
nucleo-f042k6 \

View File

@ -11,5 +11,4 @@ BOARD_INSUFFICIENT_MEMORY := \
samd10-xmini \
stk3200 \
stm32f030f4-demo \
telosb \
#

View File

@ -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 \
#

View File

@ -7,5 +7,4 @@ BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
nucleo-l011k4 \
stm32f030f4-demo \
telosb \
#

View File

@ -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 \
#

View File

@ -22,6 +22,5 @@ BOARD_INSUFFICIENT_MEMORY := \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
telosb \
weact-g030f6 \
#

View File

@ -1,7 +1,7 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-uno \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
atmega8 \

View File

@ -2,8 +2,6 @@ BOARD_INSUFFICIENT_MEMORY := \
bluepill-stm32f030c8 \
chronos \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \

View File

@ -1,8 +1,6 @@
BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
chronos \
msb-430 \
msb-430h \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-l011k4 \