]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/sifive_uart0.c
Update RISCC-V-RV32-SiFive_HiFive1_FreedomStudio project to latest tools and metal...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_FreedomStudio / freedom-metal / src / drivers / 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
42 int __metal_driver_sifive_uart0_txready(struct metal_uart *uart)
43 {
44   long control_base = __metal_driver_sifive_uart0_control_base(uart);
45
46   return !((UART_REGW(METAL_SIFIVE_UART0_TXDATA) & UART_TXFULL));
47 }
48
49
50 int __metal_driver_sifive_uart0_putc(struct metal_uart *uart, int c)
51 {
52     long control_base = __metal_driver_sifive_uart0_control_base(uart);
53
54     while (!__metal_driver_sifive_uart0_txready(uart)) {
55                 /* wait */
56     }
57     UART_REGW(METAL_SIFIVE_UART0_TXDATA) = c;
58     return 0;
59 }
60
61
62 int __metal_driver_sifive_uart0_getc(struct metal_uart *uart, int *c)
63 {
64     uint32_t ch;
65     long control_base = __metal_driver_sifive_uart0_control_base(uart);
66     /* No seperate status register, we get status and the byte at same time */
67     ch = UART_REGW(METAL_SIFIVE_UART0_RXDATA);;
68     if( ch & UART_RXEMPTY ){
69       *c = -1; /* aka: EOF in most of the world */
70     } else {
71       *c = ch & 0x0ff;
72     }
73     return 0;
74 }
75
76
77 int __metal_driver_sifive_uart0_get_baud_rate(struct metal_uart *guart)
78 {
79     struct __metal_driver_sifive_uart0 *uart = (void *)guart;
80     return uart->baud_rate;
81 }
82
83 int __metal_driver_sifive_uart0_set_baud_rate(struct metal_uart *guart, int baud_rate)
84 {
85     struct __metal_driver_sifive_uart0 *uart = (void *)guart;
86     long control_base = __metal_driver_sifive_uart0_control_base(guart);
87     struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart);
88
89     uart->baud_rate = baud_rate;
90
91     if (clock != NULL) {
92         long clock_rate = clock->vtable->get_rate_hz(clock);
93         UART_REGW(METAL_SIFIVE_UART0_DIV) = clock_rate / baud_rate - 1;
94         UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXEN;
95         UART_REGW(METAL_SIFIVE_UART0_RXCTRL) |= UART_RXEN;
96     }
97     return 0;
98 }
99
100 static void pre_rate_change_callback_func(void *priv)
101 {
102     struct __metal_driver_sifive_uart0 *uart = priv;
103     long control_base = __metal_driver_sifive_uart0_control_base((struct metal_uart *)priv);
104     struct metal_clock *clock = __metal_driver_sifive_uart0_clock((struct metal_uart *)priv);
105
106     /* Detect when the TXDATA is empty by setting the transmit watermark count
107      * to one and waiting until an interrupt is pending */
108
109     UART_REGW(METAL_SIFIVE_UART0_TXCTRL) &= ~(UART_TXCNT(0x7));
110     UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXCNT(1);
111
112     while((UART_REGW(METAL_SIFIVE_UART0_IP) & UART_TXWM) == 0) ;
113
114     /* When the TXDATA clears, the UART is still shifting out the last byte.
115      * Calculate the time we must drain to finish transmitting and then wait
116      * that long. */
117
118     long bits_per_symbol = (UART_REGW(METAL_SIFIVE_UART0_TXCTRL) & (1 << 1)) ? 9 : 10;
119     long clk_freq = clock->vtable->get_rate_hz(clock);
120     long cycles_to_wait = bits_per_symbol * clk_freq / uart->baud_rate;
121
122     for(volatile long x = 0; x < cycles_to_wait; x++)
123         __asm__("nop");
124 }
125
126 static void post_rate_change_callback_func(void *priv)
127 {
128     struct __metal_driver_sifive_uart0 *uart = priv;
129     metal_uart_set_baud_rate(&uart->uart, uart->baud_rate);
130 }
131
132 void __metal_driver_sifive_uart0_init(struct metal_uart *guart, int baud_rate)
133 {
134     struct __metal_driver_sifive_uart0 *uart = (void *)(guart);
135     struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart);
136     struct __metal_driver_sifive_gpio0 *pinmux = __metal_driver_sifive_uart0_pinmux(guart);
137
138     if(clock != NULL) {
139         uart->pre_rate_change_callback.callback = &pre_rate_change_callback_func;
140         uart->pre_rate_change_callback.priv = guart;
141         metal_clock_register_pre_rate_change_callback(clock, &(uart->pre_rate_change_callback));
142
143         uart->post_rate_change_callback.callback = &post_rate_change_callback_func;
144         uart->post_rate_change_callback.priv = guart;
145         metal_clock_register_post_rate_change_callback(clock, &(uart->post_rate_change_callback));
146     }
147
148     metal_uart_set_baud_rate(&(uart->uart), baud_rate);
149
150     if (pinmux != NULL) {
151         long pinmux_output_selector = __metal_driver_sifive_uart0_pinmux_output_selector(guart);
152         long pinmux_source_selector = __metal_driver_sifive_uart0_pinmux_source_selector(guart);
153         pinmux->gpio.vtable->enable_io(
154             (struct metal_gpio *) pinmux,
155             pinmux_output_selector,
156             pinmux_source_selector
157         );
158     }
159 }
160
161 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_uart0) = {
162     .uart.init          = __metal_driver_sifive_uart0_init,
163     .uart.putc          = __metal_driver_sifive_uart0_putc,
164     .uart.getc          = __metal_driver_sifive_uart0_getc,
165     .uart.get_baud_rate = __metal_driver_sifive_uart0_get_baud_rate,
166     .uart.set_baud_rate = __metal_driver_sifive_uart0_set_baud_rate,
167     .uart.controller_interrupt = __metal_driver_sifive_uart0_interrupt_controller,
168     .uart.get_interrupt_id     = __metal_driver_sifive_uart0_get_interrupt_id,
169 };
170
171 #endif /* METAL_SIFIVE_UART0 */
172
173 typedef int no_empty_translation_units;