From 4643ed573342a09238d23c215335dbedbc9a1074 Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Fri, 13 Mar 2020 01:23:25 -0500 Subject: [PATCH 1/4] cc26xx_cc13xx: add power abstraction Signed-off-by: Jean Pierre Dudey --- cpu/cc26x2_cc13x2/Makefile.dep | 2 + .../include/cc26xx_cc13xx_power.h | 63 ++++++++++++++ cpu/cc26xx_cc13xx/periph/gpio.c | 7 +- cpu/cc26xx_cc13xx/periph/uart.c | 6 +- cpu/cc26xx_cc13xx/power_arch.c | 82 +++++++++++++++++++ 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h create mode 100644 cpu/cc26xx_cc13xx/power_arch.c diff --git a/cpu/cc26x2_cc13x2/Makefile.dep b/cpu/cc26x2_cc13x2/Makefile.dep index a0a73e54fe..cb4d821401 100644 --- a/cpu/cc26x2_cc13x2/Makefile.dep +++ b/cpu/cc26x2_cc13x2/Makefile.dep @@ -1 +1,3 @@ +USEMODULE += cc26xx_cc13xx + include ${RIOTCPU}/cc26xx_cc13xx/Makefile.dep diff --git a/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h b/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h new file mode 100644 index 0000000000..5f55de24b3 --- /dev/null +++ b/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * 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_cc26xx_cc13xx + * @{ + * + * @file + * @brief CC26xx/CC13xx Power management + * + * @author Jean Pierre Dudey + */ + +#ifndef CC26XX_CC13XX_POWER_H +#define CC26XX_CC13XX_POWER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Power domains + */ +typedef enum { + POWER_DOMAIN_PERIPHERALS, /**< Peripherals domain */ + POWER_DOMAIN_SERIAL, /**< Serial domain */ + POWER_DOMAIN_RFC, /**< RF Core domain */ + POWER_DOMAIN_CPU, /**< CPU domain */ + POWER_DOMAIN_VIMS, /**< VIMS domain */ +} power_domain_t; + +/** + * @brief Is power domain enabled? + * + * @param[in] domain The domain. + * + * @return true Is enabled. + * @return false Is not enabled. + */ +bool power_is_domain_enabled(const power_domain_t domain); + +/** + * @brief Enable the specified power domain. + * + * @params[in] domain The domain. + */ +void power_enable_domain(const power_domain_t domain); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* CC26XX_CC13XX_POWER_H */ + +/*@}*/ diff --git a/cpu/cc26xx_cc13xx/periph/gpio.c b/cpu/cc26xx_cc13xx/periph/gpio.c index 1c8345d020..5a0aa91a31 100644 --- a/cpu/cc26xx_cc13xx/periph/gpio.c +++ b/cpu/cc26xx_cc13xx/periph/gpio.c @@ -38,9 +38,12 @@ int gpio_init(gpio_t pin, gpio_mode_t mode) if ((unsigned int)pin > 31) return -1; + /* enable peripherals power domain */ + if (!power_is_domain_enabled(POWER_DOMAIN_PERIPHERALS)) { + power_enable_domain(POWER_DOMAIN_PERIPHERALS); + } + /* enable GPIO clock */ - PRCM->PDCTL0 |= PDCTL0_PERIPH_ON; - while(!(PRCM->PDSTAT0 & PDSTAT0_PERIPH_ON)) ; PRCM->GPIOCLKGR |= 1; PRCM->CLKLOADCTL |= CLKLOADCTL_LOAD; while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) ; diff --git a/cpu/cc26xx_cc13xx/periph/uart.c b/cpu/cc26xx_cc13xx/periph/uart.c index e65f85140a..c93de7bdbb 100644 --- a/cpu/cc26xx_cc13xx/periph/uart.c +++ b/cpu/cc26xx_cc13xx/periph/uart.c @@ -57,9 +57,11 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) int rts_pin = uart_config[uart].rts_pin; int cts_pin = uart_config[uart].cts_pin; #endif + /* enable clocks: serial power domain and UART */ - PRCM->PDCTL0SERIAL = 1; - while (!(PRCM->PDSTAT0 & PDSTAT0_SERIAL_ON)) ; + if (!power_is_domain_enabled(POWER_DOMAIN_SERIAL)) { + power_enable_domain(POWER_DOMAIN_SERIAL); + } uart_poweron(uart); /* disable and reset the UART */ diff --git a/cpu/cc26xx_cc13xx/power_arch.c b/cpu/cc26xx_cc13xx/power_arch.c new file mode 100644 index 0000000000..f6910aa011 --- /dev/null +++ b/cpu/cc26xx_cc13xx/power_arch.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * 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_cc26xx_cc13xx + * @{ + * + * @file + * @brief Power management abstractions + * + * @author Jean Pierre Dudey + * + * @} + */ + +#include "cpu.h" + +#define DOMAIN_ON (1) + +bool power_is_domain_enabled(const power_domain_t domain) +{ + switch (domain) { + case POWER_DOMAIN_PERIPHERALS: + return PRCM->PDSTAT0PERIPH == DOMAIN_ON; + + case POWER_DOMAIN_SERIAL: + return PRCM->PDSTAT0SERIAL == DOMAIN_ON; + + case POWER_DOMAIN_RFC: + /* At least one of the registers need to indicate that the power + * domain is on */ + return (PRCM->PDSTAT1RFC == DOMAIN_ON) || + (PRCM->PDSTAT0RFC == DOMAIN_ON); + + case POWER_DOMAIN_VIMS: + return PRCM->PDSTAT1VIMS == DOMAIN_ON; + + case POWER_DOMAIN_CPU: + return PRCM->PDSTAT1CPU == DOMAIN_ON; + + default: + return false; + } + + return false; +} + +void power_enable_domain(const power_domain_t domain) +{ + switch (domain) { + case POWER_DOMAIN_PERIPHERALS: + PRCM->PDCTL0PERIPH = DOMAIN_ON; + break; + + case POWER_DOMAIN_SERIAL: + PRCM->PDCTL0SERIAL = DOMAIN_ON; + break; + + case POWER_DOMAIN_RFC: + /* On CC26x0 MCUs PDCTL1RFC needs to be written too in order to + * enable the RF Core power domain. On `cc13x2_cc26x2` it's not + * necessary and domain is powered normally. */ + PRCM->PDCTL0RFC = DOMAIN_ON; + PRCM->PDCTL1RFC = DOMAIN_ON; + break; + + case POWER_DOMAIN_CPU: + PRCM->PDCTL1CPU = DOMAIN_ON; + break; + + case POWER_DOMAIN_VIMS: + PRCM->PDCTL1VIMS = DOMAIN_ON; + break; + } + + while (!power_is_domain_enabled(domain)) {} +} From 7ac28c82b174b0a2a6505dc3f285c33a682c0a65 Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Fri, 13 Mar 2020 01:27:35 -0500 Subject: [PATCH 2/4] cc26x0: enable serial domain only once Signed-off-by: Jean Pierre Dudey --- cpu/cc26x0/Makefile.dep | 2 ++ cpu/cc26x0/periph/i2c.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cpu/cc26x0/Makefile.dep b/cpu/cc26x0/Makefile.dep index a0a73e54fe..cb4d821401 100644 --- a/cpu/cc26x0/Makefile.dep +++ b/cpu/cc26x0/Makefile.dep @@ -1 +1,3 @@ +USEMODULE += cc26xx_cc13xx + include ${RIOTCPU}/cc26xx_cc13xx/Makefile.dep diff --git a/cpu/cc26x0/periph/i2c.c b/cpu/cc26x0/periph/i2c.c index eea8dfdaa5..5b7ffaaa29 100644 --- a/cpu/cc26x0/periph/i2c.c +++ b/cpu/cc26x0/periph/i2c.c @@ -95,13 +95,13 @@ void i2c_init(i2c_t devnum) assert(devnum < I2C_NUMOF); /* Make sure everything is shut off in case of reinit */ - PRCM->PDCTL0SERIAL = 0; I2C->MCR = 0; PRCM->I2CCLKGR = 0; - /* enable SERIAL power domain */ - PRCM->PDCTL0SERIAL = 1; - while (!(PRCM->PDSTAT0 & PDSTAT0_SERIAL_ON)) {} + /* Enable serial power domain */ + if (!power_is_domain_enabled(POWER_DOMAIN_SERIAL)) { + power_enable_domain(POWER_DOMAIN_SERIAL); + } /* enable i2c clock in run mode */ PRCM->I2CCLKGR = 1; From c6e4768997c030c4a1b2c58b5e795515937a323d Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Mon, 23 Mar 2020 09:32:11 -0500 Subject: [PATCH 3/4] cc26xx_cc13xx: add PRCM_NONBUF register bank Signed-off-by: Jean Pierre Dudey --- cpu/cc26x0/include/cc26x0_prcm.h | 6 ++++-- cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cpu/cc26x0/include/cc26x0_prcm.h b/cpu/cc26x0/include/cc26x0_prcm.h index 60683c40f3..562e5ef56b 100644 --- a/cpu/cc26x0/include/cc26x0_prcm.h +++ b/cpu/cc26x0/include/cc26x0_prcm.h @@ -342,10 +342,12 @@ typedef struct { /** @ingroup cpu_specific_peripheral_memory_map * @{ */ -#define PRCM_BASE 0x40082000 /**< PRCM base address */ +#define PRCM_BASE (PERIPH_BASE + 0x82000) /**< PRCM base address */ +#define PRCM_BASE_NONBUF (PERIPH_BASE_NONBUF + 0x82000) /**< PRCM base address (nonbuf) */ /*@}*/ -#define PRCM ((prcm_regs_t *) (PRCM_BASE)) /**< PRCM register bank */ +#define PRCM ((prcm_regs_t *) (PRCM_BASE)) /**< PRCM register bank */ +#define PRCM_NONBUF ((prcm_regs_t *) (PRCM_BASE_NONBUF)) /**< PRCM register bank (nonbuf) */ #ifdef __cplusplus } /* end extern "C" */ diff --git a/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h b/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h index 81b735660e..246d333ea1 100644 --- a/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h +++ b/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h @@ -259,10 +259,12 @@ typedef struct { /** @ingroup cpu_specific_peripheral_memory_map * @{ */ -#define PRCM_BASE 0x40082000 /**< PRCM base address */ +#define PRCM_BASE (PERIPH_BASE + 0x82000) /**< PRCM base address */ +#define PRCM_BASE_NONBUF (PERIPH_BASE_NONBUF + 0x82000) /**< PRCM base address (nonbuf) */ /*@}*/ -#define PRCM ((prcm_regs_t *) (PRCM_BASE)) /**< PRCM register bank */ +#define PRCM ((prcm_regs_t *) (PRCM_BASE)) /**< PRCM register bank */ +#define PRCM_NONBUF ((prcm_regs_t *) (PRCM_BASE_NONBUF)) /**< PRCM register bank (nonbuf) */ #ifdef __cplusplus } /* end extern "C" */ From ffa5005021117f3fe3d8f0202af34bbab608de6b Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Fri, 13 Mar 2020 11:05:11 -0500 Subject: [PATCH 4/4] cc26xx_cc13xx: add API to manage peripheral clocks Signed-off-by: Jean Pierre Dudey --- cpu/cc26x0/include/cc26x0_prcm.h | 4 ++ cpu/cc26x0/periph/i2c.c | 9 ++- .../include/cc26x2_cc13x2_prcm.h | 5 ++ .../include/cc26xx_cc13xx_power.h | 35 ++++++++++- cpu/cc26xx_cc13xx/periph/gpio.c | 6 +- cpu/cc26xx_cc13xx/periph/timer.c | 6 +- cpu/cc26xx_cc13xx/periph/uart.c | 13 ++-- cpu/cc26xx_cc13xx/power_arch.c | 59 +++++++++++++++++++ 8 files changed, 118 insertions(+), 19 deletions(-) diff --git a/cpu/cc26x0/include/cc26x0_prcm.h b/cpu/cc26x0/include/cc26x0_prcm.h index 562e5ef56b..1c7a1fe1e0 100644 --- a/cpu/cc26x0/include/cc26x0_prcm.h +++ b/cpu/cc26x0/include/cc26x0_prcm.h @@ -337,6 +337,10 @@ typedef struct { #define PDSTAT1_CPU_ON 0x2 #define PDSTAT1_RFC_ON 0x4 #define PDSTAT1_VIMS_ON 0x8 + +#define GPIOCLKGR_CLK_EN 0x1 +#define I2CCLKGR_CLK_EN 0x1 +#define UARTCLKGR_CLK_EN_UART0 0x1 /** @} */ /** @ingroup cpu_specific_peripheral_memory_map diff --git a/cpu/cc26x0/periph/i2c.c b/cpu/cc26x0/periph/i2c.c index 5b7ffaaa29..db6ffcc7ab 100644 --- a/cpu/cc26x0/periph/i2c.c +++ b/cpu/cc26x0/periph/i2c.c @@ -31,6 +31,8 @@ #include "cpu.h" #include "periph/i2c.h" +#include "cc26xx_cc13xx_power.h" + #define ENABLE_DEBUG 0 #include "debug.h" @@ -96,17 +98,14 @@ void i2c_init(i2c_t devnum) /* Make sure everything is shut off in case of reinit */ I2C->MCR = 0; - PRCM->I2CCLKGR = 0; /* Enable serial power domain */ if (!power_is_domain_enabled(POWER_DOMAIN_SERIAL)) { power_enable_domain(POWER_DOMAIN_SERIAL); } - /* enable i2c clock in run mode */ - PRCM->I2CCLKGR = 1; - PRCM->CLKLOADCTL |= CLKLOADCTL_LOAD; - while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {} + /* enable I2C clock in run mode */ + power_clock_enable_i2c(); /* configure pins */ IOC->CFG[I2C_SDA_PIN] = (IOCFG_PORTID_I2C_MSSDA diff --git a/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h b/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h index 246d333ea1..24d2e44628 100644 --- a/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h +++ b/cpu/cc26x2_cc13x2/include/cc26x2_cc13x2_prcm.h @@ -254,6 +254,11 @@ typedef struct { #define PDSTAT1_CPU_ON 0x2 #define PDSTAT1_RFC_ON 0x4 #define PDSTAT1_VIMS_ON 0x8 + +#define GPIOCLKGR_CLK_EN 0x1 +#define I2CCLKGR_CLK_EN 0x1 +#define UARTCLKGR_CLK_EN_UART0 0x1 +#define UARTCLKGR_CLK_EN_UART1 0x1 /** @} */ /** @ingroup cpu_specific_peripheral_memory_map diff --git a/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h b/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h index 5f55de24b3..d14723df17 100644 --- a/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h +++ b/cpu/cc26xx_cc13xx/include/cc26xx_cc13xx_power.h @@ -22,6 +22,8 @@ #include #include +#include "periph/uart.h" + #ifdef __cplusplus extern "C" { #endif @@ -50,10 +52,41 @@ bool power_is_domain_enabled(const power_domain_t domain); /** * @brief Enable the specified power domain. * - * @params[in] domain The domain. + * @param[in] domain The domain. */ void power_enable_domain(const power_domain_t domain); +/** + * @brief Enable GPIO clock + */ +void power_clock_enable_gpio(void); + +/** + * @brief Enable General Purpose Timer clock + * + * @param[in] tim The timer to enable + */ +void power_clock_enable_gpt(uint32_t tim); + +/** + * @brief Enable I2C clock + */ +void power_clock_enable_i2c(void); + +/** + * @brief Enable UART clocks + * + * @param[in] uart The UART number + */ +void power_clock_enable_uart(uart_t uart); + +/** + * @brief Disable UART clocks + * + * @param[in] uart The UART number + */ +void power_clock_disable_uart(uart_t uart); + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/cpu/cc26xx_cc13xx/periph/gpio.c b/cpu/cc26xx_cc13xx/periph/gpio.c index 5a0aa91a31..74fd13f1fa 100644 --- a/cpu/cc26xx_cc13xx/periph/gpio.c +++ b/cpu/cc26xx_cc13xx/periph/gpio.c @@ -22,6 +22,8 @@ #include "cpu.h" #include "periph/gpio.h" +#include "cc26xx_cc13xx_power.h" + #define DOE_SHIFT (29U) #ifdef MODULE_PERIPH_GPIO_IRQ @@ -44,9 +46,7 @@ int gpio_init(gpio_t pin, gpio_mode_t mode) } /* enable GPIO clock */ - PRCM->GPIOCLKGR |= 1; - PRCM->CLKLOADCTL |= CLKLOADCTL_LOAD; - while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) ; + power_clock_enable_gpio(); /* configure the GPIO mode */ IOC->CFG[pin] = mode; diff --git a/cpu/cc26xx_cc13xx/periph/timer.c b/cpu/cc26xx_cc13xx/periph/timer.c index 396fcd1b3b..8cd9db7411 100644 --- a/cpu/cc26xx_cc13xx/periph/timer.c +++ b/cpu/cc26xx_cc13xx/periph/timer.c @@ -29,6 +29,8 @@ #include "periph_conf.h" #include "periph/timer.h" +#include "cc26xx_cc13xx_power.h" + #define ENABLE_DEBUG (0) #include "debug.h" @@ -105,9 +107,7 @@ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg) } /* enable the timer clock */ - PRCM->GPTCLKGR |= (1 << tim); - PRCM->CLKLOADCTL = CLKLOADCTL_LOAD; - while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {} + power_clock_enable_gpt(tim); /* disable (and reset) timer */ dev(tim)->CTL = 0; diff --git a/cpu/cc26xx_cc13xx/periph/uart.c b/cpu/cc26xx_cc13xx/periph/uart.c index c93de7bdbb..a4306f4703 100644 --- a/cpu/cc26xx_cc13xx/periph/uart.c +++ b/cpu/cc26xx_cc13xx/periph/uart.c @@ -24,6 +24,8 @@ #include "periph/uart.h" #include "periph_conf.h" +#include "cc26xx_cc13xx_power.h" + /** * @brief Bit mask for the fractional part of the baudrate */ @@ -161,9 +163,8 @@ void uart_poweron(uart_t uart) uart_regs_t *uart_reg = uart_config[uart].regs; - PRCM->UARTCLKGR |= 0x1; - PRCM->CLKLOADCTL = CLKLOADCTL_LOAD; - while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {} + /* Enable clock for this UART */ + power_clock_enable_uart(uart); uart_reg->CTL = ENABLE_MASK; } @@ -176,10 +177,8 @@ void uart_poweroff(uart_t uart) uart_reg->CTL = 0; - PRCM->UARTCLKGR = 0; - PRCM->CLKLOADCTL = CLKLOADCTL_LOAD; - while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {} - + /* Disable clock for this UART */ + power_clock_disable_uart(uart); } static void isr_uart(uart_t uart) diff --git a/cpu/cc26xx_cc13xx/power_arch.c b/cpu/cc26xx_cc13xx/power_arch.c index f6910aa011..28abea1c07 100644 --- a/cpu/cc26xx_cc13xx/power_arch.c +++ b/cpu/cc26xx_cc13xx/power_arch.c @@ -19,9 +19,21 @@ */ #include "cpu.h" +#include "cc26xx_cc13xx_power.h" #define DOMAIN_ON (1) +/* Save changes of the PRCM */ +static void prcm_commit(void) +{ + /* Write CLKLOADCTL in the non-buffered register bank to avoid buffered + * writes */ + PRCM_NONBUF->CLKLOADCTL = CLKLOADCTL_LOAD; + + /* Wait while load is done */ + while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {} +} + bool power_is_domain_enabled(const power_domain_t domain) { switch (domain) { @@ -80,3 +92,50 @@ void power_enable_domain(const power_domain_t domain) while (!power_is_domain_enabled(domain)) {} } + +void power_clock_enable_gpio(void) +{ + PRCM->GPIOCLKGR = GPIOCLKGR_CLK_EN; + + prcm_commit(); +} + +void power_clock_enable_gpt(uint32_t tim) +{ + PRCM->GPTCLKGR |= (1 << tim); + + prcm_commit(); +} +void power_clock_enable_i2c(void) { + PRCM->I2CCLKGR = I2CCLKGR_CLK_EN; + + prcm_commit(); +} + +void power_clock_enable_uart(uart_t uart) +{ + if (uart == 0) { + PRCM->UARTCLKGR |= UARTCLKGR_CLK_EN_UART0; + } +#ifdef UARTCLKGR_CLK_EN_UART1 + else if (uart == 1) { + PRCM->UARTCLKGR |= UARTCLKGR_CLK_EN_UART1; + } +#endif + + prcm_commit(); +} + +void power_clock_disable_uart(uart_t uart) +{ + if (uart == 0) { + PRCM->UARTCLKGR &= ~UARTCLKGR_CLK_EN_UART0; + } +#ifdef UARTCLKGR_CLK_EN_UART1 + else if (uart == 1) { + PRCM->UARTCLKGR &= ~UARTCLKGR_CLK_EN_UART1; + } +#endif + + prcm_commit(); +}