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