sifive_uart.c (7674B)
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 "migration/vmstate.h" 23#include "chardev/char.h" 24#include "chardev/char-fe.h" 25#include "hw/irq.h" 26#include "hw/char/sifive_uart.h" 27#include "hw/qdev-properties-system.h" 28 29/* 30 * Not yet implemented: 31 * 32 * Transmit FIFO using "qemu/fifo8.h" 33 */ 34 35/* Returns the state of the IP (interrupt pending) register */ 36static uint64_t sifive_uart_ip(SiFiveUARTState *s) 37{ 38 uint64_t ret = 0; 39 40 uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); 41 uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); 42 43 if (txcnt != 0) { 44 ret |= SIFIVE_UART_IP_TXWM; 45 } 46 if (s->rx_fifo_len > rxcnt) { 47 ret |= SIFIVE_UART_IP_RXWM; 48 } 49 50 return ret; 51} 52 53static void sifive_uart_update_irq(SiFiveUARTState *s) 54{ 55 int cond = 0; 56 if ((s->ie & SIFIVE_UART_IE_TXWM) || 57 ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { 58 cond = 1; 59 } 60 if (cond) { 61 qemu_irq_raise(s->irq); 62 } else { 63 qemu_irq_lower(s->irq); 64 } 65} 66 67static uint64_t 68sifive_uart_read(void *opaque, hwaddr addr, unsigned int size) 69{ 70 SiFiveUARTState *s = opaque; 71 unsigned char r; 72 switch (addr) { 73 case SIFIVE_UART_RXFIFO: 74 if (s->rx_fifo_len) { 75 r = s->rx_fifo[0]; 76 memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1); 77 s->rx_fifo_len--; 78 qemu_chr_fe_accept_input(&s->chr); 79 sifive_uart_update_irq(s); 80 return r; 81 } 82 return 0x80000000; 83 84 case SIFIVE_UART_TXFIFO: 85 return 0; /* Should check tx fifo */ 86 case SIFIVE_UART_IE: 87 return s->ie; 88 case SIFIVE_UART_IP: 89 return sifive_uart_ip(s); 90 case SIFIVE_UART_TXCTRL: 91 return s->txctrl; 92 case SIFIVE_UART_RXCTRL: 93 return s->rxctrl; 94 case SIFIVE_UART_DIV: 95 return s->div; 96 } 97 98 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", 99 __func__, (int)addr); 100 return 0; 101} 102 103static void 104sifive_uart_write(void *opaque, hwaddr addr, 105 uint64_t val64, unsigned int size) 106{ 107 SiFiveUARTState *s = opaque; 108 uint32_t value = val64; 109 unsigned char ch = value; 110 111 switch (addr) { 112 case SIFIVE_UART_TXFIFO: 113 qemu_chr_fe_write(&s->chr, &ch, 1); 114 sifive_uart_update_irq(s); 115 return; 116 case SIFIVE_UART_IE: 117 s->ie = val64; 118 sifive_uart_update_irq(s); 119 return; 120 case SIFIVE_UART_TXCTRL: 121 s->txctrl = val64; 122 return; 123 case SIFIVE_UART_RXCTRL: 124 s->rxctrl = val64; 125 return; 126 case SIFIVE_UART_DIV: 127 s->div = val64; 128 return; 129 } 130 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", 131 __func__, (int)addr, (int)value); 132} 133 134static const MemoryRegionOps sifive_uart_ops = { 135 .read = sifive_uart_read, 136 .write = sifive_uart_write, 137 .endianness = DEVICE_NATIVE_ENDIAN, 138 .valid = { 139 .min_access_size = 4, 140 .max_access_size = 4 141 } 142}; 143 144static void sifive_uart_rx(void *opaque, const uint8_t *buf, int size) 145{ 146 SiFiveUARTState *s = opaque; 147 148 /* Got a byte. */ 149 if (s->rx_fifo_len >= sizeof(s->rx_fifo)) { 150 printf("WARNING: UART dropped char.\n"); 151 return; 152 } 153 s->rx_fifo[s->rx_fifo_len++] = *buf; 154 155 sifive_uart_update_irq(s); 156} 157 158static int sifive_uart_can_rx(void *opaque) 159{ 160 SiFiveUARTState *s = opaque; 161 162 return s->rx_fifo_len < sizeof(s->rx_fifo); 163} 164 165static void sifive_uart_event(void *opaque, QEMUChrEvent event) 166{ 167} 168 169static int sifive_uart_be_change(void *opaque) 170{ 171 SiFiveUARTState *s = opaque; 172 173 qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, 174 sifive_uart_event, sifive_uart_be_change, s, 175 NULL, true); 176 177 return 0; 178} 179 180static Property sifive_uart_properties[] = { 181 DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr), 182 DEFINE_PROP_END_OF_LIST(), 183}; 184 185static void sifive_uart_init(Object *obj) 186{ 187 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 188 SiFiveUARTState *s = SIFIVE_UART(obj); 189 190 memory_region_init_io(&s->mmio, OBJECT(s), &sifive_uart_ops, s, 191 TYPE_SIFIVE_UART, SIFIVE_UART_MAX); 192 sysbus_init_mmio(sbd, &s->mmio); 193 sysbus_init_irq(sbd, &s->irq); 194} 195 196static void sifive_uart_realize(DeviceState *dev, Error **errp) 197{ 198 SiFiveUARTState *s = SIFIVE_UART(dev); 199 200 qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, 201 sifive_uart_event, sifive_uart_be_change, s, 202 NULL, true); 203 204} 205 206static void sifive_uart_reset_enter(Object *obj, ResetType type) 207{ 208 SiFiveUARTState *s = SIFIVE_UART(obj); 209 s->ie = 0; 210 s->ip = 0; 211 s->txctrl = 0; 212 s->rxctrl = 0; 213 s->div = 0; 214 s->rx_fifo_len = 0; 215} 216 217static void sifive_uart_reset_hold(Object *obj) 218{ 219 SiFiveUARTState *s = SIFIVE_UART(obj); 220 qemu_irq_lower(s->irq); 221} 222 223static const VMStateDescription vmstate_sifive_uart = { 224 .name = TYPE_SIFIVE_UART, 225 .version_id = 1, 226 .minimum_version_id = 1, 227 .fields = (VMStateField[]) { 228 VMSTATE_UINT8_ARRAY(rx_fifo, SiFiveUARTState, 229 SIFIVE_UART_RX_FIFO_SIZE), 230 VMSTATE_UINT8(rx_fifo_len, SiFiveUARTState), 231 VMSTATE_UINT32(ie, SiFiveUARTState), 232 VMSTATE_UINT32(ip, SiFiveUARTState), 233 VMSTATE_UINT32(txctrl, SiFiveUARTState), 234 VMSTATE_UINT32(rxctrl, SiFiveUARTState), 235 VMSTATE_UINT32(div, SiFiveUARTState), 236 VMSTATE_END_OF_LIST() 237 }, 238}; 239 240 241static void sifive_uart_class_init(ObjectClass *oc, void *data) 242{ 243 DeviceClass *dc = DEVICE_CLASS(oc); 244 ResettableClass *rc = RESETTABLE_CLASS(oc); 245 246 dc->realize = sifive_uart_realize; 247 dc->vmsd = &vmstate_sifive_uart; 248 rc->phases.enter = sifive_uart_reset_enter; 249 rc->phases.hold = sifive_uart_reset_hold; 250 device_class_set_props(dc, sifive_uart_properties); 251 set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 252} 253 254static const TypeInfo sifive_uart_info = { 255 .name = TYPE_SIFIVE_UART, 256 .parent = TYPE_SYS_BUS_DEVICE, 257 .instance_size = sizeof(SiFiveUARTState), 258 .instance_init = sifive_uart_init, 259 .class_init = sifive_uart_class_init, 260}; 261 262static void sifive_uart_register_types(void) 263{ 264 type_register_static(&sifive_uart_info); 265} 266 267type_init(sifive_uart_register_types) 268 269/* 270 * Create UART device. 271 */ 272SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, 273 Chardev *chr, qemu_irq irq) 274{ 275 DeviceState *dev; 276 SysBusDevice *s; 277 SiFiveUARTState *r; 278 279 dev = qdev_new("riscv.sifive.uart"); 280 s = SYS_BUS_DEVICE(dev); 281 qdev_prop_set_chr(dev, "chardev", chr); 282 sysbus_realize_and_unref(s, &error_fatal); 283 memory_region_add_subregion(address_space, base, 284 sysbus_mmio_get_region(s, 0)); 285 sysbus_connect_irq(s, 0, irq); 286 287 r = SIFIVE_UART(dev); 288 return r; 289}