Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #16212 from nandojve/xmega_pm
Browse files Browse the repository at this point in the history
cpu/atxmega: Add periph power management
  • Loading branch information
maribu committed Apr 7, 2021
2 parents 04b5627 + 948b490 commit ac774f3
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 16 deletions.
3 changes: 3 additions & 0 deletions boards/atxmega-a1u-xpro/include/periph_conf.h
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
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
@@ -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
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
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
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
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

0 comments on commit ac774f3

Please sign in to comment.