[RFC PATCH v2 04/14] hw/char: add TI AM64x UART model

Wadim Mueller <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
The AM64x UART is an 8250-compatible core with a TI-specific mode-select
register (MDR1) that has to be taken out of "disabled" state before the
UART transmits, plus a power/emulation management register. Wrap the
generic 16550 model to provide these.

Signed-off-by: Wadim Mueller <[email protected]>
---
 hw/char/Kconfig                |   4 ++
 hw/char/meson.build            |   1 +
 hw/char/ti-am64-uart.c         | 109 +++++++++++++++++++++++++++++++++
 include/hw/char/ti-am64-uart.h |  28 +++++++++
 4 files changed, 142 insertions(+)
 create mode 100644 hw/char/ti-am64-uart.c
 create mode 100644 include/hw/char/ti-am64-uart.h

diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 020c0a84bb..c06811130a 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -31,6 +31,10 @@ config SERIAL_MM
     bool
     select SERIAL
 
+config AM64_UART
+    bool
+    select SERIAL
+
 config SERIAL_PCI
     bool
     default y if PCI_DEVICES
diff --git a/hw/char/meson.build b/hw/char/meson.build
index fc3d7ee506..71c35af357 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -22,6 +22,7 @@ system_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c'))
 system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c'))
 system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c'))
 system_ss.add(when: 'CONFIG_DIVA_GSP', if_true: files('diva-gsp.c'))
+system_ss.add(when: 'CONFIG_AM64_UART', if_true: files('ti-am64-uart.c'))
 
 system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
 system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
diff --git a/hw/char/ti-am64-uart.c b/hw/char/ti-am64-uart.c
new file mode 100644
index 0000000000..41d02060eb
--- /dev/null
+++ b/hw/char/ti-am64-uart.c
@@ -0,0 +1,109 @@
+/*
+ * TI AM64x UART emulation
+ *
+ * Copyright (c) 2025 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/ti-am64-uart.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+
+static uint64_t am64_uart_read(void *opaque, hwaddr addr, unsigned size)
+{
+    AM64Uart *au = AM64_UART(opaque);
+
+    if (addr >= (8 << au->regshift)) {
+        return 0;
+    }
+
+    return serial_io_ops.read(&au->serial, addr >> au->regshift, 1);
+}
+
+static void am64_uart_write(void *opaque, hwaddr addr,
+                            uint64_t value, unsigned size)
+{
+    AM64Uart *au = AM64_UART(opaque);
+    value &= 255;
+    if (addr >= (8 << au->regshift)) {
+        return;
+    }
+
+    serial_io_ops.write(&au->serial, addr >> au->regshift, value, 1);
+}
+
+/*
+ * Registers are byte-wide and little-endian.  TI-specific registers outside
+ * the 16550 window are RAZ/WI; MDR1 == 0 means the 16x mode we implement.
+ */
+static const MemoryRegionOps am64_uart_ops = {
+    .read = am64_uart_read,
+    .write = am64_uart_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.max_access_size = 8,
+    .impl.max_access_size = 8,
+};
+
+static void am64_uart_realize(DeviceState *dev, Error **errp)
+{
+    AM64Uart *au = AM64_UART(dev);
+    SerialState *s = &au->serial;
+
+    if (!qdev_realize(DEVICE(s), NULL, errp)) {
+        return;
+    }
+
+    memory_region_init_io(&s->io, OBJECT(dev),
+                          &am64_uart_ops, au, "serial",
+                          64 << au->regshift);
+    sysbus_init_mmio(SYS_BUS_DEVICE(au), &s->io);
+    sysbus_init_irq(SYS_BUS_DEVICE(au), &au->serial.irq);
+}
+
+static const VMStateDescription vmstate_am64_uart = {
+    .name = "ti-am64-uart",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_STRUCT(serial, AM64Uart, 0, vmstate_serial, SerialState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void am64_uart_instance_init(Object *o)
+{
+    AM64Uart *au = AM64_UART(o);
+
+    object_initialize_child(o, "serial", &au->serial, TYPE_SERIAL);
+
+    qdev_alias_all_properties(DEVICE(&au->serial), o);
+}
+
+static const Property am64_uart_properties[] = {
+    /* AM64x has adjacent 16550 registers four bytes apart. */
+    DEFINE_PROP_UINT8("regshift", AM64Uart, regshift, 2),
+};
+
+static void am64_uart_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    device_class_set_props(dc, am64_uart_properties);
+    dc->realize = am64_uart_realize;
+    dc->vmsd = &vmstate_am64_uart;
+}
+
+static const TypeInfo types[] = {
+    {
+        .name = TYPE_AM64_UART,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .class_init = am64_uart_class_init,
+        .instance_init = am64_uart_instance_init,
+        .instance_size = sizeof(AM64Uart),
+    },
+};
+
+DEFINE_TYPES(types)
diff --git a/include/hw/char/ti-am64-uart.h b/include/hw/char/ti-am64-uart.h
new file mode 100644
index 0000000000..5cf820dc20
--- /dev/null
+++ b/include/hw/char/ti-am64-uart.h
@@ -0,0 +1,28 @@
+/*
+ * TI AM64x UART emulation
+ *
+ * Copyright (c) 2025 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_SERIAL_TI_AM64_H
+#define HW_SERIAL_TI_AM64_H
+
+#include "hw/char/serial.h"
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_AM64_UART "ti-am64-uart"
+OBJECT_DECLARE_SIMPLE_TYPE(AM64Uart, AM64_UART)
+
+struct AM64Uart {
+    SysBusDevice parent;
+
+    SerialState serial;
+
+    uint8_t regshift;
+};
+
+#endif
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.