18c6df16fSLaurent Vivier /* 265b4c8c7SPhilippe Mathieu-Daudé * SPDX-License-Identifier: GPL-2.0-or-later 38c6df16fSLaurent Vivier * 48c6df16fSLaurent Vivier * Goldfish TTY 58c6df16fSLaurent Vivier * 68c6df16fSLaurent Vivier * (c) 2020 Laurent Vivier <laurent@vivier.eu> 78c6df16fSLaurent Vivier * 88c6df16fSLaurent Vivier */ 98c6df16fSLaurent Vivier 108c6df16fSLaurent Vivier #include "qemu/osdep.h" 118c6df16fSLaurent Vivier #include "hw/irq.h" 128c6df16fSLaurent Vivier #include "hw/qdev-properties-system.h" 138c6df16fSLaurent Vivier #include "hw/sysbus.h" 148c6df16fSLaurent Vivier #include "migration/vmstate.h" 158c6df16fSLaurent Vivier #include "chardev/char-fe.h" 168c6df16fSLaurent Vivier #include "qemu/log.h" 178c6df16fSLaurent Vivier #include "trace.h" 188c6df16fSLaurent Vivier #include "exec/address-spaces.h" 19*32cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h" 208c6df16fSLaurent Vivier #include "hw/char/goldfish_tty.h" 218c6df16fSLaurent Vivier 228c6df16fSLaurent Vivier #define GOLDFISH_TTY_VERSION 1 238c6df16fSLaurent Vivier 248c6df16fSLaurent Vivier /* registers */ 258c6df16fSLaurent Vivier 268c6df16fSLaurent Vivier enum { 278c6df16fSLaurent Vivier REG_PUT_CHAR = 0x00, 288c6df16fSLaurent Vivier REG_BYTES_READY = 0x04, 298c6df16fSLaurent Vivier REG_CMD = 0x08, 308c6df16fSLaurent Vivier REG_DATA_PTR = 0x10, 318c6df16fSLaurent Vivier REG_DATA_LEN = 0x14, 328c6df16fSLaurent Vivier REG_DATA_PTR_HIGH = 0x18, 338c6df16fSLaurent Vivier REG_VERSION = 0x20, 348c6df16fSLaurent Vivier }; 358c6df16fSLaurent Vivier 368c6df16fSLaurent Vivier /* commands */ 378c6df16fSLaurent Vivier 388c6df16fSLaurent Vivier enum { 398c6df16fSLaurent Vivier CMD_INT_DISABLE = 0x00, 408c6df16fSLaurent Vivier CMD_INT_ENABLE = 0x01, 418c6df16fSLaurent Vivier CMD_WRITE_BUFFER = 0x02, 428c6df16fSLaurent Vivier CMD_READ_BUFFER = 0x03, 438c6df16fSLaurent Vivier }; 448c6df16fSLaurent Vivier 458c6df16fSLaurent Vivier static uint64_t goldfish_tty_read(void *opaque, hwaddr addr, 468c6df16fSLaurent Vivier unsigned size) 478c6df16fSLaurent Vivier { 488c6df16fSLaurent Vivier GoldfishTTYState *s = opaque; 498c6df16fSLaurent Vivier uint64_t value = 0; 508c6df16fSLaurent Vivier 518c6df16fSLaurent Vivier switch (addr) { 528c6df16fSLaurent Vivier case REG_BYTES_READY: 538c6df16fSLaurent Vivier value = fifo8_num_used(&s->rx_fifo); 548c6df16fSLaurent Vivier break; 558c6df16fSLaurent Vivier case REG_VERSION: 568c6df16fSLaurent Vivier value = GOLDFISH_TTY_VERSION; 578c6df16fSLaurent Vivier break; 588c6df16fSLaurent Vivier default: 598c6df16fSLaurent Vivier qemu_log_mask(LOG_UNIMP, 608c6df16fSLaurent Vivier "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n", 618c6df16fSLaurent Vivier __func__, addr); 628c6df16fSLaurent Vivier break; 638c6df16fSLaurent Vivier } 648c6df16fSLaurent Vivier 658c6df16fSLaurent Vivier trace_goldfish_tty_read(s, addr, size, value); 668c6df16fSLaurent Vivier 678c6df16fSLaurent Vivier return value; 688c6df16fSLaurent Vivier } 698c6df16fSLaurent Vivier 708c6df16fSLaurent Vivier static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) 718c6df16fSLaurent Vivier { 728c6df16fSLaurent Vivier uint32_t to_copy; 738c6df16fSLaurent Vivier uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE]; 748c6df16fSLaurent Vivier int len; 758c6df16fSLaurent Vivier uint64_t ptr; 768c6df16fSLaurent Vivier 778c6df16fSLaurent Vivier switch (cmd) { 788c6df16fSLaurent Vivier case CMD_INT_DISABLE: 798c6df16fSLaurent Vivier if (s->int_enabled) { 808c6df16fSLaurent Vivier if (!fifo8_is_empty(&s->rx_fifo)) { 818c6df16fSLaurent Vivier qemu_set_irq(s->irq, 0); 828c6df16fSLaurent Vivier } 838c6df16fSLaurent Vivier s->int_enabled = false; 848c6df16fSLaurent Vivier } 858c6df16fSLaurent Vivier break; 868c6df16fSLaurent Vivier case CMD_INT_ENABLE: 878c6df16fSLaurent Vivier if (!s->int_enabled) { 888c6df16fSLaurent Vivier if (!fifo8_is_empty(&s->rx_fifo)) { 898c6df16fSLaurent Vivier qemu_set_irq(s->irq, 1); 908c6df16fSLaurent Vivier } 918c6df16fSLaurent Vivier s->int_enabled = true; 928c6df16fSLaurent Vivier } 938c6df16fSLaurent Vivier break; 948c6df16fSLaurent Vivier case CMD_WRITE_BUFFER: 958c6df16fSLaurent Vivier len = s->data_len; 968c6df16fSLaurent Vivier ptr = s->data_ptr; 978c6df16fSLaurent Vivier while (len) { 988c6df16fSLaurent Vivier to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len); 998c6df16fSLaurent Vivier 100c9e0b9a5SPhilippe Mathieu-Daudé dma_memory_read_relaxed(&address_space_memory, ptr, 101c9e0b9a5SPhilippe Mathieu-Daudé data_out, to_copy); 1028c6df16fSLaurent Vivier qemu_chr_fe_write_all(&s->chr, data_out, to_copy); 1038c6df16fSLaurent Vivier 1048c6df16fSLaurent Vivier len -= to_copy; 1058c6df16fSLaurent Vivier ptr += to_copy; 1068c6df16fSLaurent Vivier } 1078c6df16fSLaurent Vivier break; 1088c6df16fSLaurent Vivier case CMD_READ_BUFFER: 1098c6df16fSLaurent Vivier len = s->data_len; 1108c6df16fSLaurent Vivier ptr = s->data_ptr; 1118c6df16fSLaurent Vivier while (len && !fifo8_is_empty(&s->rx_fifo)) { 11206252bf5SPhilippe Mathieu-Daudé const uint8_t *buf = fifo8_pop_bufptr(&s->rx_fifo, len, &to_copy); 113c9e0b9a5SPhilippe Mathieu-Daudé 114c9e0b9a5SPhilippe Mathieu-Daudé dma_memory_write_relaxed(&address_space_memory, ptr, buf, to_copy); 1158c6df16fSLaurent Vivier 1168c6df16fSLaurent Vivier len -= to_copy; 1178c6df16fSLaurent Vivier ptr += to_copy; 1188c6df16fSLaurent Vivier } 1198c6df16fSLaurent Vivier if (s->int_enabled && fifo8_is_empty(&s->rx_fifo)) { 1208c6df16fSLaurent Vivier qemu_set_irq(s->irq, 0); 1218c6df16fSLaurent Vivier } 1228c6df16fSLaurent Vivier break; 1238c6df16fSLaurent Vivier } 1248c6df16fSLaurent Vivier } 1258c6df16fSLaurent Vivier 1268c6df16fSLaurent Vivier static void goldfish_tty_write(void *opaque, hwaddr addr, 1278c6df16fSLaurent Vivier uint64_t value, unsigned size) 1288c6df16fSLaurent Vivier { 1298c6df16fSLaurent Vivier GoldfishTTYState *s = opaque; 1308c6df16fSLaurent Vivier unsigned char c; 1318c6df16fSLaurent Vivier 1328c6df16fSLaurent Vivier trace_goldfish_tty_write(s, addr, size, value); 1338c6df16fSLaurent Vivier 1348c6df16fSLaurent Vivier switch (addr) { 1358c6df16fSLaurent Vivier case REG_PUT_CHAR: 1368c6df16fSLaurent Vivier c = value; 1378c6df16fSLaurent Vivier qemu_chr_fe_write_all(&s->chr, &c, sizeof(c)); 1388c6df16fSLaurent Vivier break; 1398c6df16fSLaurent Vivier case REG_CMD: 1408c6df16fSLaurent Vivier goldfish_tty_cmd(s, value); 1418c6df16fSLaurent Vivier break; 1428c6df16fSLaurent Vivier case REG_DATA_PTR: 1438c6df16fSLaurent Vivier s->data_ptr = value; 1448c6df16fSLaurent Vivier break; 1458c6df16fSLaurent Vivier case REG_DATA_PTR_HIGH: 1468c6df16fSLaurent Vivier s->data_ptr = deposit64(s->data_ptr, 32, 32, value); 1478c6df16fSLaurent Vivier break; 1488c6df16fSLaurent Vivier case REG_DATA_LEN: 1498c6df16fSLaurent Vivier s->data_len = value; 1508c6df16fSLaurent Vivier break; 1518c6df16fSLaurent Vivier default: 1528c6df16fSLaurent Vivier qemu_log_mask(LOG_UNIMP, 1538c6df16fSLaurent Vivier "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n", 1548c6df16fSLaurent Vivier __func__, addr); 1558c6df16fSLaurent Vivier break; 1568c6df16fSLaurent Vivier } 1578c6df16fSLaurent Vivier } 1588c6df16fSLaurent Vivier 1598c6df16fSLaurent Vivier static const MemoryRegionOps goldfish_tty_ops = { 1608c6df16fSLaurent Vivier .read = goldfish_tty_read, 1618c6df16fSLaurent Vivier .write = goldfish_tty_write, 1628c6df16fSLaurent Vivier .endianness = DEVICE_NATIVE_ENDIAN, 1638c6df16fSLaurent Vivier .valid.max_access_size = 4, 1648c6df16fSLaurent Vivier .impl.max_access_size = 4, 1658c6df16fSLaurent Vivier .impl.min_access_size = 4, 1668c6df16fSLaurent Vivier }; 1678c6df16fSLaurent Vivier 1688c6df16fSLaurent Vivier static int goldfish_tty_can_receive(void *opaque) 1698c6df16fSLaurent Vivier { 1708c6df16fSLaurent Vivier GoldfishTTYState *s = opaque; 1718c6df16fSLaurent Vivier int available = fifo8_num_free(&s->rx_fifo); 1728c6df16fSLaurent Vivier 1738c6df16fSLaurent Vivier trace_goldfish_tty_can_receive(s, available); 1748c6df16fSLaurent Vivier 1758c6df16fSLaurent Vivier return available; 1768c6df16fSLaurent Vivier } 1778c6df16fSLaurent Vivier 1788c6df16fSLaurent Vivier static void goldfish_tty_receive(void *opaque, const uint8_t *buffer, int size) 1798c6df16fSLaurent Vivier { 1808c6df16fSLaurent Vivier GoldfishTTYState *s = opaque; 1818c6df16fSLaurent Vivier 1828c6df16fSLaurent Vivier trace_goldfish_tty_receive(s, size); 1838c6df16fSLaurent Vivier 1848c6df16fSLaurent Vivier g_assert(size <= fifo8_num_free(&s->rx_fifo)); 1858c6df16fSLaurent Vivier 1868c6df16fSLaurent Vivier fifo8_push_all(&s->rx_fifo, buffer, size); 1878c6df16fSLaurent Vivier 1888c6df16fSLaurent Vivier if (s->int_enabled && !fifo8_is_empty(&s->rx_fifo)) { 1898c6df16fSLaurent Vivier qemu_set_irq(s->irq, 1); 1908c6df16fSLaurent Vivier } 1918c6df16fSLaurent Vivier } 1928c6df16fSLaurent Vivier 1938c6df16fSLaurent Vivier static void goldfish_tty_reset(DeviceState *dev) 1948c6df16fSLaurent Vivier { 1958c6df16fSLaurent Vivier GoldfishTTYState *s = GOLDFISH_TTY(dev); 1968c6df16fSLaurent Vivier 1978c6df16fSLaurent Vivier trace_goldfish_tty_reset(s); 1988c6df16fSLaurent Vivier 1998c6df16fSLaurent Vivier fifo8_reset(&s->rx_fifo); 2008c6df16fSLaurent Vivier s->int_enabled = false; 2018c6df16fSLaurent Vivier s->data_ptr = 0; 2028c6df16fSLaurent Vivier s->data_len = 0; 2038c6df16fSLaurent Vivier } 2048c6df16fSLaurent Vivier 2058c6df16fSLaurent Vivier static void goldfish_tty_realize(DeviceState *dev, Error **errp) 2068c6df16fSLaurent Vivier { 2078c6df16fSLaurent Vivier GoldfishTTYState *s = GOLDFISH_TTY(dev); 2088c6df16fSLaurent Vivier 2098c6df16fSLaurent Vivier trace_goldfish_tty_realize(s); 2108c6df16fSLaurent Vivier 2118c6df16fSLaurent Vivier fifo8_create(&s->rx_fifo, GOLFISH_TTY_BUFFER_SIZE); 2128c6df16fSLaurent Vivier memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_tty_ops, s, 2138c6df16fSLaurent Vivier "goldfish_tty", 0x24); 2148c6df16fSLaurent Vivier 2158c6df16fSLaurent Vivier if (qemu_chr_fe_backend_connected(&s->chr)) { 2168c6df16fSLaurent Vivier qemu_chr_fe_set_handlers(&s->chr, goldfish_tty_can_receive, 2178c6df16fSLaurent Vivier goldfish_tty_receive, NULL, NULL, 2188c6df16fSLaurent Vivier s, NULL, true); 2198c6df16fSLaurent Vivier } 2208c6df16fSLaurent Vivier } 2218c6df16fSLaurent Vivier 2228c6df16fSLaurent Vivier static void goldfish_tty_unrealize(DeviceState *dev) 2238c6df16fSLaurent Vivier { 2248c6df16fSLaurent Vivier GoldfishTTYState *s = GOLDFISH_TTY(dev); 2258c6df16fSLaurent Vivier 2268c6df16fSLaurent Vivier trace_goldfish_tty_unrealize(s); 2278c6df16fSLaurent Vivier 2288c6df16fSLaurent Vivier fifo8_destroy(&s->rx_fifo); 2298c6df16fSLaurent Vivier } 2308c6df16fSLaurent Vivier 2318c6df16fSLaurent Vivier static const VMStateDescription vmstate_goldfish_tty = { 2328c6df16fSLaurent Vivier .name = "goldfish_tty", 2338c6df16fSLaurent Vivier .version_id = 1, 2348c6df16fSLaurent Vivier .minimum_version_id = 1, 2352f6cab05SRichard Henderson .fields = (const VMStateField[]) { 2368c6df16fSLaurent Vivier VMSTATE_UINT32(data_len, GoldfishTTYState), 2378c6df16fSLaurent Vivier VMSTATE_UINT64(data_ptr, GoldfishTTYState), 2388c6df16fSLaurent Vivier VMSTATE_BOOL(int_enabled, GoldfishTTYState), 2398c6df16fSLaurent Vivier VMSTATE_FIFO8(rx_fifo, GoldfishTTYState), 2408c6df16fSLaurent Vivier VMSTATE_END_OF_LIST() 2418c6df16fSLaurent Vivier } 2428c6df16fSLaurent Vivier }; 2438c6df16fSLaurent Vivier 244312f37d1SRichard Henderson static const Property goldfish_tty_properties[] = { 2458c6df16fSLaurent Vivier DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr), 2468c6df16fSLaurent Vivier DEFINE_PROP_END_OF_LIST(), 2478c6df16fSLaurent Vivier }; 2488c6df16fSLaurent Vivier 2498c6df16fSLaurent Vivier static void goldfish_tty_instance_init(Object *obj) 2508c6df16fSLaurent Vivier { 2518c6df16fSLaurent Vivier SysBusDevice *dev = SYS_BUS_DEVICE(obj); 2528c6df16fSLaurent Vivier GoldfishTTYState *s = GOLDFISH_TTY(obj); 2538c6df16fSLaurent Vivier 2548c6df16fSLaurent Vivier trace_goldfish_tty_instance_init(s); 2558c6df16fSLaurent Vivier 2568c6df16fSLaurent Vivier sysbus_init_mmio(dev, &s->iomem); 2578c6df16fSLaurent Vivier sysbus_init_irq(dev, &s->irq); 2588c6df16fSLaurent Vivier } 2598c6df16fSLaurent Vivier 2608c6df16fSLaurent Vivier static void goldfish_tty_class_init(ObjectClass *oc, void *data) 2618c6df16fSLaurent Vivier { 2628c6df16fSLaurent Vivier DeviceClass *dc = DEVICE_CLASS(oc); 2638c6df16fSLaurent Vivier 2648c6df16fSLaurent Vivier device_class_set_props(dc, goldfish_tty_properties); 265e3d08143SPeter Maydell device_class_set_legacy_reset(dc, goldfish_tty_reset); 2668c6df16fSLaurent Vivier dc->realize = goldfish_tty_realize; 2678c6df16fSLaurent Vivier dc->unrealize = goldfish_tty_unrealize; 2688c6df16fSLaurent Vivier dc->vmsd = &vmstate_goldfish_tty; 2698c6df16fSLaurent Vivier set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 2708c6df16fSLaurent Vivier } 2718c6df16fSLaurent Vivier 2728c6df16fSLaurent Vivier static const TypeInfo goldfish_tty_info = { 2738c6df16fSLaurent Vivier .name = TYPE_GOLDFISH_TTY, 2748c6df16fSLaurent Vivier .parent = TYPE_SYS_BUS_DEVICE, 2758c6df16fSLaurent Vivier .class_init = goldfish_tty_class_init, 2768c6df16fSLaurent Vivier .instance_init = goldfish_tty_instance_init, 2778c6df16fSLaurent Vivier .instance_size = sizeof(GoldfishTTYState), 2788c6df16fSLaurent Vivier }; 2798c6df16fSLaurent Vivier 2808c6df16fSLaurent Vivier static void goldfish_tty_register_types(void) 2818c6df16fSLaurent Vivier { 2828c6df16fSLaurent Vivier type_register_static(&goldfish_tty_info); 2838c6df16fSLaurent Vivier } 2848c6df16fSLaurent Vivier 2858c6df16fSLaurent Vivier type_init(goldfish_tty_register_types) 286