381 lines
10 KiB
C
381 lines
10 KiB
C
/*
|
|
* Copyright (c) 2023 ENE Technology Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ene_kb1200_uart
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <reg/ser.h>
|
|
|
|
struct kb1200_uart_config {
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
void (*irq_cfg_func)(void);
|
|
#endif
|
|
struct serial_regs *ser;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
};
|
|
|
|
struct kb1200_uart_data {
|
|
uart_irq_callback_user_data_t callback;
|
|
struct uart_config current_config;
|
|
void *callback_data;
|
|
uint8_t pending_flag_data;
|
|
};
|
|
|
|
static int kb1200_uart_err_check(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
int err = 0;
|
|
|
|
if (config->ser->SERSTS & SERSTS_RX_OVERRUN) {
|
|
err |= UART_ERROR_OVERRUN;
|
|
}
|
|
if (config->ser->SERSTS & SERSTS_PARITY_ERROR) {
|
|
err |= UART_ERROR_PARITY;
|
|
}
|
|
if (config->ser->SERSTS & SERSTS_FRAME_ERROR) {
|
|
err |= UART_ERROR_FRAMING;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
|
static int kb1200_uart_configure(const struct device *dev, const struct uart_config *cfg)
|
|
{
|
|
uint16_t reg_baudrate = 0;
|
|
uint8_t reg_parity = 0;
|
|
int ret = 0;
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
reg_baudrate = (DIVIDER_BASE_CLK / cfg->baudrate) - 1;
|
|
|
|
switch (cfg->parity) {
|
|
case UART_CFG_PARITY_NONE:
|
|
reg_parity = SERCFG_PARITY_NONE;
|
|
break;
|
|
case UART_CFG_PARITY_ODD:
|
|
reg_parity = SERCFG_PARITY_ODD;
|
|
break;
|
|
case UART_CFG_PARITY_EVEN:
|
|
reg_parity = SERCFG_PARITY_EVEN;
|
|
break;
|
|
case UART_CFG_PARITY_MARK:
|
|
case UART_CFG_PARITY_SPACE:
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
switch (cfg->stop_bits) {
|
|
case UART_CFG_STOP_BITS_1:
|
|
break;
|
|
case UART_CFG_STOP_BITS_0_5:
|
|
case UART_CFG_STOP_BITS_1_5:
|
|
case UART_CFG_STOP_BITS_2:
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
switch (cfg->data_bits) {
|
|
case UART_CFG_DATA_BITS_8:
|
|
break;
|
|
case UART_CFG_DATA_BITS_5:
|
|
case UART_CFG_DATA_BITS_6:
|
|
case UART_CFG_DATA_BITS_7:
|
|
case UART_CFG_DATA_BITS_9:
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
switch (cfg->flow_ctrl) {
|
|
case UART_CFG_FLOW_CTRL_NONE:
|
|
break;
|
|
case UART_CFG_FLOW_CTRL_RTS_CTS:
|
|
case UART_CFG_FLOW_CTRL_DTR_DSR:
|
|
case UART_CFG_FLOW_CTRL_RS485:
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
config->ser->SERCFG =
|
|
(reg_baudrate << 16) | (reg_parity << 2) | (SERIE_RX_ENABLE | SERIE_TX_ENABLE);
|
|
config->ser->SERCTRL = SERCTRL_MODE1;
|
|
data->current_config = *cfg;
|
|
return ret;
|
|
}
|
|
|
|
static int kb1200_uart_config_get(const struct device *dev, struct uart_config *cfg)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
*cfg = data->current_config;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static int kb1200_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
uint16_t tx_bytes = 0U;
|
|
|
|
while ((size - tx_bytes) > 0) {
|
|
/* Check Tx FIFO not Full*/
|
|
while (config->ser->SERSTS & SERSTS_TX_FULL)
|
|
;
|
|
/* Put a character into Tx FIFO */
|
|
config->ser->SERTBUF = tx_data[tx_bytes];
|
|
tx_bytes++;
|
|
}
|
|
return tx_bytes;
|
|
}
|
|
|
|
static int kb1200_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
uint16_t rx_bytes = 0U;
|
|
|
|
/* Check Rx FIFO not Empty*/
|
|
while ((size - rx_bytes > 0) && (!(config->ser->SERSTS & SERSTS_RX_EMPTY))) {
|
|
/* Put a character into Tx FIFO */
|
|
rx_data[rx_bytes] = config->ser->SERRBUF;
|
|
rx_bytes++;
|
|
}
|
|
return rx_bytes;
|
|
}
|
|
|
|
static void kb1200_uart_irq_tx_enable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERPF = SERPF_TX_EMPTY;
|
|
config->ser->SERIE |= SERIE_TX_ENABLE;
|
|
}
|
|
|
|
static void kb1200_uart_irq_tx_disable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERIE &= ~SERIE_TX_ENABLE;
|
|
config->ser->SERPF = SERPF_TX_EMPTY;
|
|
}
|
|
|
|
static int kb1200_uart_irq_tx_ready(const struct device *dev)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
return (data->pending_flag_data & SERPF_TX_EMPTY) ? 1 : 0;
|
|
}
|
|
|
|
static void kb1200_uart_irq_rx_enable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERPF = SERPF_RX_CNT_FULL;
|
|
config->ser->SERIE |= SERIE_RX_ENABLE;
|
|
}
|
|
|
|
static void kb1200_uart_irq_rx_disable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERIE &= (~SERIE_RX_ENABLE);
|
|
config->ser->SERPF = SERPF_RX_CNT_FULL;
|
|
}
|
|
|
|
static int kb1200_uart_irq_rx_ready(const struct device *dev)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
return (data->pending_flag_data & SERPF_RX_CNT_FULL) ? 1 : 0;
|
|
}
|
|
|
|
static void kb1200_uart_irq_err_enable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERPF = SERPF_RX_ERROR;
|
|
config->ser->SERIE |= SERIE_RX_ERROR;
|
|
}
|
|
|
|
static void kb1200_uart_irq_err_disable(const struct device *dev)
|
|
{
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
config->ser->SERIE &= (~SERIE_RX_ERROR);
|
|
config->ser->SERPF = SERPF_RX_ERROR;
|
|
}
|
|
|
|
static int kb1200_uart_irq_is_pending(const struct device *dev)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
return (data->pending_flag_data) ? 1 : 0;
|
|
}
|
|
|
|
static int kb1200_uart_irq_update(const struct device *dev)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
data->pending_flag_data = (config->ser->SERPF) & (config->ser->SERIE);
|
|
/*clear pending flag*/
|
|
config->ser->SERPF = data->pending_flag_data;
|
|
return 1;
|
|
}
|
|
|
|
static void kb1200_uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
|
|
void *cb_data)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
data->callback = cb;
|
|
data->callback_data = cb_data;
|
|
}
|
|
|
|
static void kb1200_uart_irq_handler(const struct device *dev)
|
|
{
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
if (data->callback) {
|
|
data->callback(dev, data->callback_data);
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
static int kb1200_uart_poll_in(const struct device *dev, unsigned char *c)
|
|
{
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
return kb1200_uart_fifo_read(dev, c, 1) ? 0 : -1;
|
|
#else
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
/* Check Rx FIFO not Empty*/
|
|
if (config->ser->SERSTS & SERSTS_RX_EMPTY) {
|
|
return -1;
|
|
}
|
|
/* Put a character into Tx FIFO */
|
|
*c = config->ser->SERRBUF;
|
|
return 0;
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
}
|
|
|
|
static void kb1200_uart_poll_out(const struct device *dev, unsigned char c)
|
|
{
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
kb1200_uart_fifo_fill(dev, &c, 1);
|
|
#else
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
|
|
/* Wait Tx FIFO not Full*/
|
|
while (config->ser->SERSTS & SER_TxFull) {
|
|
;
|
|
}
|
|
/* Put a character into Tx FIFO */
|
|
config->ser->SERTBUF = c;
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
}
|
|
|
|
static const struct uart_driver_api kb1200_uart_api = {
|
|
.poll_in = kb1200_uart_poll_in,
|
|
.poll_out = kb1200_uart_poll_out,
|
|
.err_check = kb1200_uart_err_check,
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
|
.configure = kb1200_uart_configure,
|
|
.config_get = kb1200_uart_config_get,
|
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
.fifo_fill = kb1200_uart_fifo_fill,
|
|
.fifo_read = kb1200_uart_fifo_read,
|
|
.irq_tx_enable = kb1200_uart_irq_tx_enable,
|
|
.irq_tx_disable = kb1200_uart_irq_tx_disable,
|
|
.irq_tx_ready = kb1200_uart_irq_tx_ready,
|
|
.irq_rx_enable = kb1200_uart_irq_rx_enable,
|
|
.irq_rx_disable = kb1200_uart_irq_rx_disable,
|
|
.irq_rx_ready = kb1200_uart_irq_rx_ready,
|
|
.irq_err_enable = kb1200_uart_irq_err_enable,
|
|
.irq_err_disable = kb1200_uart_irq_err_disable,
|
|
.irq_is_pending = kb1200_uart_irq_is_pending,
|
|
.irq_update = kb1200_uart_irq_update,
|
|
.irq_callback_set = kb1200_uart_irq_callback_set,
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
};
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
|
|
/* GPIO module instances */
|
|
#define KB1200_UART_DEV(inst) DEVICE_DT_INST_GET(inst),
|
|
static const struct device *const uart_devices[] = {DT_INST_FOREACH_STATUS_OKAY(KB1200_UART_DEV)};
|
|
static void kb1200_uart_isr_wrap(const struct device *dev)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(uart_devices); i++) {
|
|
const struct device *dev_ = uart_devices[i];
|
|
const struct kb1200_uart_config *config = dev_->config;
|
|
|
|
if (config->ser->SERIE & config->ser->SERPF) {
|
|
kb1200_uart_irq_handler(dev_);
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
static int kb1200_uart_init(const struct device *dev)
|
|
{
|
|
int ret;
|
|
const struct kb1200_uart_config *config = dev->config;
|
|
struct kb1200_uart_data *data = dev->data;
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
kb1200_uart_configure(dev, &data->current_config);
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
config->irq_cfg_func();
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static bool init_irq = true;
|
|
static void kb1200_uart_irq_init(void)
|
|
{
|
|
if (init_irq) {
|
|
init_irq = false;
|
|
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), kb1200_uart_isr_wrap, NULL,
|
|
0);
|
|
irq_enable(DT_INST_IRQN(0));
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
#define KB1200_UART_INIT(n) \
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
|
static struct kb1200_uart_data kb1200_uart_data_##n = { \
|
|
.current_config = { \
|
|
.baudrate = DT_INST_PROP(n, current_speed), \
|
|
.parity = UART_CFG_PARITY_NONE, \
|
|
.stop_bits = UART_CFG_STOP_BITS_1, \
|
|
.data_bits = UART_CFG_DATA_BITS_8, \
|
|
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \
|
|
}, \
|
|
}; \
|
|
static const struct kb1200_uart_config kb1200_uart_config_##n = { \
|
|
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, (.irq_cfg_func = kb1200_uart_irq_init,)) \
|
|
.ser = (struct serial_regs *)DT_INST_REG_ADDR(n), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n)}; \
|
|
DEVICE_DT_INST_DEFINE(n, &kb1200_uart_init, NULL, &kb1200_uart_data_##n, \
|
|
&kb1200_uart_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
|
|
&kb1200_uart_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(KB1200_UART_INIT)
|