123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- /*
- * Copyright (c) 2006-2024, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2024-04-11 liYony the first version
- */
- #include <rthw.h>
- #include <rtdevice.h>
- #include <drv_uart.h>
- #include "board.h"
- #include "zynqmp_uart.h"
- #define ZYNQMP_UART_DEVICE_DEFAULT(base, irq, clk) {{ \
- .ops = &_zynqmp_ops, \
- .config = RT_SERIAL_CONFIG_DEFAULT \
- }, \
- .hw_base = base, \
- .irqno = irq, \
- .in_clk = clk \
- }
- struct zynqmp_uart_device
- {
- struct rt_serial_device device;
- rt_ubase_t hw_base;
- rt_uint32_t irqno;
- rt_uint32_t in_clk;
- };
- static void _uart_set_fifo_threshold(rt_ubase_t base, rt_uint8_t trigger_level)
- {
- rt_uint32_t reg_triger;
- /* Assert validates the input arguments */
- RT_ASSERT(base != RT_NULL);
- RT_ASSERT(trigger_level <= (rt_uint8_t)XUARTPS_RXWM_MASK);
- reg_triger = ((rt_uint32_t)trigger_level) & (rt_uint32_t)XUARTPS_RXWM_MASK;
- /*
- * Write the new value for the FIFO control register to it such that the
- * threshold is changed
- */
- writel(reg_triger, base + XUARTPS_RXWM_OFFSET);
- }
- static void _uart_set_interrupt_mask(rt_ubase_t base, rt_uint32_t mask)
- {
- rt_uint32_t temp_mask = mask;
- RT_ASSERT(base != RT_NULL);
- temp_mask &= (rt_uint32_t)XUARTPS_IXR_MASK;
- writel(temp_mask, base + XUARTPS_IER_OFFSET);
- writel((~temp_mask), base + XUARTPS_IDR_OFFSET);
- }
- static rt_err_t _uart_baudrate_init(rt_ubase_t base, struct serial_configure *cfg, rt_uint32_t in_clk)
- {
- rt_uint32_t iter_baud_div; /* Iterator for available baud divisor values */
- rt_uint32_t brgr_value; /* Calculated value for baud rate generator */
- rt_uint32_t calc_baudrate; /* Calculated baud rate */
- rt_uint32_t baud_error; /* Diff between calculated and requested baud rate */
- rt_uint32_t best_brgr = 0U; /* Best value for baud rate generator */
- rt_uint8_t best_baud_div = 0U; /* Best value for baud divisor */
- rt_uint32_t best_error = 0xFFFFFFFFU;
- rt_uint32_t percent_error;
- rt_uint32_t mode_reg;
- rt_uint32_t input_clk;
- rt_uint32_t temp_reg;
- /* Asserts validate the input arguments */
- RT_ASSERT(base != RT_NULL);
- RT_ASSERT(cfg->baud_rate <= (rt_uint32_t)XUARTPS_MAX_RATE);
- RT_ASSERT(cfg->baud_rate >= (rt_uint32_t)XUARTPS_MIN_RATE);
- /*
- * Make sure the baud rate is not impossilby large.
- * Fastest possible baud rate is Input Clock / 2.
- */
- if ((cfg->baud_rate * 2) > in_clk)
- {
- return -RT_EINVAL;
- }
- /* Check whether the input clock is divided by 8 */
- mode_reg = readl(base + XUARTPS_MR_OFFSET);
- input_clk = in_clk;
- if (mode_reg & XUARTPS_MR_CLKSEL)
- {
- input_clk = in_clk / 8;
- }
- /*
- * Determine the Baud divider. It can be 4to 254.
- * Loop through all possible combinations
- */
- for (iter_baud_div = 4; iter_baud_div < 255; iter_baud_div++)
- {
- /* Calculate the value for BRGR register */
- brgr_value = input_clk / (cfg->baud_rate * (iter_baud_div + 1));
- /* Calculate the baud rate from the BRGR value */
- calc_baudrate = input_clk / (brgr_value * (iter_baud_div + 1));
- /* Avoid unsigned integer underflow */
- if (cfg->baud_rate > calc_baudrate)
- {
- baud_error = cfg->baud_rate - calc_baudrate;
- }
- else
- {
- baud_error = calc_baudrate - cfg->baud_rate;
- }
- /* Find the calculated baud rate closest to requested baud rate. */
- if (best_error > baud_error)
- {
- best_brgr = brgr_value;
- best_baud_div = iter_baud_div;
- best_error = baud_error;
- }
- }
- /* Make sure the best error is not too large. */
- percent_error = (best_error * 100) / cfg->baud_rate;
- if (XUARTPS_MAX_BAUD_ERROR_RATE < percent_error)
- {
- return -RT_EINVAL;
- }
- /* Disable TX and RX to avoid glitches when setting the baud rate. */
- temp_reg = (((readl(base + XUARTPS_CR_OFFSET)) & ((rt_uint32_t)(~XUARTPS_CR_EN_DIS_MASK))) |
- ((rt_uint32_t)XUARTPS_CR_RX_DIS | (rt_uint32_t)XUARTPS_CR_TX_DIS));
- writel(temp_reg, base + XUARTPS_CR_OFFSET);
- /* Set the baud rate divisor */
- writel(best_brgr, base + XUARTPS_BAUDGEN_OFFSET);
- writel(best_baud_div, base + XUARTPS_BAUDDIV_OFFSET);
- /* RX and TX SW reset */
- writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, base + XUARTPS_CR_OFFSET);
- /* Enable device */
- temp_reg = (((readl(base + XUARTPS_CR_OFFSET)) & ((rt_uint32_t)(~XUARTPS_CR_EN_DIS_MASK))) |
- ((rt_uint32_t)XUARTPS_CR_RX_EN | (rt_uint32_t)XUARTPS_CR_TX_EN));
- writel(temp_reg, base + XUARTPS_CR_OFFSET);
- return RT_EOK;
- }
- static rt_err_t zynqmp_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
- {
- struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
- RT_ASSERT(uart != RT_NULL);
- if (_uart_baudrate_init(uart->hw_base, cfg, uart->in_clk) != RT_EOK)
- {
- return -RT_ERROR;
- }
- rt_uint32_t mode_reg = 0U;
- /* Set the parity mode */
- mode_reg = readl(uart->hw_base + XUARTPS_MR_OFFSET);
- /* Mask off what's already there */
- mode_reg &= (~((rt_uint32_t)XUARTPS_MR_CHARLEN_MASK |
- (rt_uint32_t)XUARTPS_MR_STOPMODE_MASK |
- (rt_uint32_t)XUARTPS_MR_PARITY_MASK));
- switch (cfg->data_bits)
- {
- case DATA_BITS_6:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_6_BIT;
- break;
- case DATA_BITS_7:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_7_BIT;
- break;
- case DATA_BITS_8:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_8_BIT;
- break;
- default:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_8_BIT;
- break;
- }
- switch (cfg->stop_bits)
- {
- case STOP_BITS_1:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_1_BIT;
- break;
- case STOP_BITS_2:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_2_BIT;
- break;
- default:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_1_BIT;
- break;
- }
- switch (cfg->parity)
- {
- case PARITY_NONE:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_NONE;
- break;
- case PARITY_ODD:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_ODD;
- break;
- case PARITY_EVEN:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_EVEN;
- break;
- default:
- mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_NONE;
- break;
- }
- /* Write the mode register out */
- writel(mode_reg, uart->hw_base + XUARTPS_MR_OFFSET);
- /* Set the RX FIFO trigger at 8 data bytes. */
- writel(0x08U, uart->hw_base + XUARTPS_RXWM_OFFSET);
- /* Set the RX timeout to 1, which will be 4 character time */
- writel(0x01U, uart->hw_base + XUARTPS_RXTOUT_OFFSET);
- /* Disable all interrupts, polled mode is the default */
- writel(XUARTPS_IXR_MASK, uart->hw_base + XUARTPS_IDR_OFFSET);
- return RT_EOK;
- }
- static rt_err_t zynqmp_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
- {
- struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
- RT_ASSERT(uart != RT_NULL);
- switch (cmd)
- {
- case RT_DEVICE_CTRL_CLR_INT:
- /* Disable the UART Interrupt */
- rt_hw_interrupt_mask(uart->irqno);
- _uart_set_interrupt_mask(uart->hw_base, 0U);
- break;
- case RT_DEVICE_CTRL_SET_INT:
- /* Enable the UART Interrupt */
- _uart_set_fifo_threshold(uart->hw_base, 1);
- rt_hw_interrupt_umask(uart->irqno);
- _uart_set_interrupt_mask(uart->hw_base, XUARTPS_IXR_RXOVR);
- break;
- }
- return RT_EOK;
- }
- static int zynqmp_uart_putc(struct rt_serial_device *serial, char c)
- {
- struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
- RT_ASSERT(uart != RT_NULL);
- /* Wait until there is space in TX FIFO */
- while ((readl(uart->hw_base + XUARTPS_SR_OFFSET) &
- XUARTPS_SR_TXFULL) == XUARTPS_SR_TXFULL)
- {
- ;
- }
- /* Write the byte into the TX FIFO */
- writel((rt_uint32_t)c, uart->hw_base + XUARTPS_FIFO_OFFSET);
- return 1;
- }
- static int zynqmp_uart_getc(struct rt_serial_device *serial)
- {
- struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
- RT_ASSERT(uart != RT_NULL);
- /* Wait until there is data */
- if ((readl(uart->hw_base + XUARTPS_SR_OFFSET) &
- XUARTPS_SR_RXEMPTY) == XUARTPS_SR_RXEMPTY)
- {
- return -1;
- }
- int ch = readl(uart->hw_base + XUARTPS_FIFO_OFFSET);
- return ch;
- }
- static const struct rt_uart_ops _zynqmp_ops =
- {
- zynqmp_uart_configure,
- zynqmp_uart_control,
- zynqmp_uart_putc,
- zynqmp_uart_getc,
- };
- #ifdef BSP_USING_UART0
- static struct zynqmp_uart_device _uart0_device =
- ZYNQMP_UART_DEVICE_DEFAULT(ZYNQMP_UART0_BASE, ZYNQMP_UART0_IRQNUM, ZYNQMP_UART0_CLK_FREQ_HZ);
- #endif
- static void rt_hw_uart_isr(int irqno, void *param)
- {
- struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)param;
- RT_ASSERT(uart != RT_NULL);
- struct rt_serial_device *serial = &(uart->device);
- rt_uint32_t isr_status;
- isr_status = readl(uart->hw_base + XUARTPS_IMR_OFFSET);
- isr_status &= readl(uart->hw_base + XUARTPS_ISR_OFFSET);
- if (isr_status & (rt_uint32_t)XUARTPS_IXR_RXOVR)
- {
- writel(XUARTPS_IXR_RXOVR, uart->hw_base + XUARTPS_ISR_OFFSET);
- rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
- }
- }
- int rt_hw_uart_init(void)
- {
- struct zynqmp_uart_device *uart = RT_NULL;
- #ifdef BSP_USING_UART0
- uart = &_uart0_device;
- _uart0_device.hw_base = (rt_size_t)rt_ioremap((void*)_uart0_device.hw_base, ZYNQMP_UART0_SIZE);
- /* register UART0 device */
- rt_hw_serial_register(&uart->device, "uart0",
- RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
- uart);
- rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, uart, "uart0");
- #endif
- return 0;
- }
|