1 /* 2 * QEMU model of the Canon DIGIC UART block. 3 * 4 * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> 5 * 6 * This model is based on reverse engineering efforts 7 * made by CHDK (http://chdk.wikia.com) and 8 * Magic Lantern (http://www.magiclantern.fm) projects 9 * contributors. 10 * 11 * See "Serial terminal" docs here: 12 * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers 13 * 14 * The QEMU model of the Milkymist UART block by Michael Walle 15 * is used as a template. 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License as published by 19 * the Free Software Foundation; either version 2 of the License, or 20 * (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 */ 28 29 #include "qemu/osdep.h" 30 #include "hw/sysbus.h" 31 #include "migration/vmstate.h" 32 #include "chardev/char-fe.h" 33 #include "qemu/log.h" 34 #include "qemu/module.h" 35 36 #include "hw/char/digic-uart.h" 37 #include "hw/qdev-properties.h" 38 39 enum { 40 ST_RX_RDY = (1 << 0), 41 ST_TX_RDY = (1 << 1), 42 }; 43 44 static uint64_t digic_uart_read(void *opaque, hwaddr addr, 45 unsigned size) 46 { 47 DigicUartState *s = opaque; 48 uint64_t ret = 0; 49 50 addr >>= 2; 51 52 switch (addr) { 53 case R_RX: 54 s->reg_st &= ~(ST_RX_RDY); 55 ret = s->reg_rx; 56 break; 57 58 case R_ST: 59 ret = s->reg_st; 60 break; 61 62 default: 63 qemu_log_mask(LOG_UNIMP, 64 "digic-uart: read access to unknown register 0x" 65 TARGET_FMT_plx "\n", addr << 2); 66 } 67 68 return ret; 69 } 70 71 static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, 72 unsigned size) 73 { 74 DigicUartState *s = opaque; 75 unsigned char ch = value; 76 77 addr >>= 2; 78 79 switch (addr) { 80 case R_TX: 81 /* XXX this blocks entire thread. Rewrite to use 82 * qemu_chr_fe_write and background I/O callbacks */ 83 qemu_chr_fe_write_all(&s->chr, &ch, 1); 84 break; 85 86 case R_ST: 87 /* 88 * Ignore write to R_ST. 89 * 90 * The point is that this register is actively used 91 * during receiving and transmitting symbols, 92 * but we don't know the function of most of bits. 93 * 94 * Ignoring writes to R_ST is only a simplification 95 * of the model. It has no perceptible side effects 96 * for existing guests. 97 */ 98 break; 99 100 default: 101 qemu_log_mask(LOG_UNIMP, 102 "digic-uart: write access to unknown register 0x" 103 TARGET_FMT_plx "\n", addr << 2); 104 } 105 } 106 107 static const MemoryRegionOps uart_mmio_ops = { 108 .read = digic_uart_read, 109 .write = digic_uart_write, 110 .valid = { 111 .min_access_size = 4, 112 .max_access_size = 4, 113 }, 114 .endianness = DEVICE_NATIVE_ENDIAN, 115 }; 116 117 static int uart_can_rx(void *opaque) 118 { 119 DigicUartState *s = opaque; 120 121 return !(s->reg_st & ST_RX_RDY); 122 } 123 124 static void uart_rx(void *opaque, const uint8_t *buf, int size) 125 { 126 DigicUartState *s = opaque; 127 128 assert(uart_can_rx(opaque)); 129 130 s->reg_st |= ST_RX_RDY; 131 s->reg_rx = *buf; 132 } 133 134 static void uart_event(void *opaque, QEMUChrEvent event) 135 { 136 } 137 138 static void digic_uart_reset(DeviceState *d) 139 { 140 DigicUartState *s = DIGIC_UART(d); 141 142 s->reg_rx = 0; 143 s->reg_st = ST_TX_RDY; 144 } 145 146 static void digic_uart_realize(DeviceState *dev, Error **errp) 147 { 148 DigicUartState *s = DIGIC_UART(dev); 149 150 qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, 151 uart_event, NULL, s, NULL, true); 152 } 153 154 static void digic_uart_init(Object *obj) 155 { 156 DigicUartState *s = DIGIC_UART(obj); 157 158 memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, 159 TYPE_DIGIC_UART, 0x18); 160 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region); 161 } 162 163 static const VMStateDescription vmstate_digic_uart = { 164 .name = "digic-uart", 165 .version_id = 1, 166 .minimum_version_id = 1, 167 .fields = (VMStateField[]) { 168 VMSTATE_UINT32(reg_rx, DigicUartState), 169 VMSTATE_UINT32(reg_st, DigicUartState), 170 VMSTATE_END_OF_LIST() 171 } 172 }; 173 174 static Property digic_uart_properties[] = { 175 DEFINE_PROP_CHR("chardev", DigicUartState, chr), 176 DEFINE_PROP_END_OF_LIST(), 177 }; 178 179 static void digic_uart_class_init(ObjectClass *klass, void *data) 180 { 181 DeviceClass *dc = DEVICE_CLASS(klass); 182 183 dc->realize = digic_uart_realize; 184 dc->reset = digic_uart_reset; 185 dc->vmsd = &vmstate_digic_uart; 186 device_class_set_props(dc, digic_uart_properties); 187 } 188 189 static const TypeInfo digic_uart_info = { 190 .name = TYPE_DIGIC_UART, 191 .parent = TYPE_SYS_BUS_DEVICE, 192 .instance_size = sizeof(DigicUartState), 193 .instance_init = digic_uart_init, 194 .class_init = digic_uart_class_init, 195 }; 196 197 static void digic_uart_register_types(void) 198 { 199 type_register_static(&digic_uart_info); 200 } 201 202 type_init(digic_uart_register_types) 203