serial: Add imx uart driver shim

Adds a shim layer around the imx uart driver to adapt it to the Zephyr
serial interface.

Modem mode was introduce to control it as DCE and DTE and can be
configured in the device tree:
    modem-mode:
        type: int
        category: required
        description: Set the UART Port to modem mode 0 (dce) 1 (dte)
        generation: define

For now only the UART 2 was tested.

Signed-off-by: Diego Sueiro <diego.sueiro@gmail.com>
This commit is contained in:
Diego Sueiro 2018-03-14 18:36:53 +00:00 committed by Maureen Helm
commit 346165b2e8
6 changed files with 660 additions and 0 deletions

528
drivers/serial/uart_imx.c Normal file
View file

@ -0,0 +1,528 @@
/*
* Copyright (c) 2018 Diego Sueiro <diego.sueiro@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Driver for UART on NXP IMX family processor.
*
* For full serial function, use the USART controller.
*
*/
#include <kernel.h>
#include <arch/cpu.h>
#include <misc/__assert.h>
#include <board.h>
#include <init.h>
#include <uart.h>
#include <uart_imx.h>
#define DEV_CFG(dev) \
((const struct imx_uart_config *const)(dev)->config->config_info)
#define UART_STRUCT(dev) \
((UART_Type *)(DEV_CFG(dev))->base)
struct imx_uart_config {
UART_Type *base;
u32_t baud_rate;
u8_t modem_mode;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
void (*irq_config_func)(struct device *dev);
#endif
};
struct imx_uart_data {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_t callback;
#endif
};
/**
* @brief Initialize UART channel
*
* This routine is called to reset the chip in a quiescent state.
* It is assumed that this function is called only once per UART.
*
* @param dev UART device struct
*
* @return 0
*/
static int uart_imx_init(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
const struct imx_uart_config *config = dev->config->config_info;
int old_level;
/* disable interrupts */
old_level = irq_lock();
/* Setup UART init structure */
uart_init_config_t initConfig = {
.baudRate = config->baud_rate,
.wordLength = uartWordLength8Bits,
.stopBitNum = uartStopBitNumOne,
.parity = uartParityDisable,
.direction = uartDirectionTxRx
};
/* Get current module clock frequency */
initConfig.clockRate = get_uart_clock_freq(uart);
UART_Init(uart, &initConfig);
/* Set UART build-in hardware FIFO Watermark. */
UART_SetTxFifoWatermark(uart, 1);
UART_SetRxFifoWatermark(uart, 1);
/* restore interrupt state */
irq_unlock(old_level);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
config->irq_config_func(dev);
#endif
/* Set UART modem mode */
UART_SetModemMode(uart, config->modem_mode);
/* Finally, enable the UART module */
UART_Enable(uart);
return 0;
}
static unsigned char uart_imx_poll_out(struct device *dev, unsigned char c)
{
UART_Type *uart = UART_STRUCT(dev);
while (!UART_GetStatusFlag(uart, uartStatusTxReady))
;
UART_Putchar(uart, c);
return c;
}
static int uart_imx_poll_in(struct device *dev, unsigned char *c)
{
UART_Type *uart = UART_STRUCT(dev);
while (!UART_GetStatusFlag(uart, uartStatusRxReady))
;
*c = UART_Getchar(uart);
if (UART_GetStatusFlag(uart, uartStatusRxOverrun)) {
UART_ClearStatusFlag(uart, uartStatusRxOverrun);
}
return 0;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_imx_fifo_fill(struct device *dev, const u8_t *tx_data,
int size)
{
UART_Type *uart = UART_STRUCT(dev);
unsigned int num_tx = 0;
while (((size - num_tx) > 0) &&
UART_GetStatusFlag(uart, uartStatusTxReady)) {
/* Send a character */
UART_Putchar(uart, tx_data[num_tx]);
num_tx++;
}
return (int)num_tx;
}
static int uart_imx_fifo_read(struct device *dev, u8_t *rx_data,
const int size)
{
UART_Type *uart = UART_STRUCT(dev);
unsigned int num_rx = 0;
while (((size - num_rx) > 0) &&
UART_GetStatusFlag(uart, uartStatusRxReady)) {
/* Receive a character */
rx_data[num_rx++] = UART_Getchar(uart);
}
if (UART_GetStatusFlag(uart, uartStatusRxOverrun)) {
UART_ClearStatusFlag(uart, uartStatusRxOverrun);
}
return num_rx;
}
static void uart_imx_irq_tx_enable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntTxReady, true);
}
static void uart_imx_irq_tx_disable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntTxReady, false);
}
static int uart_imx_irq_tx_ready(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
return UART_GetStatusFlag(uart, uartStatusTxReady);
}
static void uart_imx_irq_rx_enable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntRxReady, true);
}
static void uart_imx_irq_rx_disable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntRxReady, false);
}
static int uart_imx_irq_rx_ready(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
return UART_GetStatusFlag(uart, uartStatusRxReady);
}
static void uart_imx_irq_err_enable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntParityError, true);
UART_SetIntCmd(uart, uartIntFrameError, true);
}
static void uart_imx_irq_err_disable(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
UART_SetIntCmd(uart, uartIntParityError, false);
UART_SetIntCmd(uart, uartIntFrameError, false);
}
static int uart_imx_irq_is_pending(struct device *dev)
{
UART_Type *uart = UART_STRUCT(dev);
return UART_GetStatusFlag(uart, uartStatusRxReady) ||
UART_GetStatusFlag(uart, uartStatusTxReady);
}
static int uart_imx_irq_update(struct device *dev)
{
return 1;
}
static void uart_imx_irq_callback_set(struct device *dev,
uart_irq_callback_t cb)
{
struct imx_uart_data *data = dev->driver_data;
data->callback = cb;
}
/**
* @brief Interrupt service routine.
*
* This simply calls the callback function, if one exists.
*
* Note: imx UART Tx interrupts when ready to send; Rx interrupts when char
* received.
*
* @param arg Argument to ISR.
*
* @return N/A
*/
void uart_imx_isr(void *arg)
{
struct device *dev = arg;
struct imx_uart_data *data = dev->driver_data;
if (data->callback) {
data->callback(dev);
}
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static const struct uart_driver_api uart_imx_driver_api = {
.poll_in = uart_imx_poll_in,
.poll_out = uart_imx_poll_out,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_imx_fifo_fill,
.fifo_read = uart_imx_fifo_read,
.irq_tx_enable = uart_imx_irq_tx_enable,
.irq_tx_disable = uart_imx_irq_tx_disable,
.irq_tx_ready = uart_imx_irq_tx_ready,
.irq_rx_enable = uart_imx_irq_rx_enable,
.irq_rx_disable = uart_imx_irq_rx_disable,
.irq_rx_ready = uart_imx_irq_rx_ready,
.irq_err_enable = uart_imx_irq_err_enable,
.irq_err_disable = uart_imx_irq_err_disable,
.irq_is_pending = uart_imx_irq_is_pending,
.irq_update = uart_imx_irq_update,
.irq_callback_set = uart_imx_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
#ifdef CONFIG_UART_IMX_UART_1
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_1(struct device *port);
#endif
static const struct imx_uart_config imx_uart_1_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_1_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_1_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_1_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_1,
#endif
};
static struct imx_uart_data imx_uart_1_data;
DEVICE_AND_API_INIT(uart_1, CONFIG_UART_IMX_UART_1_NAME, &uart_imx_init,
&imx_uart_1_data, &imx_uart_1_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_1(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_1_IRQ_NUM,
CONFIG_UART_IMX_UART_1_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_1),
0);
irq_enable(CONFIG_UART_IMX_UART_1_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_1 */
#ifdef CONFIG_UART_IMX_UART_2
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_2(struct device *port);
#endif
static const struct imx_uart_config imx_uart_2_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_2_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_2_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_2_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_2,
#endif
};
static struct imx_uart_data imx_uart_2_data;
DEVICE_AND_API_INIT(uart_2, CONFIG_UART_IMX_UART_2_NAME, &uart_imx_init,
&imx_uart_2_data, &imx_uart_2_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_2(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_2_IRQ_NUM,
CONFIG_UART_IMX_UART_2_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_2),
0);
irq_enable(CONFIG_UART_IMX_UART_2_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_2 */
#ifdef CONFIG_UART_IMX_UART_3
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_3(struct device *port);
#endif
static const struct imx_uart_config imx_uart_3_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_3_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_3_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_3_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_3,
#endif
};
static struct imx_uart_data imx_uart_3_data;
DEVICE_AND_API_INIT(uart_3, CONFIG_UART_IMX_UART_3_NAME, &uart_imx_init,
&imx_uart_3_data, &imx_uart_3_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_3(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_3_IRQ_NUM,
CONFIG_UART_IMX_UART_3_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_3),
0);
irq_enable(CONFIG_UART_IMX_UART_3_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_3 */
#ifdef CONFIG_UART_IMX_UART_4
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_4(struct device *port);
#endif
static const struct imx_uart_config imx_uart_4_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_4_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_4_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_4_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_4,
#endif
};
static struct imx_uart_data imx_uart_4_data;
DEVICE_AND_API_INIT(uart_4, CONFIG_UART_IMX_UART_4_NAME, &uart_imx_init,
&imx_uart_4_data, &imx_uart_4_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_4(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_4_IRQ_NUM,
CONFIG_UART_IMX_UART_4_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_4),
0);
irq_enable(CONFIG_UART_IMX_UART_4_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_4 */
#ifdef CONFIG_UART_IMX_UART_5
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_5(struct device *port);
#endif
static const struct imx_uart_config imx_uart_5_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_5_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_5_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_5_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_5,
#endif
};
static struct imx_uart_data imx_uart_5_data;
DEVICE_AND_API_INIT(uart_5, CONFIG_UART_IMX_UART_5_NAME, &uart_imx_init,
&imx_uart_5_data, &imx_uart_5_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_5(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_5_IRQ_NUM,
CONFIG_UART_IMX_UART_5_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_5),
0);
irq_enable(CONFIG_UART_IMX_UART_5_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_5 */
#ifdef CONFIG_UART_IMX_UART_6
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_6(struct device *port);
#endif
static const struct imx_uart_config imx_uart_6_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_6_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_6_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_6_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_6,
#endif
};
static struct imx_uart_data imx_uart_6_data;
DEVICE_AND_API_INIT(uart_6, CONFIG_UART_IMX_UART_6_NAME, &uart_imx_init,
&imx_uart_6_data, &imx_uart_6_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_6(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_6_IRQ_NUM,
CONFIG_UART_IMX_UART_6_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_6),
0);
irq_enable(CONFIG_UART_IMX_UART_6_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_6 */
#ifdef CONFIG_UART_IMX_UART_7
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_7(struct device *port);
#endif
static const struct imx_uart_config imx_uart_7_config = {
.base = (UART_Type *) CONFIG_UART_IMX_UART_7_BASE_ADDRESS,
.baud_rate = CONFIG_UART_IMX_UART_7_BAUD_RATE,
.modem_mode = CONFIG_UART_IMX_UART_7_MODEM_MODE,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.irq_config_func = irq_config_func_7,
#endif
};
static struct imx_uart_data imx_uart_7_data;
DEVICE_AND_API_INIT(uart_7, CONFIG_UART_IMX_UART_7_NAME, &uart_imx_init,
&imx_uart_7_data, &imx_uart_7_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&uart_imx_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void irq_config_func_7(struct device *dev)
{
IRQ_CONNECT(CONFIG_UART_IMX_UART_7_IRQ_NUM,
CONFIG_UART_IMX_UART_7_IRQ_PRI,
uart_imx_isr, DEVICE_GET(uart_7),
0);
irq_enable(CONFIG_UART_IMX_UART_7_IRQ_NUM);
}
#endif
#endif /* CONFIG_UART_IMX_UART_7 */