drivers: uart_mcux_lpuart: Fix APIs used together

Handle controller instances wanting to use different
API instances, need to change init flow so that they dont
interfere with each other, trick for now is returning
either the interrupt driven or async apis as not enabled
if the other has already been used on that controller.

Previously, there was an issue where enabling CONFIG_UART_ASYNC_API
would overwrite the init even if controller was meant to use
interrupt driven (as kconfig is global to affect all the controllers)

Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
Declan Snyder 2024-04-24 19:37:48 -05:00 committed by Alberto Escolar
parent dfe4991a2c
commit 9320907fdf

View file

@ -29,6 +29,14 @@ LOG_MODULE_REGISTER(uart_mcux_lpuart, LOG_LEVEL_ERR);
#define PINCTRL_STATE_FLOWCONTROL PINCTRL_STATE_PRIV_START #define PINCTRL_STATE_FLOWCONTROL PINCTRL_STATE_PRIV_START
#if defined(CONFIG_UART_ASYNC_API) && defined(CONFIG_UART_INTERRUPT_DRIVEN)
/* there are already going to be build errors, but at least this message will
* be the first error from this driver making the reason clear
*/
BUILD_ASSERT(IS_ENABLED(CONFIG_UART_EXCLUSIVE_API_CALLBACKS), ""
"LPUART must use exclusive api callbacks");
#endif
#ifdef CONFIG_UART_ASYNC_API #ifdef CONFIG_UART_ASYNC_API
struct lpuart_dma_config { struct lpuart_dma_config {
const struct device *dma_dev; const struct device *dma_dev;
@ -89,6 +97,14 @@ struct mcux_lpuart_async_data {
}; };
#endif #endif
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
enum mcux_lpuart_api {
LPUART_NONE,
LPUART_IRQ_DRIVEN,
LPUART_ASYNC
};
#endif
struct mcux_lpuart_data { struct mcux_lpuart_data {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN #ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t callback; uart_irq_callback_user_data_t callback;
@ -103,6 +119,9 @@ struct mcux_lpuart_data {
struct mcux_lpuart_async_data async; struct mcux_lpuart_async_data async;
#endif #endif
struct uart_config uart_config; struct uart_config uart_config;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
enum mcux_lpuart_api api_type;
#endif
}; };
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -374,12 +393,19 @@ static void mcux_lpuart_irq_callback_set(const struct device *dev,
{ {
struct mcux_lpuart_data *data = dev->data; struct mcux_lpuart_data *data = dev->data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
if (data->api_type == LPUART_ASYNC) {
LOG_ERR("UART irq and async api are exclusive");
}
#endif
data->callback = cb; data->callback = cb;
data->cb_data = cb_data; data->cb_data = cb_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) #if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->async.user_callback = NULL; data->async.user_callback = NULL;
data->async.user_data = NULL; data->async.user_data = NULL;
data->api_type = LPUART_IRQ_DRIVEN;
#endif #endif
} }
@ -664,20 +690,31 @@ static void dma_callback(const struct device *dma_dev, void *callback_arg, uint3
} }
} }
static int mcux_lpuart_configure_async(const struct device *dev);
static int mcux_lpuart_callback_set(const struct device *dev, uart_callback_t callback, static int mcux_lpuart_callback_set(const struct device *dev, uart_callback_t callback,
void *user_data) void *user_data)
{ {
struct mcux_lpuart_data *data = dev->data; struct mcux_lpuart_data *data = dev->data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
if (data->api_type == LPUART_IRQ_DRIVEN) {
LOG_ERR("UART irq and async api are exclusive");
return -ENOTSUP;
}
#endif
data->async.user_callback = callback; data->async.user_callback = callback;
data->async.user_data = user_data; data->async.user_data = user_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS) #if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->callback = NULL; data->callback = NULL;
data->cb_data = NULL; data->cb_data = NULL;
#endif data->api_type = LPUART_ASYNC;
return mcux_lpuart_configure_async(dev);
#else
return 0; return 0;
#endif
} }
static int mcux_lpuart_tx(const struct device *dev, const uint8_t *buf, size_t len, static int mcux_lpuart_tx(const struct device *dev, const uint8_t *buf, size_t len,
@ -857,6 +894,34 @@ static void mcux_lpuart_async_tx_timeout(struct k_work *work)
#endif /* CONFIG_UART_ASYNC_API */ #endif /* CONFIG_UART_ASYNC_API */
#if CONFIG_UART_MCUX_LPUART_ISR_SUPPORT #if CONFIG_UART_MCUX_LPUART_ISR_SUPPORT
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static inline void mcux_lpuart_irq_driven_isr(const struct device *dev,
struct mcux_lpuart_data *data,
const struct mcux_lpuart_config *config,
const uint32_t status) {
if (data->callback) {
data->callback(dev, data->cb_data);
}
if (status & kLPUART_RxOverrunFlag) {
LPUART_ClearStatusFlags(config->base, kLPUART_RxOverrunFlag);
}
}
#endif
#ifdef CONFIG_UART_ASYNC_API
static inline void mcux_lpuart_async_isr(struct mcux_lpuart_data *data,
const struct mcux_lpuart_config *config,
const uint32_t status) {
if (status & kLPUART_IdleLineFlag) {
async_timer_start(&data->async.rx_dma_params.timeout_work,
data->async.rx_dma_params.timeout_us);
LPUART_ClearStatusFlags(config->base, kLPUART_IdleLineFlag);
}
}
#endif
static void mcux_lpuart_isr(const struct device *dev) static void mcux_lpuart_isr(const struct device *dev)
{ {
struct mcux_lpuart_data *data = dev->data; struct mcux_lpuart_data *data = dev->data;
@ -876,54 +941,33 @@ static void mcux_lpuart_isr(const struct device *dev)
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#if CONFIG_UART_INTERRUPT_DRIVEN #if defined(CONFIG_UART_ASYNC_API) && defined(CONFIG_UART_INTERRUPT_DRIVEN)
if (data->callback) { if (data->api_type == LPUART_IRQ_DRIVEN) {
data->callback(dev, data->cb_data); mcux_lpuart_irq_driven_isr(dev, data, config, status);
} else if (data->api_type == LPUART_ASYNC) {
mcux_lpuart_async_isr(data, config, status);
} }
#elif defined(CONFIG_UART_INTERRUPT_DRIVEN)
if (status & kLPUART_RxOverrunFlag) { mcux_lpuart_irq_driven_isr(dev, data, config, status);
LPUART_ClearStatusFlags(config->base, kLPUART_RxOverrunFlag); #elif defined(CONFIG_UART_ASYNC_API)
} mcux_lpuart_async_isr(data, config, status);
#endif #endif /* API */
#if CONFIG_UART_ASYNC_API
if (status & kLPUART_IdleLineFlag) {
async_timer_start(&data->async.rx_dma_params.timeout_work,
data->async.rx_dma_params.timeout_us);
LPUART_ClearStatusFlags(config->base, kLPUART_IdleLineFlag);
}
#endif /* CONFIG_UART_ASYNC_API */
} }
#endif /* CONFIG_UART_MCUX_LPUART_ISR_SUPPORT */ #endif /* CONFIG_UART_MCUX_LPUART_ISR_SUPPORT */
static int mcux_lpuart_configure_init(const struct device *dev, const struct uart_config *cfg) static int mcux_lpuart_configure_basic(const struct device *dev, const struct uart_config *cfg,
lpuart_config_t *uart_config)
{ {
const struct mcux_lpuart_config *config = dev->config;
struct mcux_lpuart_data *data = dev->data;
uint32_t clock_freq;
if (!device_is_ready(config->clock_dev)) {
return -ENODEV;
}
if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
&clock_freq)) {
return -EINVAL;
}
lpuart_config_t uart_config;
LPUART_GetDefaultConfig(&uart_config);
/* Translate UART API enum to LPUART enum from HAL */ /* Translate UART API enum to LPUART enum from HAL */
switch (cfg->parity) { switch (cfg->parity) {
case UART_CFG_PARITY_NONE: case UART_CFG_PARITY_NONE:
uart_config.parityMode = kLPUART_ParityDisabled; uart_config->parityMode = kLPUART_ParityDisabled;
break; break;
case UART_CFG_PARITY_ODD: case UART_CFG_PARITY_ODD:
uart_config.parityMode = kLPUART_ParityOdd; uart_config->parityMode = kLPUART_ParityOdd;
break; break;
case UART_CFG_PARITY_EVEN: case UART_CFG_PARITY_EVEN:
uart_config.parityMode = kLPUART_ParityEven; uart_config->parityMode = kLPUART_ParityEven;
break; break;
default: default:
return -ENOTSUP; return -ENOTSUP;
@ -933,11 +977,11 @@ static int mcux_lpuart_configure_init(const struct device *dev, const struct uar
#if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && \ #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && \
FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT
case UART_CFG_DATA_BITS_7: case UART_CFG_DATA_BITS_7:
uart_config.dataBitsCount = kLPUART_SevenDataBits; uart_config->dataBitsCount = kLPUART_SevenDataBits;
break; break;
#endif #endif
case UART_CFG_DATA_BITS_8: case UART_CFG_DATA_BITS_8:
uart_config.dataBitsCount = kLPUART_EightDataBits; uart_config->dataBitsCount = kLPUART_EightDataBits;
break; break;
default: default:
return -ENOTSUP; return -ENOTSUP;
@ -947,10 +991,10 @@ static int mcux_lpuart_configure_init(const struct device *dev, const struct uar
FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT
switch (cfg->stop_bits) { switch (cfg->stop_bits) {
case UART_CFG_STOP_BITS_1: case UART_CFG_STOP_BITS_1:
uart_config.stopBitCount = kLPUART_OneStopBit; uart_config->stopBitCount = kLPUART_OneStopBit;
break; break;
case UART_CFG_STOP_BITS_2: case UART_CFG_STOP_BITS_2:
uart_config.stopBitCount = kLPUART_TwoStopBit; uart_config->stopBitCount = kLPUART_TwoStopBit;
break; break;
default: default:
return -ENOTSUP; return -ENOTSUP;
@ -962,25 +1006,41 @@ static int mcux_lpuart_configure_init(const struct device *dev, const struct uar
switch (cfg->flow_ctrl) { switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE: case UART_CFG_FLOW_CTRL_NONE:
case UART_CFG_FLOW_CTRL_RS485: case UART_CFG_FLOW_CTRL_RS485:
uart_config.enableTxCTS = false; uart_config->enableTxCTS = false;
uart_config.enableRxRTS = false; uart_config->enableRxRTS = false;
break; break;
case UART_CFG_FLOW_CTRL_RTS_CTS: case UART_CFG_FLOW_CTRL_RTS_CTS:
uart_config.enableTxCTS = true; uart_config->enableTxCTS = true;
uart_config.enableRxRTS = true; uart_config->enableRxRTS = true;
break; break;
default: default:
return -ENOTSUP; return -ENOTSUP;
} }
#endif #endif
uart_config.baudRate_Bps = cfg->baudrate; uart_config->baudRate_Bps = cfg->baudrate;
uart_config.enableRx = true; uart_config->enableRx = true;
/* Tx will be enabled manually after set tx-rts */ /* Tx will be enabled manually after set tx-rts */
uart_config.enableTx = false; uart_config->enableTx = false;
return 0;
}
#ifdef CONFIG_UART_ASYNC_API #ifdef CONFIG_UART_ASYNC_API
static int mcux_lpuart_configure_async(const struct device *dev)
{
const struct mcux_lpuart_config *config = dev->config;
struct mcux_lpuart_data *data = dev->data;
lpuart_config_t uart_config;
int ret;
LPUART_GetDefaultConfig(&uart_config);
ret = mcux_lpuart_configure_basic(dev, &data->uart_config, &uart_config);
if (ret) {
return ret;
}
uart_config.rxIdleType = kLPUART_IdleTypeStopBit; uart_config.rxIdleType = kLPUART_IdleTypeStopBit;
uart_config.rxIdleConfig = kLPUART_IdleCharacter1; uart_config.rxIdleConfig = kLPUART_IdleCharacter1;
data->async.next_rx_buffer = NULL; data->async.next_rx_buffer = NULL;
@ -995,8 +1055,38 @@ static int mcux_lpuart_configure_init(const struct device *dev, const struct uar
* to receive into with rx_enable * to receive into with rx_enable
*/ */
uart_config.enableRx = false; uart_config.enableRx = false;
/* Clearing the fifo of any junk received before the async rx enable was called */
while (LPUART_GetRxFifoCount(config->base) > 0) {
LPUART_ReadByte(config->base);
}
#endif /* CONFIG_UART_ASYNC_API */ return 0;
}
#endif
static int mcux_lpuart_configure_init(const struct device *dev, const struct uart_config *cfg)
{
const struct mcux_lpuart_config *config = dev->config;
struct mcux_lpuart_data *data = dev->data;
lpuart_config_t uart_config;
uint32_t clock_freq;
int ret;
if (!device_is_ready(config->clock_dev)) {
return -ENODEV;
}
if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
&clock_freq)) {
return -EINVAL;
}
LPUART_GetDefaultConfig(&uart_config);
ret = mcux_lpuart_configure_basic(dev, cfg, &uart_config);
if (ret) {
return ret;
}
LPUART_Init(config->base, &uart_config, clock_freq); LPUART_Init(config->base, &uart_config, clock_freq);
@ -1091,6 +1181,9 @@ static int mcux_lpuart_init(const struct device *dev)
/* Interrupt is managed by this driver */ /* Interrupt is managed by this driver */
config->irq_config_func(dev); config->irq_config_func(dev);
#endif #endif
#ifdef CONFIG_UART_EXCLUSIVE_API_CALLBACKS
data->api_type = LPUART_NONE;
#endif
#endif #endif
#ifdef CONFIG_PM #ifdef CONFIG_PM