zephyr/drivers/serial/uart_nuvoton.c

210 lines
4.8 KiB
C

/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2020 Linumiz
* Author: Saravanan Sekar <saravanan@linumiz.com>
*/
#include <drivers/uart.h>
#include <NuMicro.h>
#include <string.h>
#define DT_DRV_COMPAT nuvoton_numicro_uart
/* Device data structure */
#define DEV_CFG(dev) \
((const struct uart_numicro_config * const)(dev)->config)
#define DRV_DATA(dev) \
((struct uart_numicro_data * const)(dev)->data)
#define UART_STRUCT(dev) \
((UART_T *)(DEV_CFG(dev))->devcfg.base)
struct uart_numicro_config {
struct uart_device_config devcfg;
uint32_t id_rst;
uint32_t id_clk;
};
struct uart_numicro_data {
const struct device *clock;
struct uart_config ucfg;
};
static int uart_numicro_poll_in(const struct device *dev, unsigned char *c)
{
uint32_t count;
count = UART_Read(UART_STRUCT(dev), c, 1);
if (!count) {
return -1;
}
return 0;
}
static void uart_numicro_poll_out(const struct device *dev, unsigned char c)
{
UART_Write(UART_STRUCT(dev), &c, 1);
}
static int uart_numicro_err_check(const struct device *dev)
{
return 0;
}
static inline int32_t uart_numicro_convert_stopbit(enum uart_config_stop_bits sb)
{
switch (sb) {
case UART_CFG_STOP_BITS_1:
return UART_STOP_BIT_1;
case UART_CFG_STOP_BITS_1_5:
return UART_STOP_BIT_1_5;
case UART_CFG_STOP_BITS_2:
return UART_STOP_BIT_2;
default:
return -ENOTSUP;
}
};
static inline int32_t uart_numicro_convert_datalen(enum uart_config_data_bits db)
{
switch (db) {
case UART_CFG_DATA_BITS_5:
return UART_WORD_LEN_5;
case UART_CFG_DATA_BITS_6:
return UART_WORD_LEN_6;
case UART_CFG_DATA_BITS_7:
return UART_WORD_LEN_7;
case UART_CFG_DATA_BITS_8:
return UART_WORD_LEN_8;
default:
return -ENOTSUP;
}
}
static inline uint32_t uart_numicro_convert_parity(enum uart_config_parity parity)
{
switch (parity) {
case UART_CFG_PARITY_ODD:
return UART_PARITY_ODD;
case UART_CFG_PARITY_EVEN:
return UART_PARITY_EVEN;
case UART_CFG_PARITY_MARK:
return UART_PARITY_MARK;
case UART_CFG_PARITY_SPACE:
return UART_PARITY_SPACE;
case UART_CFG_PARITY_NONE:
default:
return UART_PARITY_NONE;
}
}
static int uart_numicro_configure(const struct device *dev,
const struct uart_config *cfg)
{
struct uart_numicro_data *ddata = DRV_DATA(dev);
int32_t databits, stopbits;
uint32_t parity;
databits = uart_numicro_convert_datalen(cfg->data_bits);
if (databits < 0) {
return databits;
}
stopbits = uart_numicro_convert_stopbit(cfg->stop_bits);
if (stopbits < 0) {
return stopbits;
}
if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE) {
UART_DisableFlowCtrl(UART_STRUCT(dev));
} else if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS) {
UART_EnableFlowCtrl(UART_STRUCT(dev));
} else {
return -ENOTSUP;
}
parity = uart_numicro_convert_parity(cfg->parity);
UART_SetLineConfig(UART_STRUCT(dev), cfg->baudrate, databits,
parity, stopbits);
memcpy(&ddata->ucfg, cfg, sizeof(*cfg));
return 0;
}
static int uart_numicro_config_get(const struct device *dev,
struct uart_config *cfg)
{
struct uart_numicro_data *ddata = DRV_DATA(dev);
memcpy(cfg, &ddata->ucfg, sizeof(*cfg));
return 0;
}
static int uart_numicro_init(const struct device *dev)
{
const struct uart_numicro_config *config = DEV_CFG(dev);
struct uart_numicro_data *ddata = DRV_DATA(dev);
SYS_ResetModule(config->id_rst);
SYS_UnlockReg();
/* Enable UART module clock */
CLK_EnableModuleClock(config->id_clk);
/* Select UART0 clock source is PLL */
CLK_SetModuleClock(config->id_clk, CLK_CLKSEL1_UART0SEL_PLL,
CLK_CLKDIV0_UART0(0));
/* Set pinctrl for UART0 RXD and TXD */
SYS->GPB_MFPH &= ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk);
SYS->GPB_MFPH |= (SYS_GPB_MFPH_PB12MFP_UART0_RXD |
SYS_GPB_MFPH_PB13MFP_UART0_TXD);
SYS_LockReg();
UART_Open(UART_STRUCT(dev), ddata->ucfg.baudrate);
return 0;
}
static const struct uart_driver_api uart_numicro_driver_api = {
.poll_in = uart_numicro_poll_in,
.poll_out = uart_numicro_poll_out,
.err_check = uart_numicro_err_check,
.configure = uart_numicro_configure,
.config_get = uart_numicro_config_get,
};
#define NUMICRO_INIT(index) \
\
static const struct uart_numicro_config uart_numicro_cfg_##index = { \
.devcfg = { \
.base = (uint8_t *)DT_INST_REG_ADDR(index), \
}, \
.id_rst = UART##index##_RST, \
.id_clk = UART##index##_MODULE, \
}; \
\
static struct uart_numicro_data uart_numicro_data_##index = { \
.ucfg = { \
.baudrate = DT_INST_PROP(index, current_speed), \
}, \
}; \
\
DEVICE_DT_INST_DEFINE(index, \
&uart_numicro_init, \
device_pm_control_nop, \
&uart_numicro_data_##index, \
&uart_numicro_cfg_##index, \
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_numicro_driver_api);
DT_INST_FOREACH_STATUS_OKAY(NUMICRO_INIT)