drivers: serial: Add interrupt-driven API support for RZ/G3S
This commit is to support UART interrupt-driven API for Renesas RZ/G3S. Signed-off-by: Nhut Nguyen <nhut.nguyen.kc@renesas.com>
This commit is contained in:
parent
2615ec879e
commit
5a65c861a7
2 changed files with 257 additions and 26 deletions
|
|
@ -6,6 +6,7 @@ config UART_RENESAS_RZ_SCIF
|
|||
default y
|
||||
depends on DT_HAS_RENESAS_RZ_SCIF_UART_ENABLED
|
||||
select SERIAL_HAS_DRIVER
|
||||
select SERIAL_SUPPORT_INTERRUPT
|
||||
select USE_RZ_FSP_SCIF_UART
|
||||
select PINCTRL
|
||||
help
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include "r_scif_uart.h"
|
||||
|
||||
LOG_MODULE_REGISTER(rz_scif_uart);
|
||||
|
|
@ -17,12 +18,36 @@ struct uart_rz_scif_config {
|
|||
const uart_api_t *fsp_api;
|
||||
};
|
||||
|
||||
struct uart_rz_scif_int {
|
||||
bool rxi_flag;
|
||||
bool tei_flag;
|
||||
bool rx_fifo_busy;
|
||||
bool irq_rx_enable;
|
||||
bool irq_tx_enable;
|
||||
uint8_t rx_byte;
|
||||
uint8_t tx_byte;
|
||||
uart_event_t event;
|
||||
};
|
||||
|
||||
struct uart_rz_scif_data {
|
||||
struct uart_config uart_config;
|
||||
uart_cfg_t *fsp_cfg;
|
||||
struct uart_rz_scif_int int_data;
|
||||
scif_uart_instance_ctrl_t *fsp_ctrl;
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
uart_irq_callback_user_data_t callback;
|
||||
void *callback_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
void scif_uart_rxi_isr(void);
|
||||
void scif_uart_txi_isr(void);
|
||||
void scif_uart_tei_isr(void);
|
||||
void scif_uart_eri_isr(void);
|
||||
void scif_uart_bri_isr(void);
|
||||
#endif
|
||||
|
||||
static int uart_rz_scif_poll_in(const struct device *dev, unsigned char *c)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
|
@ -41,6 +66,9 @@ static void uart_rz_scif_poll_out(const struct device *dev, unsigned char c)
|
|||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg;
|
||||
uint8_t key;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
while (!reg->FSR_b.TDFE) {
|
||||
}
|
||||
|
|
@ -49,28 +77,27 @@ static void uart_rz_scif_poll_out(const struct device *dev, unsigned char c)
|
|||
|
||||
while (!reg->FSR_b.TEND) {
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
static int uart_rz_scif_err_check(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg;
|
||||
uart_event_t event = data->int_data.event;
|
||||
int err = 0;
|
||||
|
||||
const uint32_t fsr = reg->FSR;
|
||||
const uint32_t lsr = reg->LSR;
|
||||
int errors = 0;
|
||||
|
||||
if ((lsr & R_SCIFA0_LSR_ORER_Msk) != 0) {
|
||||
errors |= UART_ERROR_OVERRUN;
|
||||
if (event & UART_EVENT_ERR_OVERFLOW) {
|
||||
err |= UART_ERROR_OVERRUN;
|
||||
}
|
||||
if ((fsr & R_SCIFA0_FSR_PER_Msk) != 0) {
|
||||
errors |= UART_ERROR_PARITY;
|
||||
if (event & UART_EVENT_ERR_FRAMING) {
|
||||
err |= UART_ERROR_FRAMING;
|
||||
}
|
||||
if ((fsr & R_SCIFA0_FSR_FER_Pos) != 0) {
|
||||
errors |= UART_ERROR_FRAMING;
|
||||
if (event & UART_EVENT_ERR_PARITY) {
|
||||
err |= UART_ERROR_PARITY;
|
||||
}
|
||||
|
||||
return errors;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uart_rz_scif_apply_config(const struct device *dev)
|
||||
|
|
@ -179,11 +206,6 @@ static int uart_rz_scif_configure(const struct device *dev, const struct uart_co
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg;
|
||||
/* Temporarily disable the DRI interrupt caused by receive data ready */
|
||||
/* TODO: support interrupt-driven api */
|
||||
reg->SCR_b.RIE = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +219,187 @@ static int uart_rz_scif_config_get(const struct device *dev, struct uart_config
|
|||
|
||||
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
|
||||
static int uart_rz_scif_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
scif_uart_instance_ctrl_t *fsp_ctrl = data->fsp_ctrl;
|
||||
|
||||
fsp_ctrl->tx_src_bytes = size;
|
||||
fsp_ctrl->p_tx_src = tx_data;
|
||||
|
||||
scif_uart_txi_isr();
|
||||
|
||||
return (size - fsp_ctrl->tx_src_bytes);
|
||||
}
|
||||
|
||||
static int uart_rz_scif_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
scif_uart_instance_ctrl_t *fsp_ctrl = data->fsp_ctrl;
|
||||
|
||||
fsp_ctrl->rx_dest_bytes = size;
|
||||
fsp_ctrl->p_rx_dest = rx_data;
|
||||
|
||||
/* Read all available data in the FIFO */
|
||||
/* If there are more available data than required, they will be lost */
|
||||
if (data->int_data.rxi_flag) {
|
||||
scif_uart_rxi_isr();
|
||||
} else {
|
||||
scif_uart_tei_isr();
|
||||
}
|
||||
|
||||
data->int_data.rx_fifo_busy = false;
|
||||
|
||||
return (size - fsp_ctrl->rx_dest_bytes);
|
||||
}
|
||||
|
||||
static void uart_rz_scif_irq_rx_enable(const struct device *dev)
|
||||
{
|
||||
const struct uart_rz_scif_config *config = dev->config;
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.irq_rx_enable = true;
|
||||
|
||||
/* Prepare 1-byte buffer to receive, it will be overwritten by fifo read */
|
||||
config->fsp_api->read(data->fsp_ctrl, &(data->int_data.rx_byte), 1);
|
||||
}
|
||||
|
||||
static void uart_rz_scif_irq_rx_disable(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.irq_rx_enable = false;
|
||||
data->int_data.rx_fifo_busy = false;
|
||||
}
|
||||
|
||||
static void uart_rz_scif_irq_tx_enable(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
const struct uart_rz_scif_config *config = dev->config;
|
||||
|
||||
data->int_data.irq_tx_enable = true;
|
||||
|
||||
/* Trigger TX with a NULL frame */
|
||||
/* It is expected not to be sent, and will be overwritten by the fifo fill */
|
||||
data->int_data.tx_byte = '\0';
|
||||
config->fsp_api->write(data->fsp_ctrl, &data->int_data.tx_byte, 1);
|
||||
}
|
||||
|
||||
static void uart_rz_scif_irq_tx_disable(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.irq_tx_enable = false;
|
||||
}
|
||||
|
||||
static int uart_rz_scif_irq_tx_ready(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
return data->int_data.irq_tx_enable;
|
||||
}
|
||||
|
||||
static int uart_rz_scif_irq_rx_ready(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
return data->int_data.rx_fifo_busy && data->int_data.irq_rx_enable;
|
||||
}
|
||||
|
||||
static int uart_rz_scif_irq_is_pending(const struct device *dev)
|
||||
{
|
||||
return (uart_rz_scif_irq_tx_ready(dev)) || (uart_rz_scif_irq_rx_ready(dev));
|
||||
}
|
||||
|
||||
static void uart_rz_scif_irq_callback_set(const struct device *dev,
|
||||
uart_irq_callback_user_data_t cb, void *cb_data)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->callback = cb;
|
||||
data->callback_data = cb_data;
|
||||
}
|
||||
|
||||
static int uart_rz_scif_irq_update(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void uart_rz_scif_rxi_isr(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.rxi_flag = true;
|
||||
data->int_data.rx_fifo_busy = true;
|
||||
if (data->callback) {
|
||||
data->callback(dev, data->callback_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_rz_scif_txi_isr(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.tei_flag = false;
|
||||
if (data->callback) {
|
||||
data->callback(dev, data->callback_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_rz_scif_tei_isr(const struct device *dev)
|
||||
{
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
if (data->int_data.tei_flag) {
|
||||
scif_uart_tei_isr();
|
||||
} else {
|
||||
data->int_data.rxi_flag = false;
|
||||
data->int_data.rx_fifo_busy = true;
|
||||
if (data->callback) {
|
||||
data->callback(dev, data->callback_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_rz_scif_eri_isr(const struct device *dev)
|
||||
{
|
||||
scif_uart_eri_isr();
|
||||
}
|
||||
|
||||
static void uart_rz_scif_bri_isr(const struct device *dev)
|
||||
{
|
||||
scif_uart_bri_isr();
|
||||
}
|
||||
|
||||
static void uart_rz_scif_event_handler(uart_callback_args_t *p_args)
|
||||
{
|
||||
const struct device *dev = (const struct device *)p_args->p_context;
|
||||
struct uart_rz_scif_data *data = dev->data;
|
||||
|
||||
data->int_data.event = p_args->event;
|
||||
switch (p_args->event) {
|
||||
case UART_EVENT_RX_CHAR:
|
||||
data->int_data.rx_byte = p_args->data;
|
||||
break;
|
||||
case UART_EVENT_RX_COMPLETE:
|
||||
break;
|
||||
case UART_EVENT_TX_DATA_EMPTY:
|
||||
data->int_data.tei_flag = true;
|
||||
break;
|
||||
case UART_EVENT_TX_COMPLETE:
|
||||
data->int_data.tei_flag = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
static DEVICE_API(uart, uart_rz_scif_driver_api) = {
|
||||
.poll_in = uart_rz_scif_poll_in,
|
||||
.poll_out = uart_rz_scif_poll_out,
|
||||
|
|
@ -205,6 +408,19 @@ static DEVICE_API(uart, uart_rz_scif_driver_api) = {
|
|||
.configure = uart_rz_scif_configure,
|
||||
.config_get = uart_rz_scif_config_get,
|
||||
#endif
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.fifo_fill = uart_rz_scif_fifo_fill,
|
||||
.fifo_read = uart_rz_scif_fifo_read,
|
||||
.irq_rx_enable = uart_rz_scif_irq_rx_enable,
|
||||
.irq_rx_disable = uart_rz_scif_irq_rx_disable,
|
||||
.irq_tx_enable = uart_rz_scif_irq_tx_enable,
|
||||
.irq_tx_disable = uart_rz_scif_irq_tx_disable,
|
||||
.irq_tx_ready = uart_rz_scif_irq_tx_ready,
|
||||
.irq_rx_ready = uart_rz_scif_irq_rx_ready,
|
||||
.irq_is_pending = uart_rz_scif_irq_is_pending,
|
||||
.irq_callback_set = uart_rz_scif_irq_callback_set,
|
||||
.irq_update = uart_rz_scif_irq_update,
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
};
|
||||
|
||||
static int uart_rz_scif_init(const struct device *dev)
|
||||
|
|
@ -227,14 +443,24 @@ static int uart_rz_scif_init(const struct device *dev)
|
|||
|
||||
config->fsp_api->open(data->fsp_ctrl, data->fsp_cfg);
|
||||
|
||||
R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg;
|
||||
/* Temporarily disable the DRI interrupt caused by receive data ready */
|
||||
/* TODO: support interrupt-driven api */
|
||||
reg->SCR_b.RIE = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define UART_RZG_IRQ_CONNECT(n, irq_name, isr) \
|
||||
do { \
|
||||
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, irq_name, irq), \
|
||||
DT_INST_IRQ_BY_NAME(n, irq_name, priority), isr, \
|
||||
DEVICE_DT_INST_GET(n), 0); \
|
||||
irq_enable(DT_INST_IRQ_BY_NAME(n, irq_name, irq)); \
|
||||
} while (0)
|
||||
|
||||
#define UART_RZG_CONFIG_FUNC(n) \
|
||||
UART_RZG_IRQ_CONNECT(n, eri, uart_rz_scif_eri_isr); \
|
||||
UART_RZG_IRQ_CONNECT(n, rxi, uart_rz_scif_rxi_isr); \
|
||||
UART_RZG_IRQ_CONNECT(n, txi, uart_rz_scif_txi_isr); \
|
||||
UART_RZG_IRQ_CONNECT(n, tei, uart_rz_scif_tei_isr); \
|
||||
UART_RZG_IRQ_CONNECT(n, bri, uart_rz_scif_bri_isr);
|
||||
|
||||
#define UART_RZG_INIT(n) \
|
||||
static scif_uart_instance_ctrl_t g_uart##n##_ctrl; \
|
||||
static scif_baud_setting_t g_uart##n##_baud_setting; \
|
||||
|
|
@ -258,8 +484,6 @@ static int uart_rz_scif_init(const struct device *dev)
|
|||
}; \
|
||||
static uart_cfg_t g_uart##n##_cfg = { \
|
||||
.channel = DT_INST_PROP(n, channel), \
|
||||
.p_callback = NULL, \
|
||||
.p_context = NULL, \
|
||||
.p_extend = &g_uart##n##_cfg_extend, \
|
||||
.p_transfer_tx = NULL, \
|
||||
.p_transfer_rx = NULL, \
|
||||
|
|
@ -271,10 +495,14 @@ static int uart_rz_scif_init(const struct device *dev)
|
|||
.txi_irq = DT_INST_IRQ_BY_NAME(n, txi, irq), \
|
||||
.tei_irq = DT_INST_IRQ_BY_NAME(n, tei, irq), \
|
||||
.eri_irq = DT_INST_IRQ_BY_NAME(n, eri, irq), \
|
||||
}; \
|
||||
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, ( \
|
||||
.p_callback = uart_rz_scif_event_handler, \
|
||||
.p_context = (void *)DEVICE_DT_INST_GET(n),)) }; \
|
||||
PINCTRL_DT_INST_DEFINE(n); \
|
||||
static const struct uart_rz_scif_config uart_rz_scif_config_##n = { \
|
||||
.pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n), .fsp_api = &g_uart_on_scif}; \
|
||||
.pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
.fsp_api = &g_uart_on_scif, \
|
||||
}; \
|
||||
static struct uart_rz_scif_data uart_rz_scif_data_##n = { \
|
||||
.uart_config = \
|
||||
{ \
|
||||
|
|
@ -290,6 +518,8 @@ static int uart_rz_scif_init(const struct device *dev)
|
|||
}; \
|
||||
static int uart_rz_scif_init_##n(const struct device *dev) \
|
||||
{ \
|
||||
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \
|
||||
(UART_RZG_CONFIG_FUNC(n);)) \
|
||||
return uart_rz_scif_init(dev); \
|
||||
} \
|
||||
DEVICE_DT_INST_DEFINE(n, &uart_rz_scif_init_##n, NULL, &uart_rz_scif_data_##n, \
|
||||
|
|
|
|||
Loading…
Reference in a new issue