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