1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/efm32/uart: add series 2 periph driver

This commit is contained in:
Jue 2022-10-17 23:03:44 +02:00
parent 2b1a260c43
commit 42c9a3c9f1
4 changed files with 286 additions and 0 deletions

View File

@ -456,7 +456,9 @@ typedef struct {
void *dev; /**< UART, USART or LEUART device used */
gpio_t rx_pin; /**< pin used for RX */
gpio_t tx_pin; /**< pin used for TX */
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) || defined(DOXYGEN)
uint32_t loc; /**< location of UART pins */
#endif
CMU_Clock_TypeDef cmu; /**< the device CMU channel */
IRQn_Type irq; /**< the devices base IRQ channel */
} uart_conf_t;

View File

@ -18,6 +18,15 @@ ifneq (,$(filter periph_rtt,$(USEMODULE)))
endif
endif
# Select the correct implementation for `periph_uart`
ifneq (,$(filter periph_uart,$(USEMODULE)))
ifeq (2,$(EFM32_SERIES))
SRC += uart_series2.c
else ifneq (,$(filter $(EFM32_SERIES),0 1))
SRC += uart_series01.c
endif
endif
# Select the correct implementation for `periph_wdt`
ifneq (,$(filter periph_wdt,$(USEMODULE)))
ifeq (0,$(EFM32_SERIES))

View File

