]> Git Repo - qemu.git/blob - hw/riscv/sifive_uart.c
maint: Add .mailmap entries for patches claiming list authorship
[qemu.git] / hw / riscv / sifive_uart.c
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 "hw/sysbus.h"
22 #include "chardev/char.h"
23 #include "chardev/char-fe.h"
24 #include "target/riscv/cpu.h"
25 #include "hw/riscv/sifive_uart.h"
26
27 /*
28  * Not yet implemented:
29  *
30  * Transmit FIFO using "qemu/fifo8.h"
31  * SIFIVE_UART_IE_TXWM interrupts
32  * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
33  * Rx FIFO watermark interrupt trigger threshold
34  * Tx FIFO watermark interrupt trigger threshold.
35  */
36
37 static void update_irq(SiFiveUARTState *s)
38 {
39     int cond = 0;
40     if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
41         cond = 1;
42     }
43     if (cond) {
44         qemu_irq_raise(s->irq);
45     } else {
46         qemu_irq_lower(s->irq);
47     }
48 }
49
50 static uint64_t
51 uart_read(void *opaque, hwaddr addr, unsigned int size)
52 {
53     SiFiveUARTState *s = opaque;
54     unsigned char r;
55     switch (addr) {
56     case SIFIVE_UART_RXFIFO:
57         if (s->rx_fifo_len) {
58             r = s->rx_fifo[0];
59             memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
60             s->rx_fifo_len--;
61             qemu_chr_fe_accept_input(&s->chr);
62             update_irq(s);
63             return r;
64         }
65         return 0x80000000;
66
67     case SIFIVE_UART_TXFIFO:
68         return 0; /* Should check tx fifo */
69     case SIFIVE_UART_IE:
70         return s->ie;
71     case SIFIVE_UART_IP:
72         return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
73     case SIFIVE_UART_TXCTRL:
74         return s->txctrl;
75     case SIFIVE_UART_RXCTRL:
76         return s->rxctrl;
77     case SIFIVE_UART_DIV:
78         return s->div;
79     }
80
81     hw_error("%s: bad read: addr=0x%x\n",
82         __func__, (int)addr);
83     return 0;
84 }
85
86 static void
87 uart_write(void *opaque, hwaddr addr,
88            uint64_t val64, unsigned int size)
89 {
90     SiFiveUARTState *s = opaque;
91     uint32_t value = val64;
92     unsigned char ch = value;
93
94     switch (addr) {
95     case SIFIVE_UART_TXFIFO:
96         qemu_chr_fe_write(&s->chr, &ch, 1);
97         return;
98     case SIFIVE_UART_IE:
99         s->ie = val64;
100         update_irq(s);
101         return;
102     case SIFIVE_UART_TXCTRL:
103         s->txctrl = val64;
104         return;
105     case SIFIVE_UART_RXCTRL:
106         s->rxctrl = val64;
107         return;
108     case SIFIVE_UART_DIV:
109         s->div = val64;
110         return;
111     }
112     hw_error("%s: bad write: addr=0x%x v=0x%x\n",
113         __func__, (int)addr, (int)value);
114 }
115
116 static const MemoryRegionOps uart_ops = {
117     .read = uart_read,
118     .write = uart_write,
119     .endianness = DEVICE_NATIVE_ENDIAN,
120     .valid = {
121         .min_access_size = 4,
122         .max_access_size = 4
123     }
124 };
125
126 static void uart_rx(void *opaque, const uint8_t *buf, int size)
127 {
128     SiFiveUARTState *s = opaque;
129
130     /* Got a byte.  */
131     if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
132         printf("WARNING: UART dropped char.\n");
133         return;
134     }
135     s->rx_fifo[s->rx_fifo_len++] = *buf;
136
137     update_irq(s);
138 }
139
140 static int uart_can_rx(void *opaque)
141 {
142     SiFiveUARTState *s = opaque;
143
144     return s->rx_fifo_len < sizeof(s->rx_fifo);
145 }
146
147 static void uart_event(void *opaque, int event)
148 {
149 }
150
151 static int uart_be_change(void *opaque)
152 {
153     SiFiveUARTState *s = opaque;
154
155     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
156         uart_be_change, s, NULL, true);
157
158     return 0;
159 }
160
161 /*
162  * Create UART device.
163  */
164 SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
165     Chardev *chr, qemu_irq irq)
166 {
167     SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
168     s->irq = irq;
169     qemu_chr_fe_init(&s->chr, chr, &error_abort);
170     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
171         uart_be_change, s, NULL, true);
172     memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
173                           TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
174     memory_region_add_subregion(address_space, base, &s->mmio);
175     return s;
176 }
This page took 0.035747 seconds and 4 git commands to generate.