1*d447e4b7SJackson Donaldson /* 2*d447e4b7SJackson Donaldson * MAX78000 UART 3*d447e4b7SJackson Donaldson * 4*d447e4b7SJackson Donaldson * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com> 5*d447e4b7SJackson Donaldson * 6*d447e4b7SJackson Donaldson * SPDX-License-Identifier: GPL-2.0-or-later 7*d447e4b7SJackson Donaldson */ 8*d447e4b7SJackson Donaldson 9*d447e4b7SJackson Donaldson #include "qemu/osdep.h" 10*d447e4b7SJackson Donaldson #include "hw/char/max78000_uart.h" 11*d447e4b7SJackson Donaldson #include "hw/irq.h" 12*d447e4b7SJackson Donaldson #include "hw/qdev-properties.h" 13*d447e4b7SJackson Donaldson #include "hw/qdev-properties-system.h" 14*d447e4b7SJackson Donaldson #include "qemu/log.h" 15*d447e4b7SJackson Donaldson #include "qemu/module.h" 16*d447e4b7SJackson Donaldson #include "migration/vmstate.h" 17*d447e4b7SJackson Donaldson #include "trace.h" 18*d447e4b7SJackson Donaldson 19*d447e4b7SJackson Donaldson 20*d447e4b7SJackson Donaldson static int max78000_uart_can_receive(void *opaque) 21*d447e4b7SJackson Donaldson { 22*d447e4b7SJackson Donaldson Max78000UartState *s = opaque; 23*d447e4b7SJackson Donaldson if (!(s->ctrl & UART_BCLKEN)) { 24*d447e4b7SJackson Donaldson return 0; 25*d447e4b7SJackson Donaldson } 26*d447e4b7SJackson Donaldson return fifo8_num_free(&s->rx_fifo); 27*d447e4b7SJackson Donaldson } 28*d447e4b7SJackson Donaldson 29*d447e4b7SJackson Donaldson static void max78000_update_irq(Max78000UartState *s) 30*d447e4b7SJackson Donaldson { 31*d447e4b7SJackson Donaldson int interrupt_level; 32*d447e4b7SJackson Donaldson 33*d447e4b7SJackson Donaldson interrupt_level = s->int_fl & s->int_en; 34*d447e4b7SJackson Donaldson qemu_set_irq(s->irq, interrupt_level); 35*d447e4b7SJackson Donaldson } 36*d447e4b7SJackson Donaldson 37*d447e4b7SJackson Donaldson static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size) 38*d447e4b7SJackson Donaldson { 39*d447e4b7SJackson Donaldson Max78000UartState *s = opaque; 40*d447e4b7SJackson Donaldson 41*d447e4b7SJackson Donaldson assert(size <= fifo8_num_free(&s->rx_fifo)); 42*d447e4b7SJackson Donaldson 43*d447e4b7SJackson Donaldson fifo8_push_all(&s->rx_fifo, buf, size); 44*d447e4b7SJackson Donaldson 45*d447e4b7SJackson Donaldson uint32_t rx_threshold = s->ctrl & 0xf; 46*d447e4b7SJackson Donaldson 47*d447e4b7SJackson Donaldson if (fifo8_num_used(&s->rx_fifo) >= rx_threshold) { 48*d447e4b7SJackson Donaldson s->int_fl |= UART_RX_THD; 49*d447e4b7SJackson Donaldson } 50*d447e4b7SJackson Donaldson 51*d447e4b7SJackson Donaldson max78000_update_irq(s); 52*d447e4b7SJackson Donaldson } 53*d447e4b7SJackson Donaldson 54*d447e4b7SJackson Donaldson static void max78000_uart_reset_hold(Object *obj, ResetType type) 55*d447e4b7SJackson Donaldson { 56*d447e4b7SJackson Donaldson Max78000UartState *s = MAX78000_UART(obj); 57*d447e4b7SJackson Donaldson 58*d447e4b7SJackson Donaldson s->ctrl = 0; 59*d447e4b7SJackson Donaldson s->status = UART_TX_EM | UART_RX_EM; 60*d447e4b7SJackson Donaldson s->int_en = 0; 61*d447e4b7SJackson Donaldson s->int_fl = 0; 62*d447e4b7SJackson Donaldson s->osr = 0; 63*d447e4b7SJackson Donaldson s->txpeek = 0; 64*d447e4b7SJackson Donaldson s->pnr = UART_RTS; 65*d447e4b7SJackson Donaldson s->fifo = 0; 66*d447e4b7SJackson Donaldson s->dma = 0; 67*d447e4b7SJackson Donaldson s->wken = 0; 68*d447e4b7SJackson Donaldson s->wkfl = 0; 69*d447e4b7SJackson Donaldson fifo8_reset(&s->rx_fifo); 70*d447e4b7SJackson Donaldson } 71*d447e4b7SJackson Donaldson 72*d447e4b7SJackson Donaldson static uint64_t max78000_uart_read(void *opaque, hwaddr addr, 73*d447e4b7SJackson Donaldson unsigned int size) 74*d447e4b7SJackson Donaldson { 75*d447e4b7SJackson Donaldson Max78000UartState *s = opaque; 76*d447e4b7SJackson Donaldson uint64_t retvalue = 0; 77*d447e4b7SJackson Donaldson switch (addr) { 78*d447e4b7SJackson Donaldson case UART_CTRL: 79*d447e4b7SJackson Donaldson retvalue = s->ctrl; 80*d447e4b7SJackson Donaldson break; 81*d447e4b7SJackson Donaldson case UART_STATUS: 82*d447e4b7SJackson Donaldson retvalue = (fifo8_num_used(&s->rx_fifo) << UART_RX_LVL) | 83*d447e4b7SJackson Donaldson UART_TX_EM | 84*d447e4b7SJackson Donaldson (fifo8_is_empty(&s->rx_fifo) ? UART_RX_EM : 0); 85*d447e4b7SJackson Donaldson break; 86*d447e4b7SJackson Donaldson case UART_INT_EN: 87*d447e4b7SJackson Donaldson retvalue = s->int_en; 88*d447e4b7SJackson Donaldson break; 89*d447e4b7SJackson Donaldson case UART_INT_FL: 90*d447e4b7SJackson Donaldson retvalue = s->int_fl; 91*d447e4b7SJackson Donaldson break; 92*d447e4b7SJackson Donaldson case UART_CLKDIV: 93*d447e4b7SJackson Donaldson retvalue = s->clkdiv; 94*d447e4b7SJackson Donaldson break; 95*d447e4b7SJackson Donaldson case UART_OSR: 96*d447e4b7SJackson Donaldson retvalue = s->osr; 97*d447e4b7SJackson Donaldson break; 98*d447e4b7SJackson Donaldson case UART_TXPEEK: 99*d447e4b7SJackson Donaldson if (!fifo8_is_empty(&s->rx_fifo)) { 100*d447e4b7SJackson Donaldson retvalue = fifo8_peek(&s->rx_fifo); 101*d447e4b7SJackson Donaldson } 102*d447e4b7SJackson Donaldson break; 103*d447e4b7SJackson Donaldson case UART_PNR: 104*d447e4b7SJackson Donaldson retvalue = s->pnr; 105*d447e4b7SJackson Donaldson break; 106*d447e4b7SJackson Donaldson case UART_FIFO: 107*d447e4b7SJackson Donaldson if (!fifo8_is_empty(&s->rx_fifo)) { 108*d447e4b7SJackson Donaldson retvalue = fifo8_pop(&s->rx_fifo); 109*d447e4b7SJackson Donaldson max78000_update_irq(s); 110*d447e4b7SJackson Donaldson } 111*d447e4b7SJackson Donaldson break; 112*d447e4b7SJackson Donaldson case UART_DMA: 113*d447e4b7SJackson Donaldson /* DMA not implemented */ 114*d447e4b7SJackson Donaldson retvalue = s->dma; 115*d447e4b7SJackson Donaldson break; 116*d447e4b7SJackson Donaldson case UART_WKEN: 117*d447e4b7SJackson Donaldson retvalue = s->wken; 118*d447e4b7SJackson Donaldson break; 119*d447e4b7SJackson Donaldson case UART_WKFL: 120*d447e4b7SJackson Donaldson retvalue = s->wkfl; 121*d447e4b7SJackson Donaldson break; 122*d447e4b7SJackson Donaldson default: 123*d447e4b7SJackson Donaldson qemu_log_mask(LOG_GUEST_ERROR, 124*d447e4b7SJackson Donaldson "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); 125*d447e4b7SJackson Donaldson break; 126*d447e4b7SJackson Donaldson } 127*d447e4b7SJackson Donaldson 128*d447e4b7SJackson Donaldson return retvalue; 129*d447e4b7SJackson Donaldson } 130*d447e4b7SJackson Donaldson 131*d447e4b7SJackson Donaldson static void max78000_uart_write(void *opaque, hwaddr addr, 132*d447e4b7SJackson Donaldson uint64_t val64, unsigned int size) 133*d447e4b7SJackson Donaldson { 134*d447e4b7SJackson Donaldson Max78000UartState *s = opaque; 135*d447e4b7SJackson Donaldson 136*d447e4b7SJackson Donaldson uint32_t value = val64; 137*d447e4b7SJackson Donaldson uint8_t data; 138*d447e4b7SJackson Donaldson 139*d447e4b7SJackson Donaldson switch (addr) { 140*d447e4b7SJackson Donaldson case UART_CTRL: 141*d447e4b7SJackson Donaldson if (value & UART_FLUSH_RX) { 142*d447e4b7SJackson Donaldson fifo8_reset(&s->rx_fifo); 143*d447e4b7SJackson Donaldson } 144*d447e4b7SJackson Donaldson if (value & UART_BCLKEN) { 145*d447e4b7SJackson Donaldson value = value | UART_BCLKRDY; 146*d447e4b7SJackson Donaldson } 147*d447e4b7SJackson Donaldson s->ctrl = value & ~(UART_FLUSH_RX | UART_FLUSH_TX); 148*d447e4b7SJackson Donaldson 149*d447e4b7SJackson Donaldson /* 150*d447e4b7SJackson Donaldson * Software can manage UART flow control manually by setting hfc_en 151*d447e4b7SJackson Donaldson * in UART_CTRL. This would require emulating uart at a lower level, 152*d447e4b7SJackson Donaldson * and is currently unimplemented. 153*d447e4b7SJackson Donaldson */ 154*d447e4b7SJackson Donaldson 155*d447e4b7SJackson Donaldson return; 156*d447e4b7SJackson Donaldson case UART_STATUS: 157*d447e4b7SJackson Donaldson /* UART_STATUS is read only */ 158*d447e4b7SJackson Donaldson return; 159*d447e4b7SJackson Donaldson case UART_INT_EN: 160*d447e4b7SJackson Donaldson s->int_en = value; 161*d447e4b7SJackson Donaldson return; 162*d447e4b7SJackson Donaldson case UART_INT_FL: 163*d447e4b7SJackson Donaldson s->int_fl = s->int_fl & ~(value); 164*d447e4b7SJackson Donaldson max78000_update_irq(s); 165*d447e4b7SJackson Donaldson return; 166*d447e4b7SJackson Donaldson case UART_CLKDIV: 167*d447e4b7SJackson Donaldson s->clkdiv = value; 168*d447e4b7SJackson Donaldson return; 169*d447e4b7SJackson Donaldson case UART_OSR: 170*d447e4b7SJackson Donaldson s->osr = value; 171*d447e4b7SJackson Donaldson return; 172*d447e4b7SJackson Donaldson case UART_PNR: 173*d447e4b7SJackson Donaldson s->pnr = value; 174*d447e4b7SJackson Donaldson return; 175*d447e4b7SJackson Donaldson case UART_FIFO: 176*d447e4b7SJackson Donaldson data = value & 0xff; 177*d447e4b7SJackson Donaldson /* 178*d447e4b7SJackson Donaldson * XXX this blocks entire thread. Rewrite to use 179*d447e4b7SJackson Donaldson * qemu_chr_fe_write and background I/O callbacks 180*d447e4b7SJackson Donaldson */ 181*d447e4b7SJackson Donaldson qemu_chr_fe_write_all(&s->chr, &data, 1); 182*d447e4b7SJackson Donaldson 183*d447e4b7SJackson Donaldson /* TX is always empty */ 184*d447e4b7SJackson Donaldson s->int_fl |= UART_TX_HE; 185*d447e4b7SJackson Donaldson max78000_update_irq(s); 186*d447e4b7SJackson Donaldson 187*d447e4b7SJackson Donaldson return; 188*d447e4b7SJackson Donaldson case UART_DMA: 189*d447e4b7SJackson Donaldson /* DMA not implemented */ 190*d447e4b7SJackson Donaldson s->dma = value; 191*d447e4b7SJackson Donaldson return; 192*d447e4b7SJackson Donaldson case UART_WKEN: 193*d447e4b7SJackson Donaldson s->wken = value; 194*d447e4b7SJackson Donaldson return; 195*d447e4b7SJackson Donaldson case UART_WKFL: 196*d447e4b7SJackson Donaldson s->wkfl = value; 197*d447e4b7SJackson Donaldson return; 198*d447e4b7SJackson Donaldson default: 199*d447e4b7SJackson Donaldson qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" 200*d447e4b7SJackson Donaldson HWADDR_PRIx "\n", __func__, addr); 201*d447e4b7SJackson Donaldson } 202*d447e4b7SJackson Donaldson } 203*d447e4b7SJackson Donaldson 204*d447e4b7SJackson Donaldson static const MemoryRegionOps max78000_uart_ops = { 205*d447e4b7SJackson Donaldson .read = max78000_uart_read, 206*d447e4b7SJackson Donaldson .write = max78000_uart_write, 207*d447e4b7SJackson Donaldson .endianness = DEVICE_LITTLE_ENDIAN, 208*d447e4b7SJackson Donaldson .valid.min_access_size = 4, 209*d447e4b7SJackson Donaldson .valid.max_access_size = 4, 210*d447e4b7SJackson Donaldson }; 211*d447e4b7SJackson Donaldson 212*d447e4b7SJackson Donaldson static const Property max78000_uart_properties[] = { 213*d447e4b7SJackson Donaldson DEFINE_PROP_CHR("chardev", Max78000UartState, chr), 214*d447e4b7SJackson Donaldson }; 215*d447e4b7SJackson Donaldson 216*d447e4b7SJackson Donaldson static const VMStateDescription max78000_uart_vmstate = { 217*d447e4b7SJackson Donaldson .name = TYPE_MAX78000_UART, 218*d447e4b7SJackson Donaldson .version_id = 1, 219*d447e4b7SJackson Donaldson .minimum_version_id = 1, 220*d447e4b7SJackson Donaldson .fields = (VMStateField[]) { 221*d447e4b7SJackson Donaldson VMSTATE_UINT32(ctrl, Max78000UartState), 222*d447e4b7SJackson Donaldson VMSTATE_UINT32(status, Max78000UartState), 223*d447e4b7SJackson Donaldson VMSTATE_UINT32(int_en, Max78000UartState), 224*d447e4b7SJackson Donaldson VMSTATE_UINT32(int_fl, Max78000UartState), 225*d447e4b7SJackson Donaldson VMSTATE_UINT32(clkdiv, Max78000UartState), 226*d447e4b7SJackson Donaldson VMSTATE_UINT32(osr, Max78000UartState), 227*d447e4b7SJackson Donaldson VMSTATE_UINT32(txpeek, Max78000UartState), 228*d447e4b7SJackson Donaldson VMSTATE_UINT32(pnr, Max78000UartState), 229*d447e4b7SJackson Donaldson VMSTATE_UINT32(fifo, Max78000UartState), 230*d447e4b7SJackson Donaldson VMSTATE_UINT32(dma, Max78000UartState), 231*d447e4b7SJackson Donaldson VMSTATE_UINT32(wken, Max78000UartState), 232*d447e4b7SJackson Donaldson VMSTATE_UINT32(wkfl, Max78000UartState), 233*d447e4b7SJackson Donaldson VMSTATE_FIFO8(rx_fifo, Max78000UartState), 234*d447e4b7SJackson Donaldson VMSTATE_END_OF_LIST() 235*d447e4b7SJackson Donaldson } 236*d447e4b7SJackson Donaldson }; 237*d447e4b7SJackson Donaldson 238*d447e4b7SJackson Donaldson static void max78000_uart_init(Object *obj) 239*d447e4b7SJackson Donaldson { 240*d447e4b7SJackson Donaldson Max78000UartState *s = MAX78000_UART(obj); 241*d447e4b7SJackson Donaldson fifo8_create(&s->rx_fifo, 8); 242*d447e4b7SJackson Donaldson 243*d447e4b7SJackson Donaldson sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); 244*d447e4b7SJackson Donaldson 245*d447e4b7SJackson Donaldson memory_region_init_io(&s->mmio, obj, &max78000_uart_ops, s, 246*d447e4b7SJackson Donaldson TYPE_MAX78000_UART, 0x400); 247*d447e4b7SJackson Donaldson sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 248*d447e4b7SJackson Donaldson } 249*d447e4b7SJackson Donaldson 250*d447e4b7SJackson Donaldson static void max78000_uart_realize(DeviceState *dev, Error **errp) 251*d447e4b7SJackson Donaldson { 252*d447e4b7SJackson Donaldson Max78000UartState *s = MAX78000_UART(dev); 253*d447e4b7SJackson Donaldson 254*d447e4b7SJackson Donaldson qemu_chr_fe_set_handlers(&s->chr, max78000_uart_can_receive, 255*d447e4b7SJackson Donaldson max78000_uart_receive, NULL, NULL, 256*d447e4b7SJackson Donaldson s, NULL, true); 257*d447e4b7SJackson Donaldson } 258*d447e4b7SJackson Donaldson 259*d447e4b7SJackson Donaldson static void max78000_uart_class_init(ObjectClass *klass, const void *data) 260*d447e4b7SJackson Donaldson { 261*d447e4b7SJackson Donaldson DeviceClass *dc = DEVICE_CLASS(klass); 262*d447e4b7SJackson Donaldson ResettableClass *rc = RESETTABLE_CLASS(klass); 263*d447e4b7SJackson Donaldson 264*d447e4b7SJackson Donaldson rc->phases.hold = max78000_uart_reset_hold; 265*d447e4b7SJackson Donaldson 266*d447e4b7SJackson Donaldson device_class_set_props(dc, max78000_uart_properties); 267*d447e4b7SJackson Donaldson dc->realize = max78000_uart_realize; 268*d447e4b7SJackson Donaldson 269*d447e4b7SJackson Donaldson dc->vmsd = &max78000_uart_vmstate; 270*d447e4b7SJackson Donaldson } 271*d447e4b7SJackson Donaldson 272*d447e4b7SJackson Donaldson static const TypeInfo max78000_uart_info = { 273*d447e4b7SJackson Donaldson .name = TYPE_MAX78000_UART, 274*d447e4b7SJackson Donaldson .parent = TYPE_SYS_BUS_DEVICE, 275*d447e4b7SJackson Donaldson .instance_size = sizeof(Max78000UartState), 276*d447e4b7SJackson Donaldson .instance_init = max78000_uart_init, 277*d447e4b7SJackson Donaldson .class_init = max78000_uart_class_init, 278*d447e4b7SJackson Donaldson }; 279*d447e4b7SJackson Donaldson 280*d447e4b7SJackson Donaldson static void max78000_uart_register_types(void) 281*d447e4b7SJackson Donaldson { 282*d447e4b7SJackson Donaldson type_register_static(&max78000_uart_info); 283*d447e4b7SJackson Donaldson } 284*d447e4b7SJackson Donaldson 285*d447e4b7SJackson Donaldson type_init(max78000_uart_register_types) 286