| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU model of Xilinx uartlite. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2009 Edgar E. Iglesias. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:28 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-04 15:40:22 +01:00
										 |  |  | #include "hw/sysbus.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-26 18:26:44 +04:00
										 |  |  | #include "chardev/char-fe.h"
 | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define DUART(x)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define R_RX            0
 | 
					
						
							|  |  |  | #define R_TX            1
 | 
					
						
							|  |  |  | #define R_STATUS        2
 | 
					
						
							|  |  |  | #define R_CTRL          3
 | 
					
						
							|  |  |  | #define R_MAX           4
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define STATUS_RXVALID    0x01
 | 
					
						
							|  |  |  | #define STATUS_RXFULL     0x02
 | 
					
						
							|  |  |  | #define STATUS_TXEMPTY    0x04
 | 
					
						
							|  |  |  | #define STATUS_TXFULL     0x08
 | 
					
						
							|  |  |  | #define STATUS_IE         0x10
 | 
					
						
							|  |  |  | #define STATUS_OVERRUN    0x20
 | 
					
						
							|  |  |  | #define STATUS_FRAME      0x40
 | 
					
						
							|  |  |  | #define STATUS_PARITY     0x80
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CONTROL_RST_TX    0x01
 | 
					
						
							|  |  |  | #define CONTROL_RST_RX    0x02
 | 
					
						
							|  |  |  | #define CONTROL_IE        0x10
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-24 23:37:28 +02:00
										 |  |  | #define TYPE_XILINX_UARTLITE "xlnx.xps-uartlite"
 | 
					
						
							|  |  |  | #define XILINX_UARTLITE(obj) \
 | 
					
						
							|  |  |  |     OBJECT_CHECK(XilinxUARTLite, (obj), TYPE_XILINX_UARTLITE) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  | typedef struct XilinxUARTLite { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:37:28 +02:00
										 |  |  |     SysBusDevice parent_obj; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-26 00:13:47 +02:00
										 |  |  |     MemoryRegion mmio; | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:51 +03:00
										 |  |  |     CharBackend chr; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  |     qemu_irq irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint8_t rx_fifo[8]; | 
					
						
							|  |  |  |     unsigned int rx_fifo_pos; | 
					
						
							|  |  |  |     unsigned int rx_fifo_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint32_t regs[R_MAX]; | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  | } XilinxUARTLite; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  | static void uart_update_irq(XilinxUARTLite *s) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     unsigned int irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->rx_fifo_len) | 
					
						
							|  |  |  |         s->regs[R_STATUS] |= STATUS_IE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE); | 
					
						
							|  |  |  |     qemu_set_irq(s->irq, irq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  | static void uart_update_status(XilinxUARTLite *s) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     uint32_t r; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r = s->regs[R_STATUS]; | 
					
						
							|  |  |  |     r &= ~7; | 
					
						
							|  |  |  |     r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */ | 
					
						
							|  |  |  |     r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1; | 
					
						
							|  |  |  |     r |= (!!s->rx_fifo_len); | 
					
						
							|  |  |  |     s->regs[R_STATUS] = r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:03 -07:00
										 |  |  | static void xilinx_uartlite_reset(DeviceState *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uart_update_status(XILINX_UARTLITE(dev)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-26 00:13:47 +02:00
										 |  |  | static uint64_t | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | uart_read(void *opaque, hwaddr addr, unsigned int size) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  |     XilinxUARTLite *s = opaque; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  |     uint32_t r = 0; | 
					
						
							|  |  |  |     addr >>= 2; | 
					
						
							|  |  |  |     switch (addr) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         case R_RX: | 
					
						
							|  |  |  |             r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7]; | 
					
						
							|  |  |  |             if (s->rx_fifo_len) | 
					
						
							|  |  |  |                 s->rx_fifo_len--; | 
					
						
							|  |  |  |             uart_update_status(s); | 
					
						
							|  |  |  |             uart_update_irq(s); | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:55 +03:00
										 |  |  |             qemu_chr_fe_accept_input(&s->chr); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             if (addr < ARRAY_SIZE(s->regs)) | 
					
						
							|  |  |  |                 r = s->regs[addr]; | 
					
						
							|  |  |  |             DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r)); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | uart_write(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-26 00:13:47 +02:00
										 |  |  |            uint64_t val64, unsigned int size) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  |     XilinxUARTLite *s = opaque; | 
					
						
							| 
									
										
										
										
											2011-08-26 00:13:47 +02:00
										 |  |  |     uint32_t value = val64; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  |     unsigned char ch = value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addr >>= 2; | 
					
						
							|  |  |  |     switch (addr) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         case R_STATUS: | 
					
						
							|  |  |  |             hw_error("write to UART STATUS?\n"); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case R_CTRL: | 
					
						
							|  |  |  |             if (value & CONTROL_RST_RX) { | 
					
						
							|  |  |  |                 s->rx_fifo_pos = 0; | 
					
						
							|  |  |  |                 s->rx_fifo_len = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             s->regs[addr] = value; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case R_TX: | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:59 +03:00
										 |  |  |             /* XXX this blocks entire thread. Rewrite to use
 | 
					
						
							|  |  |  |              * qemu_chr_fe_write and background I/O callbacks */ | 
					
						
							|  |  |  |             qemu_chr_fe_write_all(&s->chr, &ch, 1); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  |             s->regs[addr] = value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* hax.  */ | 
					
						
							|  |  |  |             s->regs[R_STATUS] |= STATUS_IE; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             DUART(printf("%s addr=%x v=%x\n", __func__, addr, value)); | 
					
						
							|  |  |  |             if (addr < ARRAY_SIZE(s->regs)) | 
					
						
							|  |  |  |                 s->regs[addr] = value; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     uart_update_status(s); | 
					
						
							|  |  |  |     uart_update_irq(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-26 00:13:47 +02:00
										 |  |  | static const MemoryRegionOps uart_ops = { | 
					
						
							|  |  |  |     .read = uart_read, | 
					
						
							|  |  |  |     .write = uart_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 1, | 
					
						
							|  |  |  |         .max_access_size = 4 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-06 16:59:32 +01:00
										 |  |  | static Property xilinx_uartlite_properties[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | static void uart_rx(void *opaque, const uint8_t *buf, int size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  |     XilinxUARTLite *s = opaque; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Got a byte.  */ | 
					
						
							|  |  |  |     if (s->rx_fifo_len >= 8) { | 
					
						
							|  |  |  |         printf("WARNING: UART dropped char.\n"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     s->rx_fifo[s->rx_fifo_pos] = *buf; | 
					
						
							|  |  |  |     s->rx_fifo_pos++; | 
					
						
							|  |  |  |     s->rx_fifo_pos &= 0x7; | 
					
						
							|  |  |  |     s->rx_fifo_len++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uart_update_status(s); | 
					
						
							|  |  |  |     uart_update_irq(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int uart_can_rx(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  |     XilinxUARTLite *s = opaque; | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-05 16:53:43 +10:00
										 |  |  |     return s->rx_fifo_len < sizeof(s->rx_fifo); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void uart_event(void *opaque, int event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:37 -07:00
										 |  |  | static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:37:28 +02:00
										 |  |  |     XilinxUARTLite *s = XILINX_UARTLITE(dev); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:59 +03:00
										 |  |  |     qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, | 
					
						
							| 
									
										
										
										
											2017-07-06 15:08:49 +03:00
										 |  |  |                              uart_event, NULL, s, NULL, true); | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:37 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void xilinx_uartlite_init(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XilinxUARTLite *s = XILINX_UARTLITE(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memory_region_init_io(&s->mmio, obj, &uart_ops, s, | 
					
						
							|  |  |  |                           "xlnx.xps-uartlite", R_MAX * 4); | 
					
						
							|  |  |  |     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:03 -07:00
										 |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:03 -07:00
										 |  |  |     dc->reset = xilinx_uartlite_reset; | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:37 -07:00
										 |  |  |     dc->realize = xilinx_uartlite_realize; | 
					
						
							| 
									
										
										
										
											2016-06-06 16:59:32 +01:00
										 |  |  |     dc->props = xilinx_uartlite_properties; | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-10 16:19:07 +01:00
										 |  |  | static const TypeInfo xilinx_uartlite_info = { | 
					
						
							| 
									
										
										
										
											2013-07-24 23:37:28 +02:00
										 |  |  |     .name          = TYPE_XILINX_UARTLITE, | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							| 
									
										
										
										
											2013-07-24 23:34:25 +02:00
										 |  |  |     .instance_size = sizeof(XilinxUARTLite), | 
					
						
							| 
									
										
										
										
											2014-05-29 02:25:37 -07:00
										 |  |  |     .instance_init = xilinx_uartlite_init, | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .class_init    = xilinx_uartlite_class_init, | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | static void xilinx_uart_register_types(void) | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     type_register_static(&xilinx_uartlite_info); | 
					
						
							| 
									
										
										
										
											2009-05-20 20:12:42 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | type_init(xilinx_uart_register_types) |