diff --git a/drivers/serial/Kconfig.mchp b/drivers/serial/Kconfig.mchp index f72b45ae0e5a3..729592498d310 100644 --- a/drivers/serial/Kconfig.mchp +++ b/drivers/serial/Kconfig.mchp @@ -6,6 +6,7 @@ config UART_MCHP_SERCOM_G1 default y depends on DT_HAS_MICROCHIP_SERCOM_G1_UART_ENABLED select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT select PINCTRL help This option enables UART driver for group (g1) of SERCOM peripherals. diff --git a/drivers/serial/uart_mchp_sercom_g1.c b/drivers/serial/uart_mchp_sercom_g1.c index 90f72e84d5ddb..424c50cef78dc 100644 --- a/drivers/serial/uart_mchp_sercom_g1.c +++ b/drivers/serial/uart_mchp_sercom_g1.c @@ -21,19 +21,60 @@ #include #include -/******************************************* - * Const and Macro Defines - ****************************************** - */ - -/* Define compatible string */ +/****************************************************************************** + * @brief Devicetree definitions + *****************************************************************************/ #define DT_DRV_COMPAT microchip_sercom_g1_uart +/****************************************************************************** + * @brief Macro definitions + *****************************************************************************/ #define UART_SUCCESS 0 #define BITSHIFT_FOR_BAUD_CALC 20 -/* Do the peripheral clock related configuration */ +/* Do the peripheral interrupt related configuration */ +#if CONFIG_UART_INTERRUPT_DRIVEN + +#if DT_INST_IRQ_HAS_IDX(0, 3) +/** + * @brief Configure UART IRQ handler for multiple interrupts. + * + * This macro sets up the IRQ handler for the UART peripheral when + * multiple interrupts are available. + * + * @param n Instance number. + */ +#define UART_MCHP_IRQ_HANDLER(n) \ + static void uart_mchp_irq_config_##n(const struct device *dev) \ + { \ + MCHP_UART_IRQ_CONNECT(n, 0); \ + MCHP_UART_IRQ_CONNECT(n, 1); \ + MCHP_UART_IRQ_CONNECT(n, 2); \ + MCHP_UART_IRQ_CONNECT(n, 3); \ + } +#else /* DT_INST_IRQ_HAS_IDX(0, 3) */ +/** + * @brief Configure UART IRQ handler for a single interrupt. + * + * This macro sets up the IRQ handler for the UART peripheral when + * only a single interrupt is available. + * + * @param n Instance number. + */ +#define UART_MCHP_IRQ_HANDLER(n) \ + static void uart_mchp_irq_config_##n(const struct device *dev) \ + { \ + MCHP_UART_IRQ_CONNECT(n, 0); \ + } +#endif /* DT_INST_IRQ_HAS_IDX(0, 3) */ + +#else /* CONFIG_UART_INTERRUPT_DRIVEN */ +#define UART_MCHP_IRQ_HANDLER(n) +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +/****************************************************************************** + * @brief Data type definitions + *****************************************************************************/ /** * @brief Clock configuration structure for the UART. * @@ -72,12 +113,20 @@ typedef struct uart_mchp_dev_cfg { /* Flag indicating if the clock is external. */ bool clock_external; + /* defines the functionality in standby sleep mode */ + uint8_t run_in_standby_en; + /* RX pinout configuration. */ uint32_t rxpo; /* TX pinout configuration. */ uint32_t txpo; +#if CONFIG_UART_INTERRUPT_DRIVEN + /* IRQ configuration function */ + void (*irq_config_func)(const struct device *dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + /* Clock configuration */ mchp_uart_clock_t uart_clock; @@ -92,8 +141,22 @@ typedef struct uart_mchp_dev_cfg { typedef struct uart_mchp_dev_data { /* Cached UART configuration */ struct uart_config config_cache; + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + /* IRQ callback function */ + uart_irq_callback_user_data_t cb; + + /* IRQ callback user data */ + void *cb_data; + + /* Cached status of TX completion */ + bool is_tx_completed_cache; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ } uart_mchp_dev_data_t; +/****************************************************************************** + * @brief Helper functions + *****************************************************************************/ /** * @brief Wait for synchronization of the UART. * @@ -467,6 +530,7 @@ static void uart_tx_on_off(sercom_registers_t *regs, bool clock_external, bool e * @param baudrate Desired baud rate. * @param clk_freq_hz Clock frequency in Hz. * @return 0 on success, -ERANGE if the calculated baud rate is out of range. + * @retval -EINVAL for invalid argument. */ static int uart_set_baudrate(sercom_registers_t *regs, bool clock_external, uint32_t baudrate, uint32_t clk_freq_hz) @@ -509,20 +573,26 @@ static int uart_set_baudrate(sercom_registers_t *regs, bool clock_external, uint * * @param regs Pointer to the sercom_registers_t structure. * @param clock_external Boolean to check external or internal clock + * @param run_in_standby Boolean to enable UART operation in standby mode. * @param enable Boolean to enable or disable the UART. */ -static void uart_enable(sercom_registers_t *regs, bool clock_external, bool enable) +static void uart_enable(sercom_registers_t *regs, bool clock_external, bool run_in_standby, + bool enable) { if (clock_external == false) { if (enable == true) { - regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_RUNSTDBY_Msk; + if (run_in_standby == true) { + regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_RUNSTDBY_Msk; + } regs->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE_Msk; } else { regs->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_ENABLE_Msk; } } else { if (enable == true) { - regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_RUNSTDBY_Msk; + if (run_in_standby == true) { + regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_RUNSTDBY_Msk; + } regs->USART_EXT.SERCOM_CTRLA |= SERCOM_USART_EXT_CTRLA_ENABLE_Msk; } else { regs->USART_EXT.SERCOM_CTRLA &= ~SERCOM_USART_EXT_CTRLA_ENABLE_Msk; @@ -599,6 +669,28 @@ static inline bool uart_is_tx_ready(sercom_registers_t *regs, bool clock_externa return retval; } +/** + * @brief Check if the UART TX is complete. + * + * This function checks if the TX operation is complete for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if TX is complete, false otherwise. + */ +static inline bool uart_is_tx_complete(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_TXC_Msk) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_TXC_Msk) != 0); + } + + return retval; +} + /** * @brief Transmit a character via UART. * @@ -617,6 +709,348 @@ static inline void uart_tx_char(sercom_registers_t *regs, bool clock_external, u } } +/** + * @brief Check if there is a buffer overflow error. + * + * This function checks if there is a buffer overflow error for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if there is a buffer overflow error, false otherwise. + */ +static inline bool uart_is_err_buffer_overflow(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = + ((regs->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_BUFOVF_Msk) != 0); + } else { + retval = + ((regs->USART_EXT.SERCOM_STATUS & SERCOM_USART_EXT_STATUS_BUFOVF_Msk) != 0); + } + + return retval; +} + +/** + * @brief Check if there is a frame error. + * + * This function checks if there is a frame error for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if there is a frame error, false otherwise. + */ +static inline bool uart_is_err_frame(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_FERR_Msk) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_STATUS & SERCOM_USART_EXT_STATUS_FERR_Msk) != 0); + } + + return retval; +} + +/** + * @brief Check if there is a parity error. + * + * This function checks if there is a parity error for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if there is a parity error, false otherwise. + */ +static inline bool uart_is_err_parity(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_PERR_Msk) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_STATUS & SERCOM_USART_EXT_STATUS_PERR_Msk) != 0); + } + + return retval; +} + +/** + * @brief Check if there is an autobaud synchronization error. + * + * This function checks if there is an autobaud synchronization error for the specified UART + * instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if there is an autobaud synchronization error, false otherwise. + */ +static inline bool uart_is_err_autobaud_sync(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_ISF_Msk) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_STATUS & SERCOM_USART_EXT_STATUS_ISF_Msk) != 0); + } + + return retval; +} + +/** + * @brief Check if there is a collision error. + * + * This function checks if there is a collision error for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if there is a collision error, false otherwise. + */ +static inline bool uart_is_err_collision(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_COLL_Msk) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_STATUS & SERCOM_USART_EXT_STATUS_COLL_Msk) != 0); + } + + return retval; +} + +/** + * @brief Clear all UART error flags. + * + * This function clears all error flags for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + */ +static inline void uart_err_clear_all(sercom_registers_t *regs, bool clock_external) +{ + if (clock_external == false) { + regs->USART_INT.SERCOM_STATUS |= + SERCOM_USART_INT_STATUS_BUFOVF_Msk | SERCOM_USART_INT_STATUS_FERR_Msk | + SERCOM_USART_INT_STATUS_PERR_Msk | SERCOM_USART_INT_STATUS_ISF_Msk | + SERCOM_USART_INT_STATUS_COLL_Msk; + } else { + regs->USART_EXT.SERCOM_STATUS |= + SERCOM_USART_EXT_STATUS_BUFOVF_Msk | SERCOM_USART_EXT_STATUS_FERR_Msk | + SERCOM_USART_EXT_STATUS_PERR_Msk | SERCOM_USART_EXT_STATUS_ISF_Msk | + SERCOM_USART_EXT_STATUS_COLL_Msk; + } +} + +/** + * @brief See if any error present. + * + * This function check for error flags for the specified UART instance. + * + * @param dev Pointer to the UART device. + */ +static uint32_t uart_get_err(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + uint32_t err = 0U; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + + if (uart_is_err_buffer_overflow(regs, clock_external) == true) { + err |= UART_ERROR_OVERRUN; + } + + if (uart_is_err_frame(regs, clock_external) == true) { + err |= UART_ERROR_FRAMING; + } + + if (uart_is_err_parity(regs, clock_external) == true) { + err |= UART_ERROR_PARITY; + } + + if (uart_is_err_autobaud_sync(regs, clock_external) == true) { + err |= UART_BREAK; + } + + if (uart_is_err_collision(regs, clock_external) == true) { + err |= UART_ERROR_COLLISION; + } + + return err; +} + +#if CONFIG_UART_INTERRUPT_DRIVEN + +/** + * @brief Check if the UART transmit interrupt is enabled. + * + * This function checks if transmit interrupt is enabled for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if interrupt is enabled, false otherwise. + */ +static inline bool uart_is_tx_interrupt_enabled(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_INTENSET & SERCOM_USART_INT_INTENSET_DRE_Msk) != + 0); + } else { + retval = ((regs->USART_EXT.SERCOM_INTENSET & SERCOM_USART_EXT_INTENSET_DRE_Msk) != + 0); + } + + return retval; +} + +/** + * @brief Check if any UART interrupt is pending. + * + * This function checks if any interrupt is pending for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @return True if any interrupt is pending, false otherwise. + */ +static inline bool uart_is_interrupt_pending(sercom_registers_t *regs, bool clock_external) +{ + bool retval; + + if (clock_external == false) { + retval = ((regs->USART_INT.SERCOM_INTENSET & regs->USART_INT.SERCOM_INTFLAG) != 0); + } else { + retval = ((regs->USART_EXT.SERCOM_INTENSET & regs->USART_EXT.SERCOM_INTFLAG) != 0); + } + + return retval; +} + +/** + * @brief Enable or disable the UART RX interrupt. + * + * This function enables or disables the RX interrupt for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @param enable True to enable the interrupt, false to disable. + */ +static void uart_enable_rx_interrupt(sercom_registers_t *regs, bool clock_external, bool enable) +{ + if (clock_external == false) { + if (enable == true) { + regs->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_RXC_Msk; + } else { + regs->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_RXC_Msk; + } + } else { + if (enable == true) { + regs->USART_EXT.SERCOM_INTENSET = SERCOM_USART_EXT_INTENSET_RXC_Msk; + } else { + regs->USART_EXT.SERCOM_INTENCLR = SERCOM_USART_EXT_INTENCLR_RXC_Msk; + } + } +} + +/** + * @brief Enable or disable the UART TX complete interrupt. + * + * This function enables or disables the TX complete interrupt for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @param enable True to enable the interrupt, false to disable. + */ +static void uart_enable_tx_complete_interrupt(sercom_registers_t *regs, bool clock_external, + bool enable) +{ + if (clock_external == false) { + if (enable == true) { + regs->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_TXC_Msk; + } else { + regs->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_TXC_Msk; + } + } else { + if (enable == true) { + regs->USART_EXT.SERCOM_INTENSET = SERCOM_USART_EXT_INTENSET_TXC_Msk; + } else { + regs->USART_EXT.SERCOM_INTENCLR = SERCOM_USART_EXT_INTENCLR_TXC_Msk; + } + } +} + +/** + * @brief Enable or disable the UART error interrupt. + * + * This function enables or disables the error interrupt for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @param enable True to enable the interrupt, false to disable. + */ +static void uart_enable_err_interrupt(sercom_registers_t *regs, bool clock_external, bool enable) +{ + if (clock_external == false) { + if (enable == true) { + regs->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_ERROR_Msk; + } else { + regs->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_ERROR_Msk; + } + } else { + if (enable == true) { + regs->USART_EXT.SERCOM_INTENSET = SERCOM_USART_EXT_INTENSET_ERROR_Msk; + } else { + regs->USART_EXT.SERCOM_INTENCLR = SERCOM_USART_EXT_INTENCLR_ERROR_Msk; + } + } +} + +/** + * @brief Clear all UART interrupts. + * + * This function clears all interrupts for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + */ +static void uart_clear_interrupts(sercom_registers_t *regs, bool clock_external) +{ + if (clock_external == false) { + regs->USART_INT.SERCOM_INTFLAG = + SERCOM_USART_INT_INTFLAG_ERROR_Msk | SERCOM_USART_INT_INTFLAG_RXBRK_Msk | + SERCOM_USART_INT_INTFLAG_CTSIC_Msk | SERCOM_USART_INT_INTFLAG_RXS_Msk | + SERCOM_USART_INT_INTFLAG_TXC_Msk; + } else { + regs->USART_EXT.SERCOM_INTFLAG = + SERCOM_USART_EXT_INTFLAG_ERROR_Msk | SERCOM_USART_EXT_INTFLAG_RXBRK_Msk | + SERCOM_USART_EXT_INTFLAG_CTSIC_Msk | SERCOM_USART_EXT_INTFLAG_RXS_Msk | + SERCOM_USART_EXT_INTFLAG_TXC_Msk; + } +} + +/** + * @brief UART ISR handler. + * + * @param dev Device structure. + */ +static void uart_mchp_isr(const struct device *dev) +{ + uart_mchp_dev_data_t *const dev_data = dev->data; + + if (dev_data->cb != NULL) { + dev_data->cb(dev, dev_data->cb_data); + } +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +/****************************************************************************** + * @brief API functions + *****************************************************************************/ /** * @brief Initialize the UART device. * @@ -687,12 +1121,109 @@ static int uart_mchp_init(const struct device *dev) } dev_data->config_cache.baudrate = cfg->baudrate; - uart_enable(regs, clock_external, true); +#if CONFIG_UART_INTERRUPT_DRIVEN + cfg->irq_config_func(dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + + uart_enable(regs, clock_external, (cfg->run_in_standby_en == 1) ? true : false, + true); + } while (0); + + return retval; +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + +/** + * @brief Configure the UART device. + * + * @param dev Device structure. + * @param new_cfg New UART configuration. + * @return 0 on success, negative error code on failure. + */ +static int uart_mchp_configure(const struct device *dev, const struct uart_config *new_cfg) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + uart_mchp_dev_data_t *const dev_data = dev->data; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + int retval = UART_SUCCESS; + + do { + /* Forcefully disable UART before configuring. run_in_standby is ignored here */ + uart_enable(regs, clock_external, false, false); + + if (new_cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + /* Flow control not yet supported though in principle possible + * on this soc family. + */ + retval = -ENOTSUP; + break; + } + dev_data->config_cache.flow_ctrl = new_cfg->flow_ctrl; + + switch (new_cfg->parity) { + case UART_CFG_PARITY_NONE: + case UART_CFG_PARITY_ODD: + case UART_CFG_PARITY_EVEN: + uart_config_parity(regs, clock_external, new_cfg->parity); + break; + default: + retval = -ENOTSUP; + } + if (retval != UART_SUCCESS) { + break; + } + dev_data->config_cache.parity = new_cfg->parity; + + retval = uart_config_stop_bits(regs, clock_external, new_cfg->stop_bits); + if (retval != UART_SUCCESS) { + break; + } + dev_data->config_cache.stop_bits = new_cfg->stop_bits; + + retval = uart_config_data_bits(regs, clock_external, new_cfg->data_bits); + if (retval != UART_SUCCESS) { + break; + } + dev_data->config_cache.data_bits = new_cfg->data_bits; + + uint32_t clock_rate = 0; + + clock_control_get_rate(cfg->uart_clock.clock_dev, cfg->uart_clock.gclk_sys, + &clock_rate); + + retval = uart_set_baudrate(regs, clock_external, new_cfg->baudrate, clock_rate); + if (retval != UART_SUCCESS) { + break; + } + dev_data->config_cache.baudrate = new_cfg->baudrate; + + uart_enable(regs, clock_external, (cfg->run_in_standby_en == 1) ? true : false, + true); } while (0); return retval; } +/** + * @brief Get the current UART configuration. + * + * @param dev Device structure. + * @param out_cfg Output configuration structure. + * @return 0 on success. + */ +static int uart_mchp_config_get(const struct device *dev, struct uart_config *out_cfg) +{ + uart_mchp_dev_data_t *const dev_data = dev->data; + + memcpy(out_cfg, &(dev_data->config_cache), sizeof(dev_data->config_cache)); + + return 0; +} + +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + /** * @brief Poll the UART device for input. * @@ -735,11 +1266,379 @@ static void uart_mchp_poll_out(const struct device *dev, unsigned char data) uart_tx_char(regs, clock_external, data); } +/** + * @brief Check for UART errors. + * + * @param dev Device structure. + * @return Error code. + */ +static int uart_mchp_err_check(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + + uint32_t err = uart_get_err(dev); + + /* Clear all errors */ + uart_err_clear_all(regs, clock_external); + + return err; +} + +#if CONFIG_UART_INTERRUPT_DRIVEN + +/** + * @brief Enable or disable the UART TX ready interrupt. + * + * This function enables or disables the TX ready interrupt for the specified UART instance. + * + * @param regs Pointer to the sercom_registers_t structure. + * @param clock_external Boolean to check external or internal clock + * @param enable True to enable the interrupt, false to disable. + */ +static void uart_enable_tx_ready_interrupt(sercom_registers_t *regs, bool clock_external, + bool enable) +{ + if (clock_external == false) { + if (enable == true) { + regs->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_DRE_Msk; + } else { + regs->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_DRE_Msk; + } + } else { + if (enable == true) { + regs->USART_EXT.SERCOM_INTENSET = SERCOM_USART_EXT_INTENSET_DRE_Msk; + } else { + regs->USART_EXT.SERCOM_INTENCLR = SERCOM_USART_EXT_INTENCLR_DRE_Msk; + } + } +} +/** + * @brief Enable UART TX interrupt. + * + * This function enables the UART TX ready and TX complete interrupts. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_tx_enable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + unsigned int key = irq_lock(); + + uart_enable_tx_ready_interrupt(regs, clock_external, true); + uart_enable_tx_complete_interrupt(regs, clock_external, true); + irq_unlock(key); +} + +/** + * @brief Fill the UART FIFO with data. + * + * This function fills the UART FIFO with data from the provided buffer. + * + * @param dev Pointer to the device structure. + * @param tx_data Pointer to the data buffer to be transmitted. + * @param len Length of the data buffer. + * @return Number of bytes written to the FIFO. + */ +static int uart_mchp_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + int retval = 0; + + if ((uart_is_tx_ready(regs, clock_external) == true) && (len >= 1)) { + uart_tx_char(regs, clock_external, tx_data[0]); /* Transmit the first character */ + retval = 1; + } + + return retval; +} + +/** + * @brief Disable UART TX interrupt. + * + * This function disables the UART TX ready and TX complete interrupts. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_tx_disable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + + uart_enable_tx_ready_interrupt(regs, clock_external, false); + uart_enable_tx_complete_interrupt(regs, clock_external, false); +} + +/** + * @brief Check if UART TX is ready. + * + * This function checks if the UART TX is ready to transmit data. + * + * @param dev Pointer to the device structure. + * @return 1 if TX is ready, 0 otherwise. + */ +static int uart_mchp_irq_tx_ready(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + + return (uart_is_tx_ready(regs, clock_external) && + uart_is_tx_interrupt_enabled(regs, clock_external)); +} + +/** + * @brief Check if UART TX is complete. + * + * This function checks if the UART TX has completed transmission. + * + * @param dev Pointer to the device structure. + * @return 1 if TX is complete, 0 otherwise. + */ +static int uart_mchp_irq_tx_complete(const struct device *dev) +{ + uart_mchp_dev_data_t *const dev_data = dev->data; + int retval = 0; + + if (dev_data->is_tx_completed_cache == true) { + retval = 1; + } + + return retval; +} + +/** + * @brief Enable UART RX interrupt. + * + * This function enables the UART RX interrupt. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_rx_enable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + + uart_enable_rx_interrupt(cfg->regs, cfg->clock_external, true); +} + +/** + * @brief Disable UART RX interrupt. + * + * This function disables the UART RX interrupt. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_rx_disable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + + uart_enable_rx_interrupt(cfg->regs, cfg->clock_external, false); +} + +/** + * @brief Check if UART RX is ready. + * + * This function checks if the UART RX has received data. + * + * @param dev Pointer to the device structure. + * @return 1 if RX is ready, 0 otherwise. + */ +static int uart_mchp_irq_rx_ready(const struct device *dev) +{ + int retval = 0; + const uart_mchp_dev_cfg_t *const cfg = dev->config; + + if (uart_is_rx_complete(cfg->regs, cfg->clock_external) == true) { + retval = 1; + } + + return retval; +} + +/** + * @brief Read data from UART FIFO. + * + * This function reads data from the UART FIFO into the provided buffer. + * + * @param dev Pointer to the device structure. + * @param rx_data Pointer to the buffer to store received data. + * @param size Size of the buffer. + * @return Number of bytes read from the FIFO. + * @retval -EINVAL for invalid argument. + */ +static int uart_mchp_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + int retval = 0; + + if (uart_is_rx_complete(regs, clock_external) == true) { + uint8_t ch = uart_get_received_char( + /* Get the received character */ + regs, clock_external); + + if (size >= 1) { + /* Store the received character in the buffer */ + *rx_data = ch; + retval = 1; + } else { + retval = -EINVAL; + } + } + + return retval; +} + +/** + * @brief Check if UART interrupt is pending. + * + * This function checks if there is any pending UART interrupt. + * + * @param dev Pointer to the device structure. + * @return 1 if an interrupt is pending, 0 otherwise. + */ +static int uart_mchp_irq_is_pending(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + int retval = 0; + + if (uart_is_interrupt_pending(cfg->regs, cfg->clock_external) == true) { + retval = 1; + } + + return retval; +} + +/** + * @brief Enable UART error interrupt. + * + * This function enables the UART error interrupt. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_err_enable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + + uart_enable_err_interrupt(cfg->regs, cfg->clock_external, true); +} + +/** + * @brief Disable UART error interrupt. + * + * This function disables the UART error interrupt. + * + * @param dev Pointer to the device structure. + */ +static void uart_mchp_irq_err_disable(const struct device *dev) +{ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + + uart_enable_err_interrupt(cfg->regs, cfg->clock_external, false); +} + +/** + * @brief Update UART interrupt status. + * + * This function clears sticky interrupts and updates the TX complete cache. + * + * @param dev Pointer to the device structure. + * @return Always returns 1. + */ +static int uart_mchp_irq_update(const struct device *dev) +{ + /* Clear sticky interrupts */ + const uart_mchp_dev_cfg_t *const cfg = dev->config; + uart_mchp_dev_data_t *const dev_data = dev->data; + sercom_registers_t *regs = cfg->regs; + bool clock_external = cfg->clock_external; + + /* + * Cache the TXC flag, and use this cached value to clear the interrupt + * if we do not use the cached value, there is a chance TXC will set + * after caching...this will cause TXC to never be cached. + */ + dev_data->is_tx_completed_cache = uart_is_tx_complete(regs, clock_external); + uart_clear_interrupts(regs, clock_external); + + return 1; +} + +/** + * @brief Set UART interrupt callback. + * + * This function sets the callback function for UART interrupts. + * + * @param dev Pointer to the device structure. + * @param cb Callback function. + * @param cb_data User data to be passed to the callback function. + */ +static void uart_mchp_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *cb_data) +{ + uart_mchp_dev_data_t *const dev_data = dev->data; + + dev_data->cb = cb; + dev_data->cb_data = cb_data; +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +/****************************************************************************** + * @brief Zephyr driver instance creation + *****************************************************************************/ static DEVICE_API(uart, uart_mchp_driver_api) = { +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = uart_mchp_configure, + .config_get = uart_mchp_config_get, +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + .poll_in = uart_mchp_poll_in, .poll_out = uart_mchp_poll_out, + .err_check = uart_mchp_err_check, + +#if CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_mchp_fifo_fill, + .fifo_read = uart_mchp_fifo_read, + .irq_tx_enable = uart_mchp_irq_tx_enable, + .irq_tx_disable = uart_mchp_irq_tx_disable, + .irq_tx_ready = uart_mchp_irq_tx_ready, + .irq_tx_complete = uart_mchp_irq_tx_complete, + .irq_rx_enable = uart_mchp_irq_rx_enable, + .irq_rx_disable = uart_mchp_irq_rx_disable, + .irq_rx_ready = uart_mchp_irq_rx_ready, + .irq_is_pending = uart_mchp_irq_is_pending, + .irq_err_enable = uart_mchp_irq_err_enable, + .irq_err_disable = uart_mchp_irq_err_disable, + .irq_update = uart_mchp_irq_update, + .irq_callback_set = uart_mchp_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; +#if CONFIG_UART_INTERRUPT_DRIVEN + +#define MCHP_UART_IRQ_CONNECT(n, m) \ + do { \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, m, irq), DT_INST_IRQ_BY_IDX(n, m, priority), \ + uart_mchp_isr, DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(n, m, irq)); \ + } while (false) + +#define UART_MCHP_IRQ_HANDLER_DECL(n) static void uart_mchp_irq_config_##n(const struct device *dev) +#define UART_MCHP_IRQ_HANDLER_FUNC(n) .irq_config_func = uart_mchp_irq_config_##n, + +#else /* CONFIG_UART_INTERRUPT_DRIVEN */ +#define UART_MCHP_IRQ_HANDLER_DECL(n) +#define UART_MCHP_IRQ_HANDLER_FUNC(n) +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + #define UART_MCHP_CONFIG_DEFN(n) \ static const uart_mchp_dev_cfg_t uart_mchp_config_##n = { \ .baudrate = DT_INST_PROP(n, current_speed), \ @@ -751,13 +1650,16 @@ static DEVICE_API(uart, uart_mchp_driver_api) = { .rxpo = (DT_INST_PROP(n, rxpo)), \ .txpo = (DT_INST_PROP(n, txpo)), \ .clock_external = DT_INST_PROP(n, clock_external), \ - UART_MCHP_CLOCK_DEFN(n)} + .run_in_standby_en = DT_INST_PROP(n, run_in_standby_en), \ + UART_MCHP_IRQ_HANDLER_FUNC(n) UART_MCHP_CLOCK_DEFN(n)} #define UART_MCHP_DEVICE_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ + UART_MCHP_IRQ_HANDLER_DECL(n); \ UART_MCHP_CONFIG_DEFN(n); \ static uart_mchp_dev_data_t uart_mchp_data_##n; \ DEVICE_DT_INST_DEFINE(n, uart_mchp_init, NULL, &uart_mchp_data_##n, &uart_mchp_config_##n, \ - PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_mchp_driver_api) + PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_mchp_driver_api); \ + UART_MCHP_IRQ_HANDLER(n) DT_INST_FOREACH_STATUS_OKAY(UART_MCHP_DEVICE_INIT) diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n19a.dtsi index 74bee592b56d8..bb0eeb4743fd5 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n20a.dtsi index 75c8389a1bf0b..4cd68eddb6e0a 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51n20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p19a.dtsi index 74bee592b56d8..c8f18bcbf9ccf 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p20a.dtsi index 75c8389a1bf0b..90c069cf1cb67 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsamd51/atsamd51p20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n19a.dtsi index 74bee592b56d8..bb0eeb4743fd5 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n20a.dtsi index 75c8389a1bf0b..4cd68eddb6e0a 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame51/atsame51n20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n19a.dtsi index 74bee592b56d8..bb0eeb4743fd5 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n20a.dtsi index 75c8389a1bf0b..4cd68eddb6e0a 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame53/atsame53n20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n19a.dtsi index 74bee592b56d8..bb0eeb4743fd5 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n20a.dtsi index 75c8389a1bf0b..4cd68eddb6e0a 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54n20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p19a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p19a.dtsi index 74bee592b56d8..c8f18bcbf9ccf 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p19a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p19a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p20a.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p20a.dtsi index 75c8389a1bf0b..90c069cf1cb67 100644 --- a/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p20a.dtsi +++ b/dts/arm/microchip/sam/sam_d5x_e5x/atsame54/atsame54p20a.dtsi @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_n.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_n.dtsi new file mode 100644 index 0000000000000..208a4cec49da4 --- /dev/null +++ b/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_n.dtsi @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + soc { + sercom6: sercom@43000800 { + compatible = "microchip,sercom-g1"; + status = "disabled"; + reg = <0x43000800 0x31>; + interrupts = <70 0>, <71 0>, <72 0>, <73 0>; + clocks = <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_APBD_SERCOM6>, + <&gclkperiph CLOCK_MCHP_GCLKPERIPH_ID_SERCOM6_CORE>; + clock-names = "mclk", "gclk"; + }; + + sercom7: sercom@43000c00 { + compatible = "microchip,sercom-g1-i2c"; + status = "disabled"; + reg = <0x43000c00 0x31>; + interrupts = <74 0>, <75 0>, <76 0>, <77 0>; + clocks = <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_APBD_SERCOM7>, + <&gclkperiph CLOCK_MCHP_GCLKPERIPH_ID_SERCOM7_CORE>; + clock-names = "mclk", "gclk"; + }; + }; +}; diff --git a/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_p.dtsi b/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_p.dtsi new file mode 100644 index 0000000000000..208a4cec49da4 --- /dev/null +++ b/dts/arm/microchip/sam/sam_d5x_e5x/common/samd5xe5x_p.dtsi @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + soc { + sercom6: sercom@43000800 { + compatible = "microchip,sercom-g1"; + status = "disabled"; + reg = <0x43000800 0x31>; + interrupts = <70 0>, <71 0>, <72 0>, <73 0>; + clocks = <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_APBD_SERCOM6>, + <&gclkperiph CLOCK_MCHP_GCLKPERIPH_ID_SERCOM6_CORE>; + clock-names = "mclk", "gclk"; + }; + + sercom7: sercom@43000c00 { + compatible = "microchip,sercom-g1-i2c"; + status = "disabled"; + reg = <0x43000c00 0x31>; + interrupts = <74 0>, <75 0>, <76 0>, <77 0>; + clocks = <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_APBD_SERCOM7>, + <&gclkperiph CLOCK_MCHP_GCLKPERIPH_ID_SERCOM7_CORE>; + clock-names = "mclk", "gclk"; + }; + }; +}; diff --git a/dts/bindings/serial/microchip,sercom-g1-uart.yaml b/dts/bindings/serial/microchip,sercom-g1-uart.yaml index d0d1697b9f315..932e12eff4a8c 100644 --- a/dts/bindings/serial/microchip,sercom-g1-uart.yaml +++ b/dts/bindings/serial/microchip,sercom-g1-uart.yaml @@ -78,3 +78,18 @@ properties: type: boolean description: | Enable collision detection for half-duplex mode. + + run-in-standby-en: + type: int + enum: + - 0 + - 1 + default: 1 + description: | + This property defines the functionality in standby sleep mode. + + 0: Generic clock is disabled when ongoing transfer is finished. The device + will not wake up on Transfer Complete interrupt unless the appropriate + ONDEMAND bits are set in the clocking chain. + 1: Wake on Receive Complete interrupt. Generic clock is enabled in all sleep modes. + Any interrupt can wake up the device.