drivers: serial: mec5: Microchip MEC5 UART serial driver
We add a serial UART driver for Microchip MEC5 HAL based chips. The driver supports polling, interrupts, and runtime configuration features. Power management will be implemented in a future PR. Signed-off-by: Scott Worley <scott.worley@microchip.com>
This commit is contained in:
parent
acc5f20357
commit
cbf867ff2c
5 changed files with 746 additions and 0 deletions
|
|
@ -37,6 +37,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_ITE_IT8XXX2 uart_ite_it8xxx2.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_UART_LITEX uart_litex.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_LPC11U6X uart_lpc11u6x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_MAX32 uart_max32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_MCHP_MEC5 uart_mchp_mec5.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_MCUX uart_mcux.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_MCUX_FLEXCOMM uart_mcux_flexcomm.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UART_MCUX_IUART uart_mcux_iuart.c)
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ rsource "Kconfig.mcux_flexcomm"
|
|||
rsource "Kconfig.mcux_iuart"
|
||||
rsource "Kconfig.mcux_lpsci"
|
||||
rsource "Kconfig.mcux_lpuart"
|
||||
rsource "Kconfig.mec5"
|
||||
rsource "Kconfig.miv"
|
||||
rsource "Kconfig.msp432p4xx"
|
||||
rsource "Kconfig.native_posix"
|
||||
|
|
|
|||
27
drivers/serial/Kconfig.mec5
Normal file
27
drivers/serial/Kconfig.mec5
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Microchip MEC5 UART
|
||||
|
||||
# Copyright (c) 2024 Microchip Technology Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config UART_MCHP_MEC5
|
||||
bool "Microchip MEC5 family ns16550 compatible UART driver"
|
||||
default y
|
||||
depends on DT_HAS_MICROCHIP_MEC5_UART_ENABLED
|
||||
select SERIAL_HAS_DRIVER
|
||||
select SERIAL_SUPPORT_INTERRUPT
|
||||
help
|
||||
This option enables the UART driver for Microchip MEC5
|
||||
family processors.
|
||||
|
||||
if UART_MCHP_MEC5
|
||||
|
||||
config UART_MCHP_MEC5_LINE_CTRL
|
||||
bool "Serial Line Control for Apps"
|
||||
depends on UART_LINE_CTRL
|
||||
help
|
||||
This enables the API for apps to control the serial line,
|
||||
such as CTS and RTS.
|
||||
|
||||
Says n if not sure.
|
||||
|
||||
endif # UART_MCHP_MEC5
|
||||
662
drivers/serial/uart_mchp_mec5.c
Normal file
662
drivers/serial/uart_mchp_mec5.c
Normal file
|
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Microchip Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Microchip MEC5 ns16550 compatible UART Serial Driver
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT microchip_mec5_uart
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/spinlock.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
LOG_MODULE_REGISTER(uart_mec5, CONFIG_UART_LOG_LEVEL);
|
||||
|
||||
/* MEC5 HAL */
|
||||
#include <device_mec5.h>
|
||||
#include <mec_ecia_api.h>
|
||||
#include <mec_uart_api.h>
|
||||
|
||||
#define UART_MEC_DFLT_CLK_FREQ 1843200u
|
||||
#define UART_MEC_DEVCFG_FLAG_RX_FIFO_TRIG_POS 0
|
||||
#define UART_MEC_DEVCFG_FLAG_RX_FIFO_TRIG_MSK 0x3u
|
||||
#define UART_MEC_DEVCFG_FLAG_FIFO_DIS_POS 4
|
||||
#define UART_MEC_DEVCFG_FLAG_USE_EXTCLK_POS 5
|
||||
|
||||
struct uart_mec5_devcfg {
|
||||
struct mec_uart_regs *regs;
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
uint32_t clock_freq;
|
||||
uint32_t flags;
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct uart_mec5_dev_data {
|
||||
const struct device *dev;
|
||||
struct uart_config current_config;
|
||||
struct uart_config ucfg;
|
||||
struct k_spinlock lock;
|
||||
enum mec_uart_ipend ipend;
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
uart_irq_callback_user_data_t cb; /* Callback function pointer */
|
||||
void *cb_data; /* Callback function arg */
|
||||
uint32_t flags;
|
||||
uint8_t rx_enabled;
|
||||
uint8_t tx_enabled;
|
||||
#endif
|
||||
};
|
||||
|
||||
static const uint8_t mec5_xlat_word_len[4] = {
|
||||
MEC_UART_WORD_LEN_5,
|
||||
MEC_UART_WORD_LEN_6,
|
||||
MEC_UART_WORD_LEN_7,
|
||||
MEC_UART_WORD_LEN_8,
|
||||
};
|
||||
|
||||
static const uint8_t mec5_xlat_stop_bits[4] = {
|
||||
MEC_UART_STOP_BITS_1,
|
||||
MEC_UART_STOP_BITS_1,
|
||||
MEC_UART_STOP_BITS_2,
|
||||
MEC_UART_STOP_BITS_2,
|
||||
};
|
||||
|
||||
static const uint8_t mec5_xlat_parity[5] = {
|
||||
(uint8_t)(MEC5_UART_CFG_PARITY_NONE >> MEC5_UART_CFG_PARITY_POS),
|
||||
(uint8_t)(MEC5_UART_CFG_PARITY_ODD >> MEC5_UART_CFG_PARITY_POS),
|
||||
(uint8_t)(MEC5_UART_CFG_PARITY_EVEN >> MEC5_UART_CFG_PARITY_POS),
|
||||
(uint8_t)(MEC5_UART_CFG_PARITY_MARK >> MEC5_UART_CFG_PARITY_POS),
|
||||
(uint8_t)(MEC5_UART_CFG_PARITY_SPACE >> MEC5_UART_CFG_PARITY_POS),
|
||||
};
|
||||
|
||||
static int uart_mec5_xlat_cfg(const struct uart_config *cfg, uint32_t *cfg_word)
|
||||
{
|
||||
uint32_t temp;
|
||||
|
||||
if (!cfg || !cfg_word) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*cfg_word = 0u;
|
||||
|
||||
if (cfg->data_bits > UART_CFG_DATA_BITS_8) {
|
||||
return -EINVAL;
|
||||
}
|
||||
temp = mec5_xlat_word_len[cfg->data_bits];
|
||||
*cfg_word |= ((temp << MEC5_UART_CFG_WORD_LEN_POS) & MEC5_UART_CFG_WORD_LEN_MSK);
|
||||
|
||||
if (cfg->stop_bits > UART_CFG_STOP_BITS_2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
temp = mec5_xlat_stop_bits[cfg->stop_bits];
|
||||
*cfg_word |= ((temp << MEC5_UART_CFG_STOP_BITS_POS) & MEC5_UART_CFG_STOP_BITS_MSK);
|
||||
|
||||
if (cfg->parity > UART_CFG_PARITY_SPACE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
temp = mec5_xlat_parity[cfg->parity];
|
||||
*cfg_word |= ((temp << MEC5_UART_CFG_PARITY_POS) & MEC5_UART_CFG_PARITY_MSK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Configure UART TX and RX FIFOs based on device tree.
|
||||
* Both FIFOs are fixed 16-byte.
|
||||
* RX FIFO has a configurable interrupt trigger level of 1, 4, 8, or 14 bytes.
|
||||
*/
|
||||
static uint32_t uart_mec5_fifo_config(uint32_t mcfg, uint32_t cfg_flags)
|
||||
{
|
||||
uint32_t new_mcfg = mcfg;
|
||||
uint32_t temp = 0;
|
||||
|
||||
if (!(cfg_flags & BIT(UART_MEC_DEVCFG_FLAG_FIFO_DIS_POS))) {
|
||||
new_mcfg |= BIT(MEC5_UART_CFG_FIFO_EN_POS);
|
||||
temp = (cfg_flags & UART_MEC_DEVCFG_FLAG_RX_FIFO_TRIG_MSK) >>
|
||||
UART_MEC_DEVCFG_FLAG_RX_FIFO_TRIG_POS;
|
||||
switch (temp) {
|
||||
case 0:
|
||||
new_mcfg |= MEC5_UART_CFG_RX_FIFO_TRIG_LVL_1;
|
||||
break;
|
||||
case 1:
|
||||
new_mcfg |= MEC5_UART_CFG_RX_FIFO_TRIG_LVL_4;
|
||||
break;
|
||||
case 2:
|
||||
new_mcfg |= MEC5_UART_CFG_RX_FIFO_TRIG_LVL_8;
|
||||
break;
|
||||
default:
|
||||
new_mcfg |= MEC5_UART_CFG_RX_FIFO_TRIG_LVL_14;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new_mcfg;
|
||||
}
|
||||
|
||||
static int config_mec5_uart(const struct device *dev, const struct uart_config *cfg)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *const data = dev->data;
|
||||
int ret = 0;
|
||||
uint32_t mcfg = 0, extclk = 0;
|
||||
|
||||
data->ipend = MEC_UART_IPEND_NONE;
|
||||
|
||||
ret = uart_mec5_xlat_cfg(cfg, &mcfg);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mcfg = uart_mec5_fifo_config(mcfg, devcfg->flags);
|
||||
mcfg |= BIT(MEC5_UART_CFG_GIRQ_EN_POS);
|
||||
|
||||
if (devcfg->flags & BIT(UART_MEC_DEVCFG_FLAG_USE_EXTCLK_POS)) {
|
||||
extclk = devcfg->clock_freq;
|
||||
}
|
||||
|
||||
ret = mec_hal_uart_init(regs, cfg->baudrate, mcfg, extclk);
|
||||
if (ret != MEC_RET_OK) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(&data->ucfg, cfg, sizeof(struct uart_config));
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
||||
/* run-time driver configuration API */
|
||||
static int uart_mec5_configure(const struct device *dev, const struct uart_config *cfg)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *const data = dev->data;
|
||||
int ret = 0;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret) {
|
||||
LOG_ERR("MEC5 UART pinctrl error (%d)", ret);
|
||||
}
|
||||
|
||||
ret = config_mec5_uart(dev, cfg);
|
||||
if (ret) {
|
||||
LOG_ERR("MEC5 UART config error (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->current_config = *cfg;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uart_mec5_config_get(const struct device *dev, struct uart_config *cfg)
|
||||
{
|
||||
struct uart_mec5_dev_data *const data = dev->data;
|
||||
|
||||
if (!cfg) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
*cfg = data->current_config;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
||||
|
||||
/* Called by kernel during driver initialization phase */
|
||||
static int uart_mec5_init(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
int ret = 0;
|
||||
|
||||
ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret) {
|
||||
LOG_ERR("MEC5 UART init pinctrl error (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = config_mec5_uart(dev, &data->ucfg);
|
||||
if (ret != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the UART for input.
|
||||
* return 0 is a byte arrived else -1 if no data.
|
||||
*/
|
||||
static int uart_mec5_poll_in(const struct device *dev, unsigned char *cptr)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
int ret = 0;
|
||||
|
||||
ret = mec_hal_uart_rx_byte(regs, (uint8_t *)cptr);
|
||||
if (ret == MEC_RET_ERR_NO_DATA) {
|
||||
k_spin_unlock(&data->lock, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Block until UART can accept data byte. */
|
||||
static void uart_mec5_poll_out(const struct device *dev, unsigned char out_data)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
ret = mec_hal_uart_tx_byte(regs, (uint8_t)out_data);
|
||||
} while (ret == MEC_RET_ERR_BUSY);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static inline void irq_tx_enable(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
mec_hal_uart_intr_mask(regs, MEC_UART_IEN_FLAG_ETHREI, MEC_UART_IEN_FLAG_ETHREI);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static inline void irq_tx_disable(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
mec_hal_uart_intr_mask(regs, MEC_UART_IEN_FLAG_ETHREI, 0);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static inline void irq_rx_enable(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
mec_hal_uart_intr_mask(regs, MEC_UART_IEN_FLAG_ERDAI, MEC_UART_IEN_FLAG_ERDAI);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static inline void irq_rx_disable(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
mec_hal_uart_intr_mask(regs, MEC_UART_IEN_FLAG_ERDAI, 0);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static int uart_mec5_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
int num_tx = 0, ret = 0;
|
||||
|
||||
if (len < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
while (num_tx < len) {
|
||||
ret = mec_hal_uart_tx_byte(regs, tx_data[num_tx]);
|
||||
if (ret == MEC_RET_ERR_BUSY) {
|
||||
break;
|
||||
}
|
||||
num_tx++;
|
||||
}
|
||||
|
||||
if (data->tx_enabled) {
|
||||
irq_tx_enable(dev);
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return num_tx;
|
||||
}
|
||||
|
||||
static int uart_mec5_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
int num_rx = 0, ret = 0;
|
||||
|
||||
if (size < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
uint8_t *pdata = rx_data;
|
||||
|
||||
while (num_rx < size) {
|
||||
ret = mec_hal_uart_rx_byte(regs, pdata);
|
||||
if (ret != MEC_RET_OK) {
|
||||
break;
|
||||
}
|
||||
pdata++;
|
||||
num_rx++;
|
||||
}
|
||||
|
||||
if (data->rx_enabled) {
|
||||
irq_rx_enable(dev);
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return num_rx;
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_tx_enable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
data->tx_enabled = 1;
|
||||
irq_tx_enable(dev);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_tx_disable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
irq_tx_disable(dev);
|
||||
data->tx_enabled = 0;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static int uart_mec5_irq_tx_ready(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
int ret = 0;
|
||||
|
||||
if (data->ipend == MEC_UART_IPEND_TX) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_rx_enable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
data->rx_enabled = 1;
|
||||
irq_rx_enable(dev);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_rx_disable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
irq_rx_disable(dev);
|
||||
data->rx_enabled = 0;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
/* check if UART TX shift register is empty. Empty TX shift register indicates
|
||||
* the UART does not need clocks and can be put into a low power state.
|
||||
* return 1 nothing remains to be transmitted, 0 otherwise.
|
||||
*/
|
||||
static int uart_mec5_irq_tx_complete(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
int ret = mec_hal_uart_is_tx_empty(regs);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uart_mec5_irq_rx_ready(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
int ret = 0;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
if (data->ipend == MEC_UART_IPEND_RX_DATA) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void irq_error_enable(const struct device *dev, uint8_t enable)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
uint8_t msk = 0;
|
||||
|
||||
if (enable) {
|
||||
msk = MEC_UART_IEN_FLAG_ELSI;
|
||||
}
|
||||
|
||||
mec_hal_uart_intr_mask(regs, MEC_UART_IEN_FLAG_ELSI, msk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable received line status interrupt active when one or more of the following errors
|
||||
* occur: overrun, parity, framing, or break.
|
||||
*/
|
||||
static void uart_mec5_irq_err_enable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
irq_error_enable(dev, 1u);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_err_disable(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
irq_error_enable(dev, 0);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static int uart_mec5_irq_is_pending(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
int ret = 0;
|
||||
|
||||
if (data->ipend != MEC_UART_IPEND_NONE) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uart_mec5_irq_update(const struct device *dev)
|
||||
{
|
||||
const struct uart_mec5_devcfg *const devcfg = dev->config;
|
||||
struct mec_uart_regs *const regs = devcfg->regs;
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
data->ipend = MEC_UART_IPEND_NONE;
|
||||
mec_hal_uart_pending_status(regs, &data->ipend);
|
||||
|
||||
switch (data->ipend) {
|
||||
case MEC_UART_IPEND_NONE:
|
||||
break;
|
||||
case MEC_UART_IPEND_TX:
|
||||
irq_tx_disable(dev);
|
||||
break;
|
||||
case MEC_UART_IPEND_RX_DATA:
|
||||
irq_rx_disable(dev);
|
||||
break;
|
||||
case MEC_UART_IPEND_RX_ERR:
|
||||
irq_error_enable(dev, 0);
|
||||
break;
|
||||
case MEC_UART_IPEND_MODEM:
|
||||
__fallthrough; /* fall through */
|
||||
default:
|
||||
k_panic();
|
||||
break;
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void uart_mec5_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
|
||||
void *cb_data)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
data->cb = cb;
|
||||
data->cb_data = cb_data;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static void uart_mec5_isr(const struct device *dev)
|
||||
{
|
||||
struct uart_mec5_dev_data *data = dev->data;
|
||||
|
||||
if (data->cb) {
|
||||
data->cb(dev, data->cb_data);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
static const struct uart_driver_api uart_mec5_driver_api = {
|
||||
.poll_in = uart_mec5_poll_in,
|
||||
.poll_out = uart_mec5_poll_out,
|
||||
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
||||
.configure = uart_mec5_configure,
|
||||
.config_get = uart_mec5_config_get,
|
||||
#endif
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.fifo_fill = uart_mec5_fifo_fill,
|
||||
.fifo_read = uart_mec5_fifo_read,
|
||||
.irq_tx_enable = uart_mec5_irq_tx_enable,
|
||||
.irq_tx_disable = uart_mec5_irq_tx_disable,
|
||||
.irq_tx_ready = uart_mec5_irq_tx_ready,
|
||||
.irq_rx_enable = uart_mec5_irq_rx_enable,
|
||||
.irq_rx_disable = uart_mec5_irq_rx_disable,
|
||||
.irq_tx_complete = uart_mec5_irq_tx_complete,
|
||||
.irq_rx_ready = uart_mec5_irq_rx_ready,
|
||||
.irq_err_enable = uart_mec5_irq_err_enable,
|
||||
.irq_err_disable = uart_mec5_irq_err_disable,
|
||||
.irq_is_pending = uart_mec5_irq_is_pending,
|
||||
.irq_update = uart_mec5_irq_update,
|
||||
.irq_callback_set = uart_mec5_irq_callback_set,
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
#define UART_MEC5_CONFIGURE(n) \
|
||||
do { \
|
||||
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uart_mec5_isr, \
|
||||
DEVICE_DT_INST_GET(n), 0); \
|
||||
\
|
||||
irq_enable(DT_INST_IRQN(n)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define UART_MEC5_CONFIGURE(n)
|
||||
#endif
|
||||
|
||||
#define UART_MEC5_DCFG_FLAGS(i) \
|
||||
((DT_INST_ENUM_IDX_OR(i, rx_fifo_trig, 2) & 0x3u) | \
|
||||
((DT_INST_PROP_OR(i, fifo_mode_disable, 0) & 0x1u) << 4) | \
|
||||
((DT_INST_PROP_OR(i, use_extclk, 0) & 0x1u) << 5))
|
||||
|
||||
#define DEV_DATA_FLOW_CTRL(n) DT_INST_PROP_OR(n, hw_flow_control, UART_CFG_FLOW_CTRL_NONE)
|
||||
|
||||
#define UART_MEC5_DEVICE(i) \
|
||||
PINCTRL_DT_INST_DEFINE(i); \
|
||||
static const struct uart_mec5_devcfg uart_mec5_##i##_devcfg = { \
|
||||
.regs = (struct mec_uart_regs *)DT_INST_REG_ADDR(i), \
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i), \
|
||||
.clock_freq = DT_INST_PROP_OR(i, clock_frequency, UART_MEC_DFLT_CLK_FREQ), \
|
||||
.flags = UART_MEC5_DCFG_FLAGS(i), \
|
||||
}; \
|
||||
static struct uart_mec5_dev_data uart_mec5_##i##_dev_data = { \
|
||||
.ucfg.baudrate = DT_INST_PROP_OR(i, current_speed, 0), \
|
||||
.ucfg.parity = UART_CFG_PARITY_NONE, \
|
||||
.ucfg.stop_bits = UART_CFG_STOP_BITS_1, \
|
||||
.ucfg.data_bits = UART_CFG_DATA_BITS_8, \
|
||||
.ucfg.flow_ctrl = DEV_DATA_FLOW_CTRL(i), \
|
||||
}; \
|
||||
static int uart_mec5_##i##_init(const struct device *dev) \
|
||||
{ \
|
||||
UART_MEC5_CONFIGURE(i); \
|
||||
return uart_mec5_init(dev); \
|
||||
} \
|
||||
DEVICE_DT_INST_DEFINE(i, uart_mec5_##i##_init, NULL, &uart_mec5_##i##_dev_data, \
|
||||
&uart_mec5_##i##_devcfg, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
|
||||
&uart_mec5_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(UART_MEC5_DEVICE)
|
||||
55
dts/bindings/serial/microchip,mec5-uart.yaml
Normal file
55
dts/bindings/serial/microchip,mec5-uart.yaml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright (c) 2024 Microchip Technology Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Microchip MEC5 UART
|
||||
|
||||
compatible: "microchip,mec5-uart"
|
||||
|
||||
include: [uart-controller.yaml, pinctrl-device.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
pinctrl-0:
|
||||
required: true
|
||||
|
||||
pinctrl-names:
|
||||
required: true
|
||||
|
||||
fifo-mode-disable:
|
||||
type: boolean
|
||||
description: |
|
||||
Disable 16550 FIFO mode. Both 16-byte TX and RX FIFOs will be
|
||||
disabled. UART will revert to a one byte holding register for
|
||||
TX and RX.
|
||||
|
||||
rx-fifo-trig:
|
||||
type: string
|
||||
default: "8"
|
||||
description: |
|
||||
RX FIFO byte count trigger limit. When the number of received bytes
|
||||
reaches this level the UART will signal an interrupt if enabled.
|
||||
enum:
|
||||
- "1"
|
||||
- "4"
|
||||
- "8"
|
||||
- "14"
|
||||
|
||||
use-extclk:
|
||||
type: boolean
|
||||
description: |
|
||||
Optional source of an external UART clock. If present the
|
||||
driver will use this pin as the UART input clock source.
|
||||
The pin should have a 1.8432 MHz clock waveform for normal
|
||||
UART BAUD rates or 48 MHz for high speed BAUD rates.
|
||||
Refer to data sheet for the pin(s) available as external UART
|
||||
clock input. The pin should be added to the default PINCTRL list.
|
||||
Example using external 1.8432MHz clock on MEC5 external UART clock pin.
|
||||
|
||||
clock-frequency = <1843200>;
|
||||
pinctrl-0 = < &uart1_tx_gpio170 &uart1_tx_gpio171 &uart_clk_gpio025>;
|
||||
pinctrl-names = "default";
|
||||
Loading…
Reference in a new issue