From 2bd130112aa9549f1b8a55792d93e95ab55d10b5 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Fri, 11 Sep 2020 00:27:26 -0300 Subject: [PATCH] drivers: serial: psoc6: Add interrupts support Current Cypress PSoC-6 serial driver only works using polling mode. Add serial driver interrupt routines to allow use of interrupts. Signed-off-by: Gerson Fernando Budke --- drivers/serial/Kconfig.psoc6 | 1 + drivers/serial/uart_psoc6.c | 214 ++++++++++++++++++++++++++++++++++- 2 files changed, 214 insertions(+), 1 deletion(-) diff --git a/drivers/serial/Kconfig.psoc6 b/drivers/serial/Kconfig.psoc6 index b865409537f..7f1e2456041 100644 --- a/drivers/serial/Kconfig.psoc6 +++ b/drivers/serial/Kconfig.psoc6 @@ -8,5 +8,6 @@ config UART_PSOC6 bool "PSoC-6 MCU SCB serial driver" depends on SOC_FAMILY_PSOC6 select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT help This option enables the SCB[UART] driver for PSoC-6 SoC family. diff --git a/drivers/serial/uart_psoc6.c b/drivers/serial/uart_psoc6.c index 0340f38be62..5ad44ada6cf 100644 --- a/drivers/serial/uart_psoc6.c +++ b/drivers/serial/uart_psoc6.c @@ -22,6 +22,7 @@ #include "cy_syslib.h" #include "cy_sysclk.h" #include "cy_scb_uart.h" +#include "cy_sysint.h" /* UART desired baud rate is 115200 bps (Standard mode). * The UART baud rate = (SCB clock frequency / Oversample). @@ -46,10 +47,24 @@ struct cypress_psoc6_config { CySCB_Type *base; uint32_t periph_id; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_config_func_t irq_config_func; +#endif uint32_t num_pins; struct soc_gpio_pin pins[]; }; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +struct cypress_psoc6_data { + uart_irq_callback_user_data_t irq_cb; /* Interrupt Callback */ + void *irq_cb_data; /* Interrupt Callback Arg */ +}; + +#define DEV_DATA(dev) \ + ((struct cypress_psoc6_data *const)(dev)->data) + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + /* Populate configuration structure */ static const cy_stc_scb_uart_config_t uartConfig = { .uartMode = CY_SCB_UART_STANDARD, @@ -111,6 +126,10 @@ static int uart_psoc6_init(const struct device *dev) (void) Cy_SCB_UART_Init(config->base, &uartConfig, NULL); Cy_SCB_UART_Enable(config->base); +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + config->irq_config_func(dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + return 0; } @@ -133,21 +152,214 @@ static void uart_psoc6_poll_out(const struct device *dev, unsigned char c) } } +static int uart_psoc6_err_check(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t status = Cy_SCB_UART_GetRxFifoStatus(config->base); + int errors = 0; + + if (status & CY_SCB_UART_RX_OVERFLOW) { + errors |= UART_ERROR_OVERRUN; + } + + if (status & CY_SCB_UART_RX_ERR_PARITY) { + errors |= UART_ERROR_PARITY; + } + + if (status & CY_SCB_UART_RX_ERR_FRAME) { + errors |= UART_ERROR_FRAMING; + } + + return errors; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +static int uart_psoc6_fifo_fill(const struct device *dev, + const uint8_t *tx_data, + int size) +{ + const struct cypress_psoc6_config *config = dev->config; + + return Cy_SCB_UART_PutArray(config->base, (uint8_t *) tx_data, size); +} + +static int uart_psoc6_fifo_read(const struct device *dev, + uint8_t *rx_data, + const int size) +{ + const struct cypress_psoc6_config *config = dev->config; + + return Cy_SCB_UART_GetArray(config->base, rx_data, size); +} + +static void uart_psoc6_irq_tx_enable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + + Cy_SCB_SetTxInterruptMask(config->base, CY_SCB_UART_TX_EMPTY); +} + +static void uart_psoc6_irq_tx_disable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + + Cy_SCB_SetTxInterruptMask(config->base, 0); +} + +static int uart_psoc6_irq_tx_ready(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t status = Cy_SCB_UART_GetTxFifoStatus(config->base); + + Cy_SCB_UART_ClearTxFifoStatus(config->base, CY_SCB_UART_TX_INTR_MASK); + + return (status & CY_SCB_UART_TX_NOT_FULL); +} + +static int uart_psoc6_irq_tx_complete(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t status = Cy_SCB_UART_GetTxFifoStatus(config->base); + + Cy_SCB_UART_ClearTxFifoStatus(config->base, CY_SCB_UART_TX_INTR_MASK); + + return (status & CY_SCB_UART_TX_DONE); +} + +static void uart_psoc6_irq_rx_enable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + + Cy_SCB_SetRxInterruptMask(config->base, CY_SCB_UART_RX_NOT_EMPTY); +} + +static void uart_psoc6_irq_rx_disable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + + Cy_SCB_SetRxInterruptMask(config->base, 0); +} + +static int uart_psoc6_irq_rx_ready(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t status = Cy_SCB_UART_GetRxFifoStatus(config->base); + + Cy_SCB_UART_ClearRxFifoStatus(config->base, CY_SCB_UART_RX_INTR_MASK); + + return (status & CY_SCB_UART_RX_NOT_EMPTY); +} + +static void uart_psoc6_irq_err_enable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t intmask = Cy_SCB_GetRxInterruptMask(config->base) | + CY_SCB_UART_RECEIVE_ERR; + + Cy_SCB_SetRxInterruptMask(config->base, intmask); +} + +static void uart_psoc6_irq_err_disable(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t intmask = Cy_SCB_GetRxInterruptMask(config->base) & + ~(CY_SCB_UART_RECEIVE_ERR); + + Cy_SCB_SetRxInterruptMask(config->base, intmask); +} + +static int uart_psoc6_irq_is_pending(const struct device *dev) +{ + const struct cypress_psoc6_config *config = dev->config; + uint32_t intcause = Cy_SCB_GetInterruptCause(config->base); + + return (intcause & (CY_SCB_TX_INTR | CY_SCB_RX_INTR)); +} + +static int uart_psoc6_irq_update(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 1; +} + +static void uart_psoc6_irq_callback_set(const struct device *dev, + uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct cypress_psoc6_data *const dev_data = DEV_DATA(dev); + + dev_data->irq_cb = cb; + dev_data->irq_cb_data = cb_data; +} + +static void uart_psoc6_isr(const struct device *dev) +{ + struct cypress_psoc6_data *const dev_data = DEV_DATA(dev); + + if (dev_data->irq_cb) { + dev_data->irq_cb(dev, dev_data->irq_cb_data); + } +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + static const struct uart_driver_api uart_psoc6_driver_api = { .poll_in = uart_psoc6_poll_in, .poll_out = uart_psoc6_poll_out, + .err_check = uart_psoc6_err_check, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_psoc6_fifo_fill, + .fifo_read = uart_psoc6_fifo_read, + .irq_tx_enable = uart_psoc6_irq_tx_enable, + .irq_tx_disable = uart_psoc6_irq_tx_disable, + .irq_tx_ready = uart_psoc6_irq_tx_ready, + .irq_rx_enable = uart_psoc6_irq_rx_enable, + .irq_rx_disable = uart_psoc6_irq_rx_disable, + .irq_tx_complete = uart_psoc6_irq_tx_complete, + .irq_rx_ready = uart_psoc6_irq_rx_ready, + .irq_err_enable = uart_psoc6_irq_err_enable, + .irq_err_disable = uart_psoc6_irq_err_disable, + .irq_is_pending = uart_psoc6_irq_is_pending, + .irq_update = uart_psoc6_irq_update, + .irq_callback_set = uart_psoc6_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +#define CY_PSOC6_UART_IRQ_FUNC(n) \ + static void cy_psoc6_uart##n##_irq_config(const struct device *port) \ + { \ + CY_PSOC6_DT_INST_NVIC_INSTALL(n, \ + uart_psoc6_isr); \ + }; +#define CY_PSOC6_UART_IRQ_SET_FUNC(n) \ + .irq_config_func = cy_psoc6_uart##n##_irq_config +#define CY_PSOC6_UART_DECL_DATA(n) \ + static struct cypress_psoc6_data cy_psoc6_uart##n##_data = { 0 }; +#define CY_PSOC6_UART_DECL_DATA_PTR(n) &cy_psoc6_uart##n##_data +#else +#define CY_PSOC6_UART_IRQ_FUNC(n) +#define CY_PSOC6_UART_IRQ_SET_FUNC(n) +#define CY_PSOC6_UART_DECL_DATA(n) +#define CY_PSOC6_UART_DECL_DATA_PTR(n) NULL +#endif + #define CY_PSOC6_UART_INIT(n) \ + CY_PSOC6_UART_DECL_DATA(n) \ + CY_PSOC6_UART_IRQ_FUNC(n) \ static const struct cypress_psoc6_config cy_psoc6_uart##n##_config = { \ .base = (CySCB_Type *)DT_INST_REG_ADDR(n), \ .periph_id = DT_INST_PROP(n, peripheral_id), \ \ .num_pins = CY_PSOC6_DT_INST_NUM_PINS(n), \ .pins = CY_PSOC6_DT_INST_PINS(n), \ + \ + CY_PSOC6_UART_IRQ_SET_FUNC(n) \ }; \ DEVICE_DT_INST_DEFINE(n, &uart_psoc6_init, device_pm_control_nop, \ - NULL, \ + CY_PSOC6_UART_DECL_DATA_PTR(n), \ &cy_psoc6_uart##n##_config, PRE_KERNEL_1, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &uart_psoc6_driver_api);