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:
parent
2b1a260c43
commit
42c9a3c9f1
@ -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;
|
||||
|
@ -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))
|
||||
|
275
cpu/efm32/periph/uart_series2.c
Normal file
275
cpu/efm32/periph/uart_series2.c
Normal 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
|
Loading…
Reference in New Issue
Block a user