1 /*
2  * Copyright (c) 2018 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <lk/reg.h>
9 #include <lk/trace.h>
10 #include <lib/cbuf.h>
11 #include <kernel/thread.h>
12 #include <platform.h>
13 #include <platform/interrupts.h>
14 #include <platform/debug.h>
15 #include <platform/virt.h>
16 #include <sys/types.h>
17 
18 #include "platform_p.h"
19 
20 // simple 16550 driver for the emulated serial port on qemu riscv virt machine
21 
22 static volatile uint8_t *const uart_base = (uint8_t *)UART0_BASE_VIRT;
23 
24 #define RXBUF_SIZE 128
25 static char uart_rx_buf_data[RXBUF_SIZE];
26 static cbuf_t uart_rx_buf;
27 
uart_read_8(size_t offset)28 static inline uint8_t uart_read_8(size_t offset) {
29     return uart_base[offset];
30 }
31 
uart_write_8(size_t offset,uint8_t val)32 static inline void uart_write_8(size_t offset, uint8_t val) {
33     uart_base[offset] = val;
34 }
35 
uart_irq_handler(void * arg)36 static enum handler_return uart_irq_handler(void *arg) {
37     unsigned char c;
38     bool resched = false;
39 
40     while (uart_read_8(5) & (1<<0)) {
41         c = uart_read_8(0);
42         cbuf_write_char(&uart_rx_buf, c, false);
43         resched = true;
44     }
45 
46     return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
47 }
48 
uart_init(void)49 void uart_init(void) {
50     /* finish uart init to get rx going */
51     cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
52 
53     register_int_handler(IRQ_UART0, uart_irq_handler, NULL);
54 
55     uart_write_8(1, 0x1); // enable receive data available interrupt
56 
57     unmask_interrupt(IRQ_UART0);
58 }
59 
uart_putc(char c)60 static void uart_putc(char c) {
61     while ((uart_read_8(5) & (1<<6)) == 0)
62         ;
63     uart_write_8(0, c);
64 }
65 
uart_getc(char * c,bool wait)66 static int uart_getc(char *c, bool wait) {
67     return cbuf_read_char(&uart_rx_buf, c, wait);
68 }
69 
platform_dputc(char c)70 void platform_dputc(char c) {
71     if (c == '\n')
72         platform_dputc('\r');
73     uart_putc(c);
74 }
75 
platform_dgetc(char * c,bool wait)76 int platform_dgetc(char *c, bool wait) {
77     int ret = uart_getc(c, wait);
78 
79     return ret;
80 }
81 
82 /* panic-time getc/putc */
platform_pgetc(char * c,bool wait)83 int platform_pgetc(char *c, bool wait) {
84     if (uart_read_8(5) & (1<<0)) {
85         *c = uart_read_8(0);
86         return 0;
87     }
88     return -1;
89 }
90 
91