1 /*
2  * Copyright (c) 2021 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 // goldfish tty
21 // from https://github.com/qemu/qemu/blob/master/hw/char/goldfish_tty.c
22 volatile unsigned int * const goldfish_tty_base = (void *)VIRT_GF_TTY_MMIO_BASE;
23 
24 // registers
25 enum {
26     REG_PUT_CHAR      = 0x00,
27     REG_BYTES_READY   = 0x04,
28     REG_CMD           = 0x08,
29     REG_DATA_PTR      = 0x10,
30     REG_DATA_LEN      = 0x14,
31     REG_DATA_PTR_HIGH = 0x18,
32     REG_VERSION       = 0x20,
33 };
34 
35 // commands
36 
37 enum {
38     CMD_INT_DISABLE   = 0x00,
39     CMD_INT_ENABLE    = 0x01,
40     CMD_WRITE_BUFFER  = 0x02,
41     CMD_READ_BUFFER   = 0x03,
42 };
43 
44 #define RXBUF_SIZE 128
45 static char uart_rx_buf_data[RXBUF_SIZE];
46 static cbuf_t uart_rx_buf;
47 
48 static char transfer_buf[1]; // static pointer used to transfer MMIO data
49 
write_reg(int reg,uint32_t val)50 static void write_reg(int reg, uint32_t val) {
51     goldfish_tty_base[reg / 4] = val;
52 }
53 
read_reg(int reg)54 static uint32_t read_reg(int reg) {
55     return goldfish_tty_base[reg / 4];
56 }
57 
uart_irq_handler(void * arg)58 static enum handler_return uart_irq_handler(void *arg) {
59     bool resched = false;
60 
61     // use a DMA read of one byte if a byte is ready
62     if (read_reg(REG_BYTES_READY) > 0) {
63         write_reg(REG_CMD, CMD_READ_BUFFER);
64         char c = transfer_buf[0];
65         cbuf_write_char(&uart_rx_buf, c, false);
66         resched = true;
67     }
68 
69     return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
70 }
71 
goldfish_tty_early_init(void)72 void goldfish_tty_early_init(void) {
73     // make sure irqs are disabled
74     write_reg(REG_CMD, CMD_INT_DISABLE);
75 
76     // set up the transfer buffer for receives
77     write_reg(REG_DATA_PTR, (uint32_t)transfer_buf);
78     write_reg(REG_DATA_PTR_HIGH, 0);
79     write_reg(REG_DATA_LEN, sizeof(transfer_buf));
80 }
81 
goldfish_tty_init(void)82 void goldfish_tty_init(void) {
83     /* finish uart init to get rx going */
84     cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
85 
86     register_int_handler(GOLDFISH_TTY_IRQ, uart_irq_handler, NULL);
87 
88     unmask_interrupt(GOLDFISH_TTY_IRQ);
89 
90     write_reg(REG_CMD, CMD_INT_ENABLE);
91 }
92 
uart_putc(char c)93 void uart_putc(char c) {
94     write_reg(REG_PUT_CHAR, c);
95 }
96 
uart_getc(char * c,bool wait)97 int uart_getc(char *c, bool wait) {
98 #if 1
99     return cbuf_read_char(&uart_rx_buf, c, wait);
100 #else
101     return platform_pgetc(c, false);
102 #endif
103 }
104 
platform_dputc(char c)105 void platform_dputc(char c) {
106     if (c == '\n')
107         platform_dputc('\r');
108     uart_putc(c);
109 }
110 
platform_dgetc(char * c,bool wait)111 int platform_dgetc(char *c, bool wait) {
112     int ret = uart_getc(c, wait);
113 
114     return ret;
115 }
116 
117 /* panic-time getc/putc */
platform_pputc(char c)118 void platform_pputc(char c) {
119     return uart_putc(c);
120 }
121 
platform_pgetc(char * c,bool wait)122 int platform_pgetc(char *c, bool wait) {
123     // use a DMA read of one byte if a byte is ready
124     if (read_reg(REG_BYTES_READY) > 0) {
125         write_reg(REG_CMD, CMD_READ_BUFFER);
126         *c = transfer_buf[0];
127         return 0;
128     }
129     return -1;
130 }
131