| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU model of the UART on the SiFive E300 and U500 series SOCs. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2016 Stefan O'Rear | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms and conditions of the GNU General Public License, | 
					
						
							|  |  |  |  * version 2 or later, as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope it will be useful, but WITHOUT | 
					
						
							|  |  |  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
					
						
							|  |  |  |  * more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along with | 
					
						
							|  |  |  |  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							| 
									
										
										
										
											2019-09-06 09:19:54 -07:00
										 |  |  | #include "qemu/log.h"
 | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | #include "hw/sysbus.h"
 | 
					
						
							|  |  |  | #include "chardev/char.h"
 | 
					
						
							|  |  |  | #include "chardev/char-fe.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:48 +02:00
										 |  |  | #include "hw/hw.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:42 +02:00
										 |  |  | #include "hw/irq.h"
 | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | #include "hw/riscv/sifive_uart.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Not yet implemented: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Transmit FIFO using "qemu/fifo8.h" | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 00:19:12 +00:00
										 |  |  | /* Returns the state of the IP (interrupt pending) register */ | 
					
						
							|  |  |  | static uint64_t uart_ip(SiFiveUARTState *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint64_t ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); | 
					
						
							|  |  |  |     uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (txcnt != 0) { | 
					
						
							|  |  |  |         ret |= SIFIVE_UART_IP_TXWM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s->rx_fifo_len > rxcnt) { | 
					
						
							|  |  |  |         ret |= SIFIVE_UART_IP_RXWM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | static void update_irq(SiFiveUARTState *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int cond = 0; | 
					
						
							| 
									
										
										
										
											2019-03-17 01:03:10 -07:00
										 |  |  |     if ((s->ie & SIFIVE_UART_IE_TXWM) || | 
					
						
							|  |  |  |         ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  |         cond = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (cond) { | 
					
						
							|  |  |  |         qemu_irq_raise(s->irq); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         qemu_irq_lower(s->irq); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t | 
					
						
							|  |  |  | uart_read(void *opaque, hwaddr addr, unsigned int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = opaque; | 
					
						
							|  |  |  |     unsigned char r; | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case SIFIVE_UART_RXFIFO: | 
					
						
							|  |  |  |         if (s->rx_fifo_len) { | 
					
						
							|  |  |  |             r = s->rx_fifo[0]; | 
					
						
							|  |  |  |             memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1); | 
					
						
							|  |  |  |             s->rx_fifo_len--; | 
					
						
							|  |  |  |             qemu_chr_fe_accept_input(&s->chr); | 
					
						
							|  |  |  |             update_irq(s); | 
					
						
							|  |  |  |             return r; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return 0x80000000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case SIFIVE_UART_TXFIFO: | 
					
						
							|  |  |  |         return 0; /* Should check tx fifo */ | 
					
						
							|  |  |  |     case SIFIVE_UART_IE: | 
					
						
							|  |  |  |         return s->ie; | 
					
						
							|  |  |  |     case SIFIVE_UART_IP: | 
					
						
							| 
									
										
										
										
											2018-12-14 00:19:12 +00:00
										 |  |  |         return uart_ip(s); | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  |     case SIFIVE_UART_TXCTRL: | 
					
						
							|  |  |  |         return s->txctrl; | 
					
						
							|  |  |  |     case SIFIVE_UART_RXCTRL: | 
					
						
							|  |  |  |         return s->rxctrl; | 
					
						
							|  |  |  |     case SIFIVE_UART_DIV: | 
					
						
							|  |  |  |         return s->div; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 09:19:54 -07:00
										 |  |  |     qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", | 
					
						
							|  |  |  |                   __func__, (int)addr); | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | uart_write(void *opaque, hwaddr addr, | 
					
						
							|  |  |  |            uint64_t val64, unsigned int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = opaque; | 
					
						
							|  |  |  |     uint32_t value = val64; | 
					
						
							|  |  |  |     unsigned char ch = value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case SIFIVE_UART_TXFIFO: | 
					
						
							|  |  |  |         qemu_chr_fe_write(&s->chr, &ch, 1); | 
					
						
							| 
									
										
										
										
											2019-03-17 01:03:10 -07:00
										 |  |  |         update_irq(s); | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  |         return; | 
					
						
							|  |  |  |     case SIFIVE_UART_IE: | 
					
						
							|  |  |  |         s->ie = val64; | 
					
						
							|  |  |  |         update_irq(s); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case SIFIVE_UART_TXCTRL: | 
					
						
							|  |  |  |         s->txctrl = val64; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case SIFIVE_UART_RXCTRL: | 
					
						
							|  |  |  |         s->rxctrl = val64; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case SIFIVE_UART_DIV: | 
					
						
							|  |  |  |         s->div = val64; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-06 09:19:54 -07:00
										 |  |  |     qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", | 
					
						
							|  |  |  |                   __func__, (int)addr, (int)value); | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps uart_ops = { | 
					
						
							|  |  |  |     .read = uart_read, | 
					
						
							|  |  |  |     .write = uart_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void uart_rx(void *opaque, const uint8_t *buf, int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Got a byte.  */ | 
					
						
							|  |  |  |     if (s->rx_fifo_len >= sizeof(s->rx_fifo)) { | 
					
						
							|  |  |  |         printf("WARNING: UART dropped char.\n"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     s->rx_fifo[s->rx_fifo_len++] = *buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     update_irq(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int uart_can_rx(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return s->rx_fifo_len < sizeof(s->rx_fifo); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												chardev: Use QEMUChrEvent enum in IOEventHandler typedef
The Chardev events are listed in the QEMUChrEvent enum.
By using the enum in the IOEventHandler typedef we:
- make the IOEventHandler type more explicit (this handler
  process out-of-band information, while the IOReadHandler
  is in-band),
- help static code analyzers.
This patch was produced with the following spatch script:
  @match@
  expression backend, opaque, context, set_open;
  identifier fd_can_read, fd_read, fd_event, be_change;
  @@
  qemu_chr_fe_set_handlers(backend, fd_can_read, fd_read, fd_event,
                           be_change, opaque, context, set_open);
  @depends on match@
  identifier opaque, event;
  identifier match.fd_event;
  @@
   static
  -void fd_event(void *opaque, int event)
  +void fd_event(void *opaque, QEMUChrEvent event)
   {
   ...
   }
Then the typedef was modified manually in
include/chardev/char-fe.h.
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Corey Minyard <cminyard@mvista.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20191218172009.8868-15-philmd@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
											
										 
											2019-12-18 18:20:09 +01:00
										 |  |  | static void uart_event(void *opaque, QEMUChrEvent event) | 
					
						
							| 
									
										
										
										
											2018-03-03 01:31:14 +13:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int uart_be_change(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, | 
					
						
							|  |  |  |         uart_be_change, s, NULL, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Create UART device. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, | 
					
						
							|  |  |  |     Chardev *chr, qemu_irq irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState)); | 
					
						
							|  |  |  |     s->irq = irq; | 
					
						
							|  |  |  |     qemu_chr_fe_init(&s->chr, chr, &error_abort); | 
					
						
							|  |  |  |     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, | 
					
						
							|  |  |  |         uart_be_change, s, NULL, true); | 
					
						
							|  |  |  |     memory_region_init_io(&s->mmio, NULL, &uart_ops, s, | 
					
						
							|  |  |  |                           TYPE_SIFIVE_UART, SIFIVE_UART_MAX); | 
					
						
							|  |  |  |     memory_region_add_subregion(address_space, base, &s->mmio); | 
					
						
							|  |  |  |     return s; | 
					
						
							|  |  |  | } |