1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
4 #include <metal/machine/platform.h>
6 #ifdef METAL_SIFIVE_UART0
8 #include <metal/drivers/sifive_uart0.h>
9 #include <metal/machine.h>
12 #define UART_TXEN (1 << 0)
13 #define UART_TXFULL (1 << 31)
16 #define UART_RXEN (1 << 0)
17 #define UART_RXEMPTY (1 << 31)
20 #define UART_NSTOP (1 << 1)
21 #define UART_TXCNT(count) ((0x7 & count) << 16)
24 #define UART_TXWM (1 << 0)
26 #define UART_REG(offset) (((unsigned long)control_base + offset))
27 #define UART_REGB(offset) (__METAL_ACCESS_ONCE((__metal_io_u8 *)UART_REG(offset)))
28 #define UART_REGW(offset) (__METAL_ACCESS_ONCE((__metal_io_u32 *)UART_REG(offset)))
30 struct metal_interrupt *
31 __metal_driver_sifive_uart0_interrupt_controller(struct metal_uart *uart)
33 return __metal_driver_sifive_uart0_interrupt_parent(uart);
36 int __metal_driver_sifive_uart0_get_interrupt_id(struct metal_uart *uart)
38 return (__metal_driver_sifive_uart0_interrupt_line(uart) + METAL_INTERRUPT_ID_GL0);
41 int __metal_driver_sifive_uart0_putc(struct metal_uart *uart, unsigned char c)
43 long control_base = __metal_driver_sifive_uart0_control_base(uart);
45 while ((UART_REGW(METAL_SIFIVE_UART0_TXDATA) & UART_TXFULL) != 0) { }
46 UART_REGW(METAL_SIFIVE_UART0_TXDATA) = c;
50 int __metal_driver_sifive_uart0_getc(struct metal_uart *uart, unsigned char *c)
52 uint32_t ch = UART_RXEMPTY;
53 long control_base = __metal_driver_sifive_uart0_control_base(uart);
55 while (ch & UART_RXEMPTY) {
56 ch = UART_REGW(METAL_SIFIVE_UART0_RXDATA);
62 int __metal_driver_sifive_uart0_get_baud_rate(struct metal_uart *guart)
64 struct __metal_driver_sifive_uart0 *uart = (void *)guart;
65 return uart->baud_rate;
68 int __metal_driver_sifive_uart0_set_baud_rate(struct metal_uart *guart, int baud_rate)
70 struct __metal_driver_sifive_uart0 *uart = (void *)guart;
71 long control_base = __metal_driver_sifive_uart0_control_base(guart);
72 struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart);
74 uart->baud_rate = baud_rate;
77 long clock_rate = clock->vtable->get_rate_hz(clock);
78 UART_REGW(METAL_SIFIVE_UART0_DIV) = clock_rate / baud_rate - 1;
79 UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXEN;
80 UART_REGW(METAL_SIFIVE_UART0_RXCTRL) |= UART_RXEN;
85 static void pre_rate_change_callback(void *priv)
87 struct __metal_driver_sifive_uart0 *uart = priv;
88 long control_base = __metal_driver_sifive_uart0_control_base((struct metal_uart *)priv);
89 struct metal_clock *clock = __metal_driver_sifive_uart0_clock((struct metal_uart *)priv);
91 /* Detect when the TXDATA is empty by setting the transmit watermark count
92 * to one and waiting until an interrupt is pending */
94 UART_REGW(METAL_SIFIVE_UART0_TXCTRL) &= ~(UART_TXCNT(0x7));
95 UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXCNT(1);
97 while((UART_REGW(METAL_SIFIVE_UART0_IP) & UART_TXWM) == 0) ;
99 /* When the TXDATA clears, the UART is still shifting out the last byte.
100 * Calculate the time we must drain to finish transmitting and then wait
103 long bits_per_symbol = (UART_REGW(METAL_SIFIVE_UART0_TXCTRL) & (1 << 1)) ? 9 : 10;
104 long clk_freq = clock->vtable->get_rate_hz(clock);
105 long cycles_to_wait = bits_per_symbol * clk_freq / uart->baud_rate;
107 for(volatile long x = 0; x < cycles_to_wait; x++)
111 static void post_rate_change_callback(void *priv)
113 struct __metal_driver_sifive_uart0 *uart = priv;
114 metal_uart_set_baud_rate(&uart->uart, uart->baud_rate);
117 void __metal_driver_sifive_uart0_init(struct metal_uart *guart, int baud_rate)
119 struct __metal_driver_sifive_uart0 *uart = (void *)(guart);
120 struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart);
121 struct __metal_driver_sifive_gpio0 *pinmux = __metal_driver_sifive_uart0_pinmux(guart);
124 metal_clock_register_pre_rate_change_callback(clock, &pre_rate_change_callback, guart);
125 metal_clock_register_post_rate_change_callback(clock, &post_rate_change_callback, guart);
128 metal_uart_set_baud_rate(&(uart->uart), baud_rate);
130 if (pinmux != NULL) {
131 long pinmux_output_selector = __metal_driver_sifive_uart0_pinmux_output_selector(guart);
132 long pinmux_source_selector = __metal_driver_sifive_uart0_pinmux_source_selector(guart);
133 pinmux->gpio.vtable->enable_io(
134 (struct metal_gpio *) pinmux,
135 pinmux_output_selector,
136 pinmux_source_selector
141 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_uart0) = {
142 .uart.init = __metal_driver_sifive_uart0_init,
143 .uart.putc = __metal_driver_sifive_uart0_putc,
144 .uart.getc = __metal_driver_sifive_uart0_getc,
145 .uart.get_baud_rate = __metal_driver_sifive_uart0_get_baud_rate,
146 .uart.set_baud_rate = __metal_driver_sifive_uart0_set_baud_rate,
147 .uart.controller_interrupt = __metal_driver_sifive_uart0_interrupt_controller,
148 .uart.get_interrupt_id = __metal_driver_sifive_uart0_get_interrupt_id,
151 #endif /* METAL_SIFIVE_UART0 */