xref: /openbmc/qemu/hw/char/sifive_uart.c (revision 19f4ed36)
1 /*
2  * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
3  *
4  * Copyright (c) 2016 Stefan O'Rear
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2 or later, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "qapi/error.h"
21 #include "qemu/log.h"
22 #include "chardev/char.h"
23 #include "chardev/char-fe.h"
24 #include "hw/irq.h"
25 #include "hw/char/sifive_uart.h"
26 
27 /*
28  * Not yet implemented:
29  *
30  * Transmit FIFO using "qemu/fifo8.h"
31  */
32 
33 /* Returns the state of the IP (interrupt pending) register */
34 static uint64_t uart_ip(SiFiveUARTState *s)
35 {
36     uint64_t ret = 0;
37 
38     uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
39     uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl);
40 
41     if (txcnt != 0) {
42         ret |= SIFIVE_UART_IP_TXWM;
43     }
44     if (s->rx_fifo_len > rxcnt) {
45         ret |= SIFIVE_UART_IP_RXWM;
46     }
47 
48     return ret;
49 }
50 
51 static void update_irq(SiFiveUARTState *s)
52 {
53     int cond = 0;
54     if ((s->ie & SIFIVE_UART_IE_TXWM) ||
55         ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) {
56         cond = 1;
57     }
58     if (cond) {
59         qemu_irq_raise(s->irq);
60     } else {
61         qemu_irq_lower(s->irq);
62     }
63 }
64 
65 static uint64_t
66 uart_read(void *opaque, hwaddr addr, unsigned int size)
67 {
68     SiFiveUARTState *s = opaque;
69     unsigned char r;
70     switch (addr) {
71     case SIFIVE_UART_RXFIFO:
72         if (s->rx_fifo_len) {
73             r = s->rx_fifo[0];
74             memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
75             s->rx_fifo_len--;
76             qemu_chr_fe_accept_input(&s->chr);
77             update_irq(s);
78             return r;
79         }
80         return 0x80000000;
81 
82     case SIFIVE_UART_TXFIFO:
83         return 0; /* Should check tx fifo */
84     case SIFIVE_UART_IE:
85         return s->ie;
86     case SIFIVE_UART_IP:
87         return uart_ip(s);
88     case SIFIVE_UART_TXCTRL:
89         return s->txctrl;
90     case SIFIVE_UART_RXCTRL:
91         return s->rxctrl;
92     case SIFIVE_UART_DIV:
93         return s->div;
94     }
95 
96     qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
97                   __func__, (int)addr);
98     return 0;
99 }
100 
101 static void
102 uart_write(void *opaque, hwaddr addr,
103            uint64_t val64, unsigned int size)
104 {
105     SiFiveUARTState *s = opaque;
106     uint32_t value = val64;
107     unsigned char ch = value;
108 
109     switch (addr) {
110     case SIFIVE_UART_TXFIFO:
111         qemu_chr_fe_write(&s->chr, &ch, 1);
112         update_irq(s);
113         return;
114     case SIFIVE_UART_IE:
115         s->ie = val64;
116         update_irq(s);
117         return;
118     case SIFIVE_UART_TXCTRL:
119         s->txctrl = val64;
120         return;
121     case SIFIVE_UART_RXCTRL:
122         s->rxctrl = val64;
123         return;
124     case SIFIVE_UART_DIV:
125         s->div = val64;
126         return;
127     }
128     qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
129                   __func__, (int)addr, (int)value);
130 }
131 
132 static const MemoryRegionOps uart_ops = {
133     .read = uart_read,
134     .write = uart_write,
135     .endianness = DEVICE_NATIVE_ENDIAN,
136     .valid = {
137         .min_access_size = 4,
138         .max_access_size = 4
139     }
140 };
141 
142 static void uart_rx(void *opaque, const uint8_t *buf, int size)
143 {
144     SiFiveUARTState *s = opaque;
145 
146     /* Got a byte.  */
147     if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
148         printf("WARNING: UART dropped char.\n");
149         return;
150     }
151     s->rx_fifo[s->rx_fifo_len++] = *buf;
152 
153     update_irq(s);
154 }
155 
156 static int uart_can_rx(void *opaque)
157 {
158     SiFiveUARTState *s = opaque;
159 
160     return s->rx_fifo_len < sizeof(s->rx_fifo);
161 }
162 
163 static void uart_event(void *opaque, QEMUChrEvent event)
164 {
165 }
166 
167 static int uart_be_change(void *opaque)
168 {
169     SiFiveUARTState *s = opaque;
170 
171     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
172         uart_be_change, s, NULL, true);
173 
174     return 0;
175 }
176 
177 /*
178  * Create UART device.
179  */
180 SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
181     Chardev *chr, qemu_irq irq)
182 {
183     SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
184     s->irq = irq;
185     qemu_chr_fe_init(&s->chr, chr, &error_abort);
186     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
187         uart_be_change, s, NULL, true);
188     memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
189                           TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
190     memory_region_add_subregion(address_space, base, &s->mmio);
191     return s;
192 }
193