zephyr/drivers/serial/uart_imx.c
Martí Bolívar 7e0eed9235 devicetree: allow access to all nodes
Usually, we want to operate only on "available" device
nodes ("available" means "status is okay and a matching binding is
found"), but that's not true in all cases.

Sometimes we want to operate on special nodes without matching
bindings, such as those describing memory.

To handle the distinction, change various additional devicetree APIs
making it clear that they operate only on available device nodes,
adjusting gen_defines and devicetree.h implementation details
accordingly:

- emit macros for all existing nodes in gen_defines.py, regardless
  of status or matching binding
- rename DT_NUM_INST to DT_NUM_INST_STATUS_OKAY
- rename DT_NODE_HAS_COMPAT to DT_NODE_HAS_COMPAT_STATUS_OKAY
- rename DT_INST_FOREACH to DT_INST_FOREACH_STATUS_OKAY
- rename DT_ANY_INST_ON_BUS to DT_ANY_INST_ON_BUS_STATUS_OKAY
- rewrite DT_HAS_NODE_STATUS_OKAY in terms of a new DT_NODE_HAS_STATUS
- resurrect DT_HAS_NODE in the form of DT_NODE_EXISTS
- remove DT_COMPAT_ON_BUS as a public API
- use the new default_prop_types edtlib parameter

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-05-08 19:37:18 -05:00

329 lines
7.7 KiB
C

/*
* Copyright (c) 2018 Diego Sueiro <diego.sueiro@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_imx_uart
/**
* @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 <sys/__assert.h>
#include <soc.h>
#include <init.h>
#include <drivers/uart.h>
#include <uart_imx.h>
#define DEV_CFG(dev) \
((const struct imx_uart_config *const)(dev)->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_user_data_t callback;
void *cb_data;
#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_info;
unsigned 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, 2);
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 void 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);
}
static int uart_imx_poll_in(struct device *dev, unsigned char *c)
{
UART_Type *uart = UART_STRUCT(dev);
while (!UART_GetStatusFlag(uart, uartStatusRxDataReady)) {
}
*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 = 0U;
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 = 0U;
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_user_data_t cb,
void *cb_data)
{
struct imx_uart_data *data = dev->driver_data;
data->callback = cb;
data->cb_data = cb_data;
}
/**
* @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(data->cb_data);
}
}
#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 */
};
#define UART_IMX_DECLARE_CFG(n, IRQ_FUNC_INIT) \
static const struct imx_uart_config imx_uart_##n##_config = { \
.base = (UART_Type *) DT_INST_REG_ADDR(n), \
.baud_rate = DT_INST_PROP(n, current_speed), \
.modem_mode = DT_INST_PROP(n, modem_mode), \
IRQ_FUNC_INIT \
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
#define UART_IMX_CONFIG_FUNC(n) \
static void irq_config_func_##n(struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), \
DT_INST_IRQ(n, priority), \
uart_imx_isr, \
DEVICE_GET(uart_##n), 0); \
irq_enable(DT_INST_IRQN(n)); \
}
#define UART_IMX_IRQ_CFG_FUNC_INIT(n) \
.irq_config_func = irq_config_func_##n
#define UART_IMX_INIT_CFG(n) \
UART_IMX_DECLARE_CFG(n, UART_IMX_IRQ_CFG_FUNC_INIT(n))
#else
#define UART_IMX_CONFIG_FUNC(n)
#define UART_IMX_IRQ_CFG_FUNC_INIT
#define UART_IMX_INIT_CFG(n) \
UART_IMX_DECLARE_CFG(n, UART_IMX_IRQ_CFG_FUNC_INIT)
#endif
#define UART_IMX_INIT(n) \
static struct imx_uart_data imx_uart_##n##_data; \
\
static const struct imx_uart_config imx_uart_##n##_config; \
\
DEVICE_AND_API_INIT(uart_##n, DT_INST_LABEL(n), &uart_imx_init, \
&imx_uart_##n##_data, &imx_uart_##n##_config, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_imx_driver_api); \
\
UART_IMX_CONFIG_FUNC(n) \
\
UART_IMX_INIT_CFG(n);
DT_INST_FOREACH_STATUS_OKAY(UART_IMX_INIT)