1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <arch/arm64/periphmap.h>
8 #include <dev/interrupt.h>
9 #include <dev/uart.h>
10 #include <kernel/thread.h>
11 #include <lib/cbuf.h>
12 #include <lib/debuglog.h>
13 #include <reg.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <trace.h>
17 
18 #include <pdev/driver.h>
19 #include <pdev/uart.h>
20 #include <zircon/boot/driver-config.h>
21 
22 // clang-format off
23 
24 #define S905_UART_WFIFO         (0x0)
25 #define S905_UART_RFIFO         (0x4)
26 #define S905_UART_CONTROL       (0x8)
27 #define S905_UART_STATUS        (0xc)
28 #define S905_UART_IRQ_CONTROL   (0x10)
29 #define S905_UART_REG5          (0x14)
30 
31 
32 #define S905_UART_CONTROL_INVRTS    (1 << 31)
33 #define S905_UART_CONTROL_MASKERR   (1 << 30)
34 #define S905_UART_CONTROL_INVCTS    (1 << 29)
35 #define S905_UART_CONTROL_TXINTEN   (1 << 28)
36 #define S905_UART_CONTROL_RXINTEN   (1 << 27)
37 #define S905_UART_CONTROL_INVTX     (1 << 26)
38 #define S905_UART_CONTROL_INVRX     (1 << 25)
39 #define S905_UART_CONTROL_CLRERR    (1 << 24)
40 #define S905_UART_CONTROL_RSTRX     (1 << 23)
41 #define S905_UART_CONTROL_RSTTX     (1 << 22)
42 #define S905_UART_CONTROL_XMITLEN   (1 << 20)
43 #define S905_UART_CONTROL_XMITLEN_MASK   (0x3 << 20)
44 #define S905_UART_CONTROL_PAREN     (1 << 19)
45 #define S905_UART_CONTROL_PARTYPE   (1 << 18)
46 #define S905_UART_CONTROL_STOPLEN   (1 << 16)
47 #define S905_UART_CONTROL_STOPLEN_MASK   (0x3 << 16)
48 #define S905_UART_CONTROL_TWOWIRE   (1 << 15)
49 #define S905_UART_CONTROL_RXEN      (1 << 13)
50 #define S905_UART_CONTROL_TXEN      (1 << 12)
51 #define S905_UART_CONTROL_BAUD0     (1 << 0)
52 #define S905_UART_CONTROL_BAUD0_MASK     (0xfff << 0)
53 
54 #define S905_UART_STATUS_RXBUSY         (1 << 26)
55 #define S905_UART_STATUS_TXBUSY         (1 << 25)
56 #define S905_UART_STATUS_RXOVRFLW       (1 << 24)
57 #define S905_UART_STATUS_CTSLEVEL       (1 << 23)
58 #define S905_UART_STATUS_TXEMPTY        (1 << 22)
59 #define S905_UART_STATUS_TXFULL         (1 << 21)
60 #define S905_UART_STATUS_RXEMPTY        (1 << 20)
61 #define S905_UART_STATUS_RXFULL         (1 << 19)
62 #define S905_UART_STATUS_TXOVRFLW       (1 << 18)
63 #define S905_UART_STATUS_FRAMEERR       (1 << 17)
64 #define S905_UART_STATUS_PARERR         (1 << 16)
65 #define S905_UART_STATUS_TXCOUNT_POS    (8)
66 #define S905_UART_STATUS_TXCOUNT_MASK   (0x7f << S905_UART_STATUS_TXCOUNT_POS)
67 #define S905_UART_STATUS_RXCOUNT_POS    (0)
68 #define S905_UART_STATUS_RXCOUNT_MASK   (0x7f << S905_UART_STATUS_RXCOUNT_POS)
69 
70 #define UARTREG(base, reg)  (*(volatile uint32_t*)((base)  + (reg)))
71 
72 #define RXBUF_SIZE 128
73 #define NUM_UART 5
74 
75 #define S905_UART0_OFFSET          (0x011084c0)
76 #define S905_UART1_OFFSET          (0x011084dc)
77 #define S905_UART2_OFFSET          (0x01108700)
78 #define S905_UART0_AO_OFFSET       (0x081004c0)
79 #define S905_UART1_AO_OFFSET       (0x081004e0)
80 
81 // clang-format on
82 
83 static cbuf_t uart_rx_buf;
84 static bool initialized = false;
85 static vaddr_t s905_uart_base = 0;
86 static uint32_t s905_uart_irq = 0;
87 
88 /*
89  * Tx driven irq:
90  * According to the meson s905 UART spec
91  * https://dn.odroid.com/S905/DataSheet/S905_Public_Datasheet_V1.1.4.pdf
92  * 1) Tx Fifo depth is 64 bytes
93  * 2) The Misc register (aka irq control), by default will
94  * interrupt when the # of bytes in the fifo falls below 32
95  * but this can be changed if necessary (XMIT_IRQ_CNT).
96  * But no need to change this right now.
97  * 3) UART status register (TXCOUNT_MASK) holds the # of bytes
98  * in the Tx FIFO. More usefully, the TXFULL bit tells us when
99  * the Tx FIFO is full. We can use this to continue shoving
100  * data into the FIFO.
101  * 4) Setting TXINTEN will generate an interrupt each time a byte is
102  * read from the Tx FIFO. So we can leave the interrupt unmasked.
103  */
104 static bool uart_tx_irq_enabled = false;
105 static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
106                                                       true,
107                                                       EVENT_FLAG_AUTOUNSIGNAL);
108 
109 static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
110 
uart_irq(void * arg)111 static interrupt_eoi uart_irq(void* arg) {
112     uintptr_t base = (uintptr_t)arg;
113 
114     /* read interrupt status and mask */
115     while ((UARTREG(base, S905_UART_STATUS) & S905_UART_STATUS_RXCOUNT_MASK) > 0) {
116         if (cbuf_space_avail(&uart_rx_buf) == 0) {
117             break;
118         }
119         char c = static_cast<char>(UARTREG(base, S905_UART_RFIFO));
120         cbuf_write_char(&uart_rx_buf, c);
121     }
122     if (UARTREG(s905_uart_base, S905_UART_CONTROL) & S905_UART_CONTROL_TXINTEN) {
123         spin_lock(&uart_spinlock);
124         if (!(UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL))
125         /* Signal any waiting Tx */
126         {
127             event_signal(&uart_dputc_event, true);
128         }
129         spin_unlock(&uart_spinlock);
130     }
131 
132     return IRQ_EOI_DEACTIVATE;
133 }
134 
s905_uart_init(const void * driver_data,uint32_t length)135 static void s905_uart_init(const void* driver_data, uint32_t length) {
136     assert(s905_uart_base);
137     assert(s905_uart_irq);
138 
139     // create circular buffer to hold received data
140     cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
141 
142     //reset the port
143     UARTREG(s905_uart_base, S905_UART_CONTROL) |= S905_UART_CONTROL_RSTRX |
144                                                   S905_UART_CONTROL_RSTTX |
145                                                   S905_UART_CONTROL_CLRERR;
146     UARTREG(s905_uart_base, S905_UART_CONTROL) &= ~(S905_UART_CONTROL_RSTRX |
147                                                     S905_UART_CONTROL_RSTTX |
148                                                     S905_UART_CONTROL_CLRERR);
149     // enable rx and tx
150     UARTREG(s905_uart_base, S905_UART_CONTROL) |= S905_UART_CONTROL_TXEN |
151                                                   S905_UART_CONTROL_RXEN;
152 
153     uint32_t val;
154     val = S905_UART_CONTROL_INVRTS | S905_UART_CONTROL_RXINTEN |
155           S905_UART_CONTROL_TWOWIRE;
156     if (dlog_bypass() == false) {
157         val |= S905_UART_CONTROL_TXINTEN;
158     }
159     UARTREG(s905_uart_base, S905_UART_CONTROL) |= val;
160 
161     // Set to interrupt every 1 rx byte
162     uint32_t temp2 = UARTREG(s905_uart_base, S905_UART_IRQ_CONTROL);
163     temp2 &= 0xffff0000;
164     temp2 |= (1 << 8) | (1);
165     UARTREG(s905_uart_base, S905_UART_IRQ_CONTROL) = temp2;
166 
167     zx_status_t status = register_int_handler(s905_uart_irq, &uart_irq, (void*)s905_uart_base);
168     DEBUG_ASSERT(status == ZX_OK);
169 
170     initialized = true;
171 
172     if (dlog_bypass() == true) {
173         uart_tx_irq_enabled = false;
174     } else {
175         /* start up tx driven output */
176         printf("UART: started IRQ driven TX\n");
177         uart_tx_irq_enabled = true;
178     }
179 
180     // enable interrupt
181     unmask_interrupt(s905_uart_irq);
182 }
183 
184 /* panic-time getc/putc */
s905_uart_pputc(char c)185 static int s905_uart_pputc(char c) {
186     if (!s905_uart_base) {
187         return 0;
188     }
189 
190     /* spin while fifo is full */
191     while (UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL)
192         ;
193     UARTREG(s905_uart_base, S905_UART_WFIFO) = c;
194 
195     return 1;
196 }
197 
s905_uart_pgetc()198 static int s905_uart_pgetc() {
199     if (!s905_uart_base) {
200         return 0;
201     }
202 
203     if ((UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_RXEMPTY) == 0) {
204         return UARTREG(s905_uart_base, S905_UART_RFIFO);
205     } else {
206         return ZX_ERR_INTERNAL;
207     }
208 }
209 
s905_uart_getc(bool wait)210 static int s905_uart_getc(bool wait) {
211     if (!s905_uart_base) {
212         return -1;
213     }
214 
215     if (initialized) {
216         // do cbuf stuff here
217         char c;
218         if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
219             return c;
220         }
221         return ZX_ERR_INTERNAL;
222 
223     } else {
224         //Interrupts not online yet, use the panic calls for now.
225         return s905_uart_pgetc();
226     }
227 }
228 
229 /*
230  * Keeping this simple for now, we try to write 1 byte at a time
231  * to the Tx FIFO. Blocking or spinning if the Tx FIFO is full.
232  * The event is signaled up from the interrupt handler, when a
233  * byte is read from the Tx FIFO.
234  * (setting TXINTEN results in the generation of an interrupt
235  * each time a byte is read from the Tx FIFO).
236  */
s905_dputs(const char * str,size_t len,bool block,bool map_NL)237 static void s905_dputs(const char* str, size_t len,
238                        bool block, bool map_NL) {
239     spin_lock_saved_state_t state;
240     bool copied_CR = false;
241 
242     if (!s905_uart_base) {
243         return;
244     }
245     if (!uart_tx_irq_enabled) {
246         block = false;
247     }
248     spin_lock_irqsave(&uart_spinlock, state);
249     while (len > 0) {
250         /* Is FIFO Full ? */
251         while (UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL) {
252             spin_unlock_irqrestore(&uart_spinlock, state);
253             if (block) {
254                 event_wait(&uart_dputc_event);
255             } else {
256                 arch_spinloop_pause();
257             }
258             spin_lock_irqsave(&uart_spinlock, state);
259         }
260 
261         if (*str == '\n' && map_NL && !copied_CR) {
262             copied_CR = true;
263             UARTREG(s905_uart_base, S905_UART_WFIFO) = '\r';
264         } else {
265             copied_CR = false;
266             UARTREG(s905_uart_base, S905_UART_WFIFO) = *str++;
267             len--;
268         }
269     }
270     spin_unlock_irqrestore(&uart_spinlock, state);
271 }
272 
s905_uart_start_panic()273 static void s905_uart_start_panic() {
274     uart_tx_irq_enabled = false;
275 }
276 
277 static const struct pdev_uart_ops s905_uart_ops = {
278     .getc = s905_uart_getc,
279     .pputc = s905_uart_pputc,
280     .pgetc = s905_uart_pgetc,
281     .start_panic = s905_uart_start_panic,
282     .dputs = s905_dputs,
283 };
284 
s905_uart_init_early(const void * driver_data,uint32_t length)285 static void s905_uart_init_early(const void* driver_data, uint32_t length) {
286     ASSERT(length >= sizeof(dcfg_simple_t));
287     auto driver = static_cast<const dcfg_simple_t*>(driver_data);
288     ASSERT(driver->mmio_phys && driver->irq);
289 
290     s905_uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
291     ASSERT(s905_uart_base);
292     s905_uart_irq = driver->irq;
293 
294     pdev_register_uart(&s905_uart_ops);
295 }
296 
297 LK_PDEV_INIT(s905_uart_init_early, KDRV_AMLOGIC_UART, s905_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
298 LK_PDEV_INIT(s905_uart_init, KDRV_AMLOGIC_UART, s905_uart_init, LK_INIT_LEVEL_PLATFORM);
299