@ -0,0 +1,275 @@
/*
* Copyright (C) 2022 SSV Software Systems GmbH
*
* 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_efm32
* @ingroup drivers_periph_uart
* @{
*
* @file
* @brief Low-level UART driver implementation.
*
* @author Juergen Fitschen <me@jue.yt>
* @}
*/
#include <assert.h>
#include "cpu.h"
#include "periph/uart.h"
#include "periph/gpio.h"
#include "pm_layered.h"
#include "bitarithm.h"
#include "em_eusart.h"
/**
* @brief Allocate memory to store the callback functions
*/
static uart_isr_ctx_t isr_ctx[UART_NUMOF];
/**
* @brief Marks blocked_pm_mode entry valid
*/
#define PM_MODE_SHALL_BLOCK BIT7
#define PM_MODE_BLOCKED BIT6
/**
* @brief Mask for storing the pm mode
*/
#define PM_MODE_MASK (BIT6 - 1)
#if IS_ACTIVE(MODULE_PM_LAYERED)
/**
* @brief Keep track of currently blocked blocked_pm_mode
*/
static uint8_t blocked_pm_mode[UART_NUMOF];
static inline void _clear_pm_mode(uart_t dev)
{
/* unblock previously blocked mode */
if (blocked_pm_mode[dev] & PM_MODE_BLOCKED) {
pm_unblock(blocked_pm_mode[dev] & PM_MODE_MASK);
CLRBIT(blocked_pm_mode[dev], PM_MODE_BLOCKED);
}
}
static inline void _set_pm_mode(uart_t dev)
{
/* Check if we shall block any pm mode at all */
if (!(blocked_pm_mode[dev] & PM_MODE_SHALL_BLOCK)) {
return;
}
/* block minimum required pm mode */
if (!(blocked_pm_mode[dev] & PM_MODE_BLOCKED)) {
pm_block(blocked_pm_mode[dev] & PM_MODE_MASK);
SETBIT(blocked_pm_mode[dev], PM_MODE_BLOCKED);
}
}
static inline void _setup_pm_mode(uart_t dev, uint8_t mode)
{
assert(!(blocked_pm_mode[dev] & PM_MODE_BLOCKED));
blocked_pm_mode[dev] = mode;
}
#else /* !IS_ACTIVE(MODULE_PM_LAYERED) */
static inline void _clear_pm_mode(uart_t dev) { (void) dev; }
static inline void _set_pm_mode(uart_t dev) { (void) dev; }
static inline void _setup_pm_mode(uart_t dev, uint8_t mode) { (void) dev; (void) mode; }
#endif /* !IS_ACTIVE(MODULE_PM_LAYERED) */
#define GET_PIN(x) (x & 0xf)
#define GET_PORT(x) (x >> 4)
int uart_init(uart_t dev, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{
/* check if device is valid */
if (dev >= UART_NUMOF) {
return -1;
}
/* save interrupt callback context */
isr_ctx[dev].rx_cb = rx_cb;
isr_ctx[dev].arg = arg;
EUSART_TypeDef *uart = (EUSART_TypeDef *) uart_config[dev].dev;
/* select and enable clock */
CMU_ClockEnable(uart_config[dev].cmu, true);
/* initialize the pins */
gpio_init(uart_config[dev].rx_pin, GPIO_IN_PU);
gpio_init(uart_config[dev].tx_pin, GPIO_OUT | 1); /* 1 for high */
/* reset and initialize peripheral */
/* the clock source defines whether the periph is initialized in LF or HF mode */
CMU_Select_TypeDef src = CMU_ClockSelectGet(uart_config[dev].cmu);
if ((src == cmuSelect_LFRCO) ||
(src == cmuSelect_ULFRCO) ||
(src == cmuSelect_LFXO)) {
EUSART_UartInit_TypeDef init = EUSART_UART_INIT_DEFAULT_LF;
init.enable = eusartDisable;
init.baudrate = baudrate;
EUSART_UartInitLf(uart, &init);
} else {
EUSART_UartInit_TypeDef init = EUSART_UART_INIT_DEFAULT_HF;
init.enable = eusartDisable;
init.baudrate = baudrate;
EUSART_UartInitHf(uart, &init);
}
/* route pings to eusart periph */
GPIO->EUSARTROUTE[EUSART_NUM(uart)].TXROUTE =
(GET_PORT(uart_config[dev].tx_pin) << _GPIO_EUSART_TXROUTE_PORT_SHIFT) |
(GET_PIN(uart_config[dev].tx_pin) << _GPIO_EUSART_TXROUTE_PIN_SHIFT);
GPIO->EUSARTROUTE[EUSART_NUM(uart)].RXROUTE =
(GET_PORT(uart_config[dev].rx_pin) << _GPIO_EUSART_RXROUTE_PORT_SHIFT) |
(GET_PIN(uart_config[dev].rx_pin) << _GPIO_EUSART_RXROUTE_PIN_SHIFT);
GPIO->EUSARTROUTE[EUSART_NUM(uart)].ROUTEEN =
(GPIO_EUSART_ROUTEEN_RXPEN | GPIO_EUSART_ROUTEEN_TXPEN);
/* clear previously set pm_mode */
_clear_pm_mode(dev);
_setup_pm_mode(dev, 0);
/* enable the interrupt */
if (rx_cb) {
/* If we want to receive data from the UART,
* we have to make sure to stick with the right pm mode! */
switch (src) {
case cmuSelect_ULFRCO:
/* NOP ... EM3 is the lowest available pm mode */
break;
case cmuSelect_LFRCO:
case cmuSelect_LFXO:
/* We must stay in EM2 for LFXO / LFRCO -> block EM3*/
_setup_pm_mode(dev, PM_MODE_SHALL_BLOCK | EFM32_PM_MODE_EM3);
break;
default:
/* We must stay in EM0/1 for all other clock -> block EM2*/
_setup_pm_mode(dev, PM_MODE_SHALL_BLOCK | EFM32_PM_MODE_EM2);
}
/* Enable RX FIFO Level IRQ */
/* CFG1->RXFIW is set to one frame after reset */
EUSART_IntEnable(uart, EUSART_IEN_RXFL);
NVIC_ClearPendingIRQ(uart_config[dev].irq);
NVIC_EnableIRQ(uart_config[dev].irq);
}
uart_poweron(dev);
return 0;
}
void uart_poweron(uart_t dev)
{
EUSART_TypeDef *uart = uart_config[dev].dev;
CMU_ClockEnable(uart_config[dev].cmu, true);
/* enable tx */
EUSART_Enable_TypeDef enable = eusartEnableTx;
/* enable rx if needed */
if (isr_ctx[dev].rx_cb) {
enable |= eusartEnableRx;
}
EUSART_Enable(uart, enable);
/* block power mode */
_set_pm_mode(dev);
}
void uart_poweroff(uart_t dev)
{
EUSART_TypeDef *uart = uart_config[dev].dev;
/* unblock power mode */
_clear_pm_mode(dev);
/* disable tx and rx */
EUSART_Enable(uart, eusartDisable);
CMU_ClockEnable(uart_config[dev].cmu, false);
}
void uart_write(uart_t dev, const uint8_t *data, size_t len)
{
EUSART_TypeDef *uart = uart_config[dev].dev;
/* When the system goes into EM2 or EM3, all EUSARTs located in PD1 will be
* disabled automatically. We can detect this by querying the transmitter
* enable bit and re-enable the EUSART on-the-fly. */
if (!(uart->STATUS & EUSART_STATUS_TXENS)) {
uart_poweron(dev);
}
while (len--) {
EUSART_Tx(uart, *(data++));
}
/* spin until transmission is complete */
while (!(EUSART_StatusGet(uart) & EUSART_STATUS_TXC)) {}
}
static void rx_irq(uart_t dev)
{
EUSART_TypeDef *uart = uart_config[dev].dev;
if (EUSART_IntGetEnabled(uart) & EUSART_IF_RXFL) {
while (EUSART_StatusGet(uart) & EUSART_STATUS_RXFL) {
uint8_t c = EUSART_Rx(uart);
if (isr_ctx[dev].rx_cb) {
isr_ctx[dev].rx_cb(isr_ctx[dev].arg, c);
}
}
EUSART_IntClear(uart, EUSART_IF_RXFL);
}
cortexm_isr_end();
}
#ifdef UART_0_ISR_RX
void UART_0_ISR_RX(void)
{
rx_irq(0);
}
#endif
#ifdef UART_1_ISR_RX
void UART_1_ISR_RX(void)
{
rx_irq(1);
}
#endif
#ifdef UART_2_ISR_RX
void UART_2_ISR_RX(void)
{
rx_irq(2);
}
#endif
#ifdef UART_3_ISR_RX
void UART_3_ISR_RX(void)
{
rx_irq(3);
}
#endif
#ifdef UART_4_ISR_RX
void UART_4_ISR_RX(void)
{
rx_irq(4);
}
#endif