]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_IAR/SiFive_code/sifive_uart0.c
Minor updates to comment block for xTaskCheckForTimeOut().
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_IAR / SiFive_code / sifive_uart0.c
1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_SIFIVE_UART0
7
8 #include <metal/drivers/sifive_uart0.h>
9 #include <metal/machine.h>
10
11 /* TXDATA Fields */
12 #define UART_TXEN               (1 <<  0)
13 #define UART_TXFULL             (1 << 31)
14
15 /* RXDATA Fields */
16 #define UART_RXEN               (1 <<  0)
17 #define UART_RXEMPTY            (1 << 31)
18
19 /* TXCTRL Fields */
20 #define UART_NSTOP              (1 <<  1)
21 #define UART_TXCNT(count)       ((0x7 & count) << 16)
22
23 /* IP Fields */
24 #define UART_TXWM               (1 <<  0)
25
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)))
29
30 struct metal_interrupt *
31 __metal_driver_sifive_uart0_interrupt_controller(struct metal_uart *uart)
32 {
33     return __metal_driver_sifive_uart0_interrupt_parent(uart);
34 }
35
36 int __metal_driver_sifive_uart0_get_interrupt_id(struct metal_uart *uart)
37 {
38     return (__metal_driver_sifive_uart0_interrupt_line(uart) + METAL_INTERRUPT_ID_GL0);
39 }
40
41 int __metal_driver_sifive_uart0_putc(struct metal_uart *uart, unsigned char c)
42 {
43     long control_base = __metal_driver_sifive_uart0_control_base(uart);
44
45     while ((UART_REGW(METAL_SIFIVE_UART0_TXDATA) & UART_TXFULL) != 0) { }
46     UART_REGW(METAL_SIFIVE_UART0_TXDATA) = c;
47     return 0;
48 }
49
50 int __metal_driver_sifive_uart0_getc(struct metal_uart *uart, unsigned char *c)
51 {
52     uint32_t ch = UART_RXEMPTY;
53     long control_base = __metal_driver_sifive_uart0_control_base(uart);
54
55     while (ch & UART_RXEMPTY) {
56         ch = UART_REGW(METAL_SIFIVE_UART0_RXDATA);
57     }
58     *c = ch & 0xff;
59     return 0;
60 }
61
62 int __metal_driver_sifive_uart0_get_baud_rate(struct metal_uart *guart)
63 {
64     struct __metal_driver_sifive_uart0 *uart = (void *)guart;
65     return uart->baud_rate;
66 }
67
68 int __metal_driver_sifive_uart0_set_baud_rate(struct metal_uart *guart, int baud_rate)
69 {
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);
73
74     uart->baud_rate = baud_rate;
75
76     if (clock != NULL) {
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;
81     }
82     return 0;
83 }
84
85 static void pre_rate_change_callback(void *priv)
86 {
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);
90
91     /* Detect when the TXDATA is empty by setting the transmit watermark count
92      * to one and waiting until an interrupt is pending */
93
94     UART_REGW(METAL_SIFIVE_UART0_TXCTRL) &= ~(UART_TXCNT(0x7));
95     UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXCNT(1);
96
97     while((UART_REGW(METAL_SIFIVE_UART0_IP) & UART_TXWM) == 0) ;
98
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
101      * that long. */
102
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;
106
107     for(volatile long x = 0; x < cycles_to_wait; x++)
108         asm("nop");
109 }
110
111 static void post_rate_change_callback(void *priv)
112 {
113     struct __metal_driver_sifive_uart0 *uart = priv;
114     metal_uart_set_baud_rate(&uart->uart, uart->baud_rate);
115 }
116
117 void __metal_driver_sifive_uart0_init(struct metal_uart *guart, int baud_rate)
118 {
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);
122
123     if(clock != NULL) {
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);
126     }
127
128     metal_uart_set_baud_rate(&(uart->uart), baud_rate);
129
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
137         );
138     }
139 }
140
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,
149 };
150
151 #endif /* METAL_SIFIVE_UART0 */