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 "hw/sysbus.h" 23 #include "chardev/char.h" 24 #include "chardev/char-fe.h" 25 #include "hw/hw.h" 26 #include "hw/irq.h" 27 #include "hw/char/sifive_uart.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 */ 36 static uint64_t 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 53 static void 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 67 static uint64_t 68 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 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 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 103 static void 104 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 update_irq(s); 115 return; 116 case SIFIVE_UART_IE: 117 s->ie = val64; 118 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 134 static const MemoryRegionOps uart_ops = { 135 .read = uart_read, 136 .write = uart_write, 137 .endianness = DEVICE_NATIVE_ENDIAN, 138 .valid = { 139 .min_access_size = 4, 140 .max_access_size = 4 141 } 142 }; 143 144 static void 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 update_irq(s); 156 } 157 158 static int uart_can_rx(void *opaque) 159 { 160 SiFiveUARTState *s = opaque; 161 162 return s->rx_fifo_len < sizeof(s->rx_fifo); 163 } 164 165 static void uart_event(void *opaque, QEMUChrEvent event) 166 { 167 } 168 169 static int uart_be_change(void *opaque) 170 { 171 SiFiveUARTState *s = opaque; 172 173 qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, 174 uart_be_change, s, NULL, true); 175 176 return 0; 177 } 178 179 /* 180 * Create UART device. 181 */ 182 SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, 183 Chardev *chr, qemu_irq irq) 184 { 185 SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState)); 186 s->irq = irq; 187 qemu_chr_fe_init(&s->chr, chr, &error_abort); 188 qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, 189 uart_be_change, s, NULL, true); 190 memory_region_init_io(&s->mmio, NULL, &uart_ops, s, 191 TYPE_SIFIVE_UART, SIFIVE_UART_MAX); 192 memory_region_add_subregion(address_space, base, &s->mmio); 193 return s; 194 } 195