1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2024-04-11 liYony the first version
9 */
10
11 #include <rthw.h>
12 #include <rtdevice.h>
13 #include <drv_uart.h>
14 #include "board.h"
15 #include "zynqmp_uart.h"
16
17 #define ZYNQMP_UART_DEVICE_DEFAULT(base, irq, clk) {{ \
18 .ops = &_zynqmp_ops, \
19 .config = RT_SERIAL_CONFIG_DEFAULT \
20 }, \
21 .hw_base = base, \
22 .irqno = irq, \
23 .in_clk = clk \
24 }
25
26 struct zynqmp_uart_device
27 {
28 struct rt_serial_device device;
29 rt_ubase_t hw_base;
30 rt_uint32_t irqno;
31 rt_uint32_t in_clk;
32 };
33
_uart_set_fifo_threshold(rt_ubase_t base,rt_uint8_t trigger_level)34 static void _uart_set_fifo_threshold(rt_ubase_t base, rt_uint8_t trigger_level)
35 {
36 rt_uint32_t reg_triger;
37
38 /* Assert validates the input arguments */
39 RT_ASSERT(base != RT_NULL);
40 RT_ASSERT(trigger_level <= (rt_uint8_t)XUARTPS_RXWM_MASK);
41
42 reg_triger = ((rt_uint32_t)trigger_level) & (rt_uint32_t)XUARTPS_RXWM_MASK;
43
44 /*
45 * Write the new value for the FIFO control register to it such that the
46 * threshold is changed
47 */
48 writel(reg_triger, base + XUARTPS_RXWM_OFFSET);
49 }
50
_uart_set_interrupt_mask(rt_ubase_t base,rt_uint32_t mask)51 static void _uart_set_interrupt_mask(rt_ubase_t base, rt_uint32_t mask)
52 {
53 rt_uint32_t temp_mask = mask;
54
55 RT_ASSERT(base != RT_NULL);
56
57 temp_mask &= (rt_uint32_t)XUARTPS_IXR_MASK;
58
59 writel(temp_mask, base + XUARTPS_IER_OFFSET);
60 writel((~temp_mask), base + XUARTPS_IDR_OFFSET);
61 }
62
_uart_baudrate_init(rt_ubase_t base,struct serial_configure * cfg,rt_uint32_t in_clk)63 static rt_err_t _uart_baudrate_init(rt_ubase_t base, struct serial_configure *cfg, rt_uint32_t in_clk)
64 {
65 rt_uint32_t iter_baud_div; /* Iterator for available baud divisor values */
66 rt_uint32_t brgr_value; /* Calculated value for baud rate generator */
67 rt_uint32_t calc_baudrate; /* Calculated baud rate */
68 rt_uint32_t baud_error; /* Diff between calculated and requested baud rate */
69 rt_uint32_t best_brgr = 0U; /* Best value for baud rate generator */
70 rt_uint8_t best_baud_div = 0U; /* Best value for baud divisor */
71 rt_uint32_t best_error = 0xFFFFFFFFU;
72 rt_uint32_t percent_error;
73 rt_uint32_t mode_reg;
74 rt_uint32_t input_clk;
75 rt_uint32_t temp_reg;
76
77 /* Asserts validate the input arguments */
78 RT_ASSERT(base != RT_NULL);
79 RT_ASSERT(cfg->baud_rate <= (rt_uint32_t)XUARTPS_MAX_RATE);
80 RT_ASSERT(cfg->baud_rate >= (rt_uint32_t)XUARTPS_MIN_RATE);
81
82 /*
83 * Make sure the baud rate is not impossilby large.
84 * Fastest possible baud rate is Input Clock / 2.
85 */
86 if ((cfg->baud_rate * 2) > in_clk)
87 {
88 return -RT_EINVAL;
89 }
90 /* Check whether the input clock is divided by 8 */
91 mode_reg = readl(base + XUARTPS_MR_OFFSET);
92
93 input_clk = in_clk;
94 if (mode_reg & XUARTPS_MR_CLKSEL)
95 {
96 input_clk = in_clk / 8;
97 }
98
99 /*
100 * Determine the Baud divider. It can be 4to 254.
101 * Loop through all possible combinations
102 */
103 for (iter_baud_div = 4; iter_baud_div < 255; iter_baud_div++)
104 {
105
106 /* Calculate the value for BRGR register */
107 brgr_value = input_clk / (cfg->baud_rate * (iter_baud_div + 1));
108
109 /* Calculate the baud rate from the BRGR value */
110 calc_baudrate = input_clk / (brgr_value * (iter_baud_div + 1));
111
112 /* Avoid unsigned integer underflow */
113 if (cfg->baud_rate > calc_baudrate)
114 {
115 baud_error = cfg->baud_rate - calc_baudrate;
116 }
117 else
118 {
119 baud_error = calc_baudrate - cfg->baud_rate;
120 }
121
122 /* Find the calculated baud rate closest to requested baud rate. */
123 if (best_error > baud_error)
124 {
125
126 best_brgr = brgr_value;
127 best_baud_div = iter_baud_div;
128 best_error = baud_error;
129 }
130 }
131
132 /* Make sure the best error is not too large. */
133 percent_error = (best_error * 100) / cfg->baud_rate;
134 if (XUARTPS_MAX_BAUD_ERROR_RATE < percent_error)
135 {
136 return -RT_EINVAL;
137 }
138
139 /* Disable TX and RX to avoid glitches when setting the baud rate. */
140 temp_reg = (((readl(base + XUARTPS_CR_OFFSET)) & ((rt_uint32_t)(~XUARTPS_CR_EN_DIS_MASK))) |
141 ((rt_uint32_t)XUARTPS_CR_RX_DIS | (rt_uint32_t)XUARTPS_CR_TX_DIS));
142 writel(temp_reg, base + XUARTPS_CR_OFFSET);
143
144 /* Set the baud rate divisor */
145 writel(best_brgr, base + XUARTPS_BAUDGEN_OFFSET);
146 writel(best_baud_div, base + XUARTPS_BAUDDIV_OFFSET);
147
148 /* RX and TX SW reset */
149 writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, base + XUARTPS_CR_OFFSET);
150
151 /* Enable device */
152 temp_reg = (((readl(base + XUARTPS_CR_OFFSET)) & ((rt_uint32_t)(~XUARTPS_CR_EN_DIS_MASK))) |
153 ((rt_uint32_t)XUARTPS_CR_RX_EN | (rt_uint32_t)XUARTPS_CR_TX_EN));
154 writel(temp_reg, base + XUARTPS_CR_OFFSET);
155
156 return RT_EOK;
157 }
158
zynqmp_uart_configure(struct rt_serial_device * serial,struct serial_configure * cfg)159 static rt_err_t zynqmp_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
160 {
161 struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
162
163 RT_ASSERT(uart != RT_NULL);
164
165 if (_uart_baudrate_init(uart->hw_base, cfg, uart->in_clk) != RT_EOK)
166 {
167 return -RT_ERROR;
168 }
169
170 rt_uint32_t mode_reg = 0U;
171
172 /* Set the parity mode */
173 mode_reg = readl(uart->hw_base + XUARTPS_MR_OFFSET);
174
175 /* Mask off what's already there */
176 mode_reg &= (~((rt_uint32_t)XUARTPS_MR_CHARLEN_MASK |
177 (rt_uint32_t)XUARTPS_MR_STOPMODE_MASK |
178 (rt_uint32_t)XUARTPS_MR_PARITY_MASK));
179
180 switch (cfg->data_bits)
181 {
182 case DATA_BITS_6:
183 mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_6_BIT;
184 break;
185 case DATA_BITS_7:
186 mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_7_BIT;
187 break;
188 case DATA_BITS_8:
189 mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_8_BIT;
190 break;
191 default:
192 mode_reg |= (rt_uint32_t)XUARTPS_MR_CHARLEN_8_BIT;
193 break;
194 }
195
196 switch (cfg->stop_bits)
197 {
198 case STOP_BITS_1:
199 mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_1_BIT;
200 break;
201 case STOP_BITS_2:
202 mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_2_BIT;
203 break;
204 default:
205 mode_reg |= (rt_uint32_t)XUARTPS_MR_STOPMODE_1_BIT;
206 break;
207 }
208
209 switch (cfg->parity)
210 {
211 case PARITY_NONE:
212 mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_NONE;
213 break;
214 case PARITY_ODD:
215 mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_ODD;
216 break;
217 case PARITY_EVEN:
218 mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_EVEN;
219 break;
220 default:
221 mode_reg |= (rt_uint32_t)XUARTPS_MR_PARITY_NONE;
222 break;
223 }
224
225 /* Write the mode register out */
226 writel(mode_reg, uart->hw_base + XUARTPS_MR_OFFSET);
227
228 /* Set the RX FIFO trigger at 8 data bytes. */
229 writel(0x08U, uart->hw_base + XUARTPS_RXWM_OFFSET);
230
231 /* Set the RX timeout to 1, which will be 4 character time */
232 writel(0x01U, uart->hw_base + XUARTPS_RXTOUT_OFFSET);
233
234 /* Disable all interrupts, polled mode is the default */
235 writel(XUARTPS_IXR_MASK, uart->hw_base + XUARTPS_IDR_OFFSET);
236
237 return RT_EOK;
238 }
239
zynqmp_uart_control(struct rt_serial_device * serial,int cmd,void * arg)240 static rt_err_t zynqmp_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
241 {
242 struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
243
244 RT_ASSERT(uart != RT_NULL);
245
246 switch (cmd)
247 {
248 case RT_DEVICE_CTRL_CLR_INT:
249 /* Disable the UART Interrupt */
250 rt_hw_interrupt_mask(uart->irqno);
251 _uart_set_interrupt_mask(uart->hw_base, 0U);
252 break;
253 case RT_DEVICE_CTRL_SET_INT:
254 /* Enable the UART Interrupt */
255 _uart_set_fifo_threshold(uart->hw_base, 1);
256 rt_hw_interrupt_umask(uart->irqno);
257 _uart_set_interrupt_mask(uart->hw_base, XUARTPS_IXR_RXOVR);
258 break;
259 }
260
261 return RT_EOK;
262 }
263
zynqmp_uart_putc(struct rt_serial_device * serial,char c)264 static int zynqmp_uart_putc(struct rt_serial_device *serial, char c)
265 {
266 struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
267
268 RT_ASSERT(uart != RT_NULL);
269
270 /* Wait until there is space in TX FIFO */
271 while ((readl(uart->hw_base + XUARTPS_SR_OFFSET) &
272 XUARTPS_SR_TXFULL) == XUARTPS_SR_TXFULL)
273 {
274 ;
275 }
276
277 /* Write the byte into the TX FIFO */
278 writel((rt_uint32_t)c, uart->hw_base + XUARTPS_FIFO_OFFSET);
279
280 return 1;
281 }
282
zynqmp_uart_getc(struct rt_serial_device * serial)283 static int zynqmp_uart_getc(struct rt_serial_device *serial)
284 {
285 struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)serial;
286
287 RT_ASSERT(uart != RT_NULL);
288
289 /* Wait until there is data */
290 if ((readl(uart->hw_base + XUARTPS_SR_OFFSET) &
291 XUARTPS_SR_RXEMPTY) == XUARTPS_SR_RXEMPTY)
292 {
293 return -1;
294 }
295
296 int ch = readl(uart->hw_base + XUARTPS_FIFO_OFFSET);
297
298 return ch;
299 }
300
301 static const struct rt_uart_ops _zynqmp_ops =
302 {
303 zynqmp_uart_configure,
304 zynqmp_uart_control,
305 zynqmp_uart_putc,
306 zynqmp_uart_getc,
307 };
308
309 #ifdef BSP_USING_UART0
310 static struct zynqmp_uart_device _uart0_device =
311 ZYNQMP_UART_DEVICE_DEFAULT(ZYNQMP_UART0_BASE, ZYNQMP_UART0_IRQNUM, ZYNQMP_UART0_CLK_FREQ_HZ);
312 #endif
313
rt_hw_uart_isr(int irqno,void * param)314 static void rt_hw_uart_isr(int irqno, void *param)
315 {
316 struct zynqmp_uart_device *uart = (struct zynqmp_uart_device *)param;
317
318 RT_ASSERT(uart != RT_NULL);
319 struct rt_serial_device *serial = &(uart->device);
320 rt_uint32_t isr_status;
321
322 isr_status = readl(uart->hw_base + XUARTPS_IMR_OFFSET);
323 isr_status &= readl(uart->hw_base + XUARTPS_ISR_OFFSET);
324 if (isr_status & (rt_uint32_t)XUARTPS_IXR_RXOVR)
325 {
326 writel(XUARTPS_IXR_RXOVR, uart->hw_base + XUARTPS_ISR_OFFSET);
327 rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
328 }
329 }
330
rt_hw_uart_init(void)331 int rt_hw_uart_init(void)
332 {
333 struct zynqmp_uart_device *uart = RT_NULL;
334
335 #ifdef BSP_USING_UART0
336 uart = &_uart0_device;
337 _uart0_device.hw_base = (rt_size_t)rt_ioremap((void*)_uart0_device.hw_base, ZYNQMP_UART0_SIZE);
338 /* register UART0 device */
339 rt_hw_serial_register(&uart->device, "uart0",
340 RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
341 uart);
342 rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, uart, "uart0");
343 #endif
344
345 return 0;
346 }
347