1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

cpu/atxmega: Add periph power management

The current xmega don't have a way to disable peripherals that are
not in used.  Add peripheral management to allow enable only the mcu
blocks that will be used by application.  This saves power on active
and sleep modes.  By default, at clock initialization, all peripherals
are now disabled and each drive must activate at initialization phase.
The periph_timer and periph_uart were updated with this new feature.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
This commit is contained in:
Gerson Fernando Budke 2021-01-23 18:00:47 -03:00
parent e04dd4dcce
commit 93ed3cd9d6
6 changed files with 136 additions and 16 deletions

View File

@ -22,6 +22,7 @@
#include "cpu.h"
#include "cpu_clock.h"
#include "cpu_pm.h"
#include "panic.h"
#define ENABLE_DEBUG 0
@ -63,18 +64,7 @@ void avr8_reset_cause(void)
void __attribute__((weak)) avr8_clk_init(void)
{
volatile uint8_t *reg = (uint8_t *)&PR.PRGEN;
uint8_t i;
/* Turn off all peripheral clocks that can be turned off. */
for (i = 0; i <= 7; i++) {
reg[i] = 0xff;
}
/* Turn on all peripheral clocks that can be turned on. */
for (i = 0; i <= 7; i++) {
reg[i] = 0x00;
}
pm_periph_power_off();
/* XMEGA A3U [DATASHEET] p.23 After reset, the device starts up running
* from the 2MHz internal oscillator. The other clock sources, DFLLs

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @{
*
* @file
* @brief Power Management and Power Reduction API
*
* This help to save power disabling all non used peripherals. It can help to
* save power when in active or sleep modes. For any other low power modes
* xmega will freeze all peripherals clock.
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*/
#include "periph_cpu.h"
#ifndef CPU_PM_H
#define CPU_PM_H
#ifdef __cplusplus
extern "C" {
#endif
void pm_periph_enable(pwr_reduction_t pwr);
void pm_periph_disable(pwr_reduction_t pwr);
void pm_periph_power_off(void);
#ifdef __cplusplus
}
#endif
#endif /* CPU_PM_H */
/** @} */

View File

@ -68,6 +68,29 @@ enum {
PORT_MAX,
};
/**
* @brief Power Reduction Peripheral Mask
*/
typedef uint16_t pwr_reduction_t;
/**
* @brief Define a CPU specific Power Reduction index macro
*/
#define PWR_RED_REG(reg, dev) ((reg << 8) | dev)
/**
* @brief Define a CPU specific Power Reduction index macro
*/
enum {
PWR_GENERAL_POWER,
PWR_PORT_A,
PWR_PORT_B,
PWR_PORT_C,
PWR_PORT_D,
PWR_PORT_E,
PWR_PORT_F,
};
/**
* @name Power management configuration
* @{
@ -190,6 +213,7 @@ typedef enum {
*/
typedef struct {
USART_t *dev; /**< pointer to the used UART device */
pwr_reduction_t pwr; /**< Power Management */
gpio_t rx_pin; /**< pin used for RX */
gpio_t tx_pin; /**< pin used for TX */
#ifdef MODULE_PERIPH_UART_HW_FC
@ -235,6 +259,7 @@ typedef enum {
*/
typedef struct {
TC0_t *dev; /**< Pointer to the used as Timer device */
pwr_reduction_t pwr; /**< Power Management */
timer_type_t type; /**< Timer Type */
cpu_int_lvl_t int_lvl[TIMER_CH_MAX_NUMOF]; /**< Interrupt channels level */
} timer_conf_t;

View File

@ -23,10 +23,41 @@
#include "periph_conf.h"
#include "periph/pm.h"
#include "cpu_pm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define PWR_REG_BASE ((uint16_t)&PR)
#define PWR_REG_OFFSET (0x01)
/**
* @brief Extract the device id of the given power reduction mask
*/
static inline uint8_t _device_mask(pwr_reduction_t pwr)
{
return (pwr & 0xff);
}
/**
* @brief Extract the register id of the given power reduction mask
*/
static inline uint8_t _register_id(pwr_reduction_t pwr)
{
return (pwr >> 8) & 0xff;
}
/**
* @brief Generate the register index of the given power reduction mask
*/
static inline uint8_t *_register_addr(pwr_reduction_t pwr)
{
uint8_t id = _register_id(pwr);
uint16_t addr = PWR_REG_BASE + (id * PWR_REG_OFFSET);
return (uint8_t *)addr;
}
void pm_reboot(void)
{
DEBUG("Reboot Software Reset\n" );
@ -78,3 +109,30 @@ void pm_set(unsigned mode)
sleep_disable();
irq_restore(irq_state);
}
void pm_periph_enable(pwr_reduction_t pwr)
{
uint8_t mask = _device_mask(pwr);
uint8_t *reg = _register_addr(pwr);
*reg &= ~mask;
}
void pm_periph_disable(pwr_reduction_t pwr)
{
uint8_t mask = _device_mask(pwr);
uint8_t *reg = _register_addr(pwr);
*reg |= mask;
}
void pm_periph_power_off(void)
{
uint8_t *reg = _register_addr(PWR_GENERAL_POWER);
uint8_t i;
/* Freeze all peripheral clocks */
for (i = 0; i <= 7; i++) {
reg[i] = 0xff;
}
}

View File

@ -24,6 +24,7 @@
#include <assert.h>
#include "cpu.h"
#include "cpu_pm.h"
#include "thread.h"
#include "periph/timer.h"
@ -112,6 +113,8 @@ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
return -1;
}
pm_periph_enable(timer_config[tim].pwr);
dev = timer_config[tim].dev;
/* stop and reset timer */
@ -301,6 +304,7 @@ void timer_stop(tim_t tim)
DEBUG("timer_stop\n");
timer_config[tim].dev->CTRLA = 0;
timer_config[tim].dev->CTRLFSET = TC_CMD_RESTART_gc;
pm_periph_disable(timer_config[tim].pwr);
}
void timer_start(tim_t tim)
@ -310,6 +314,7 @@ void timer_start(tim_t tim)
}
DEBUG("timer_start\n");
pm_periph_enable(timer_config[tim].pwr);
timer_config[tim].dev->CTRLA = ctx[tim].prescaler;
}

View File

@ -29,6 +29,7 @@
#include "board.h"
#include "cpu.h"
#include "cpu_pm.h"
#include "sched.h"
#include "thread.h"
#include "periph/uart.h"
@ -257,6 +258,8 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
isr_ctx[uart].rx_cb = rx_cb;
isr_ctx[uart].arg = arg;
pm_periph_enable(uart_config[uart].pwr);
/* disable and reset UART */
dev(uart)->CTRLA = 0;
dev(uart)->CTRLB = 0;
@ -315,14 +318,12 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len)
void uart_poweron(uart_t uart)
{
(void)uart;
/* not implemented (yet) */
pm_periph_enable(uart_config[uart].pwr);
}
void uart_poweroff(uart_t uart)
{
(void)uart;
/* not implemented (yet) */
pm_periph_disable(uart_config[uart].pwr);
}
static inline void _rx_isr_handler(int num)