Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/atxmega: Add periph power management #16212

Merged
merged 2 commits into from
Apr 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions boards/atxmega-a1u-xpro/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extern "C" {
static const timer_conf_t timer_config[] = {
{
.dev = (void *)&TCC1,
.pwr = PWR_RED_REG(PWR_PORT_C, PR_TC1_bm),
.type = TC_TYPE_1,
.int_lvl = { CPU_INT_LVL_LOW,
CPU_INT_LVL_OFF,
Expand All @@ -44,6 +45,7 @@ static const timer_conf_t timer_config[] = {
},
{
.dev = (void *)&TCC0,
.pwr = PWR_RED_REG(PWR_PORT_C, PR_TC0_bm),
.type = TC_TYPE_0,
.int_lvl = { CPU_INT_LVL_LOW,
CPU_INT_LVL_LOW,
Expand All @@ -69,6 +71,7 @@ static const timer_conf_t timer_config[] = {
static const uart_conf_t uart_config[] = {
{ /* CDC-ACM */
.dev = &USARTE0,
.pwr = PWR_RED_REG(PWR_PORT_E, PR_USART0_bm),
.rx_pin = GPIO_PIN(PORT_E, 2),
.tx_pin = GPIO_PIN(PORT_E, 3),
#ifdef MODULE_PERIPH_UART_HW_FC
Expand Down
14 changes: 2 additions & 12 deletions cpu/atxmega/atxmega_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "cpu.h"
#include "cpu_clock.h"
#include "cpu_pm.h"
#include "panic.h"

#define ENABLE_DEBUG 0
Expand Down Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions cpu/atxmega/include/cpu_pm.h
Original file line number Diff line number Diff line change
@@ -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 */
/** @} */
25 changes: 25 additions & 0 deletions cpu/atxmega/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
* @{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
58 changes: 58 additions & 0 deletions cpu/atxmega/periph/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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" );
Expand Down Expand Up @@ -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;
}
}
5 changes: 5 additions & 0 deletions cpu/atxmega/periph/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <assert.h>

#include "cpu.h"
#include "cpu_pm.h"
#include "thread.h"

#include "periph/timer.h"
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}

Expand Down
9 changes: 5 additions & 4 deletions cpu/atxmega/periph/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "board.h"
#include "cpu.h"
#include "cpu_pm.h"
#include "sched.h"
#include "thread.h"
#include "periph/uart.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down