On Fri, 2026-10-02 at 09:33 +1000, Portia Stephens wrote:
> The Atlantis SoC has 5 UARTs. This adds a machine property to set
> which uart is on the first serial interface. This is the same pattern
> that is used by aspeed. The rest of the uart's will be added in
> sequential order starting at serial interface 1.
>
> The default value is uart1 which means the Ascalon console will be on
> serial interface 0 by default.
>
> Signed-off-by: Portia Stephens <[email protected]>
> Reviewed-by: Daniel Henrique Barboza
> <[email protected]>
> ---
> hw/riscv/tt_atlantis.c | 77 ++++++++++++++++++++++++++++++--
> --
> include/hw/riscv/tt_atlantis.h | 1 +
> 2 files changed, 69 insertions(+), 9 deletions(-)
>
> diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
> index 2342edce93..7a5e1ddcd1 100644
> --- a/hw/riscv/tt_atlantis.c
> +++ b/hw/riscv/tt_atlantis.c
> @@ -12,6 +12,7 @@
> #include "qemu/error-report.h"
> #include "qemu/guest-random.h"
> #include "qemu/units.h"
> +#include "qapi/visitor.h"
>
> #include "hw/core/boards.h"
> #include "hw/core/loader.h"
> @@ -422,6 +423,26 @@ static void load_fdt(TTAtlantisState *ams)
> create_fdt_memory(ms->fdt, &ams->soc);
> }
>
> +static inline int tt_atlantis_uart_index(int uart_dev)
> +{
> + return uart_dev - TT_ATL_UART0;
> +}
> +
> +static void tt_atlantis_connect_serial_hds_to_uarts(TTAtlantisState
> *ams)
> +{
> + TTAtlantisSoCState *s = &ams->soc;
> + int uart_index = tt_atlantis_uart_index(ams->uart_chosen);
> +
> + qdev_prop_set_chr(DEVICE(&s->uart[uart_index]), "chardev",
> serial_hd(0));
> +
> + for (int i = 1, uart = 0; i < TT_ATL_NUM_UARTS; uart++) {
> + if (uart == uart_index) {
> + continue;
> + }
> + qdev_prop_set_chr(DEVICE(&s->uart[uart]), "chardev",
> serial_hd(i++));
> + }
I don't like this. We shouldn't dynamically change the command line API
based on a property
Alistair
> +}
> +
> static void tt_atlantis_machine_done(Notifier *n, void *data)
> {
> TTAtlantisState *ams = container_of(n, TTAtlantisState,
> machine_done);
> @@ -484,11 +505,6 @@ static void tt_atlantis_machine_done(Notifier
> *n, void *data)
> fdt_load_addr);
> }
>
> -static inline int tt_atlantis_uart_index(int uart_dev)
> -{
> - return uart_dev - TT_ATL_UART0;
> -}
> -
> static void tt_atlantis_soc_init(Object *obj)
> {
> TTAtlantisSoCState *s = TT_ATLANTIS_SOC(obj);
> @@ -592,10 +608,6 @@ static void tt_atlantis_soc_realize(DeviceState
> *dev, Error **errp)
> memory_region_add_subregion(s->memory, s-
> >memmap[TT_ATL_BOOTROM].base,
> &s->bootrom);
>
> - /* UART1, the soc console (UART0 is for the boot microcontroller
> */
> - qdev_prop_set_chr(DEVICE(&s-
> >uart[tt_atlantis_uart_index(TT_ATL_UART1)]),
> - "chardev", serial_hd(0));
> -
> for (int i = 0; i < TT_ATL_NUM_UARTS; i++) {
> SysBusDevice *sbd = SYS_BUS_DEVICE(&s->uart[i]);
> if (!sysbus_realize(sbd, errp)) {
> @@ -663,6 +675,10 @@ static void
> tt_atlantis_machine_init(MachineState *machine)
> OBJECT(&ams->soc_memory),
> &error_abort);
> object_property_set_link(OBJECT(&ams->soc), "dram",
> OBJECT(machine->ram),
> &error_abort);
> +
> + /* Map serial interfaces to uart */
> + tt_atlantis_connect_serial_hds_to_uarts(ams);
> +
> qdev_realize(DEVICE(&ams->soc), NULL, &error_fatal);
>
> /* I2C peripherals: qemu specific */
> @@ -680,6 +696,47 @@ static void
> tt_atlantis_machine_init(MachineState *machine)
> qemu_add_machine_init_done_notifier(&ams->machine_done);
> }
>
> +static void tt_atlantis_get_console(Object *obj, Visitor *v, const
> char *name,
> + void *opaque, Error **errp)
> +{
> + TTAtlantisState *ams = TT_ATLANTIS_MACHINE(obj);
> + uint32_t uart_index = tt_atlantis_uart_index(ams->uart_chosen);
> +
> + visit_type_uint32(v, name, &uart_index, errp);
> +}
> +
> +static void tt_atlantis_set_console(Object *obj, Visitor *v, const
> char *name,
> + void *opaque, Error **errp)
> +{
> + TTAtlantisState *ams = TT_ATLANTIS_MACHINE(obj);
> + uint32_t value;
> +
> + if (!visit_type_uint32(v, name, &value, errp)) {
> + return;
> + }
> +
> + if (value >= TT_ATL_NUM_UARTS) {
> + error_setg(errp, "\"console\" should be in range [%d - %d]",
> + 0, TT_ATL_NUM_UARTS - 1);
> + return;
> + }
> + ams->uart_chosen = value + TT_ATL_UART0;
> +}
> +
> +static void tt_atlantis_machine_class_props_init(ObjectClass *oc)
> +{
> + ObjectProperty *console;
> +
> + console = object_class_property_add(oc, "console", "uint32_t",
> + tt_atlantis_get_console,
> + tt_atlantis_set_console,
> NULL, NULL);
> + object_class_property_set_description(oc, "console",
> + "Change the default UART index on -serial. Valid values
> are 0 " \
> + "through 4");
> + /* Default to UART1, the Ascalon console */
> + object_property_set_default_uint(console, 1);
> +}
> +
> static void tt_atlantis_machine_class_init(ObjectClass *oc, const
> void *data)
> {
> MachineClass *mc = MACHINE_CLASS(oc);
> @@ -693,6 +750,8 @@ static void
> tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
> mc->block_default_type = IF_VIRTIO;
> mc->no_cdrom = 1;
> mc->default_ram_id = "tt_atlantis.ram";
> +
> + tt_atlantis_machine_class_props_init(oc);
> }
>
> static const TypeInfo tt_atlantis_types[] = {
> diff --git a/include/hw/riscv/tt_atlantis.h
> b/include/hw/riscv/tt_atlantis.h
> index b81b7c8abd..7bd46e9c9b 100644
> --- a/include/hw/riscv/tt_atlantis.h
> +++ b/include/hw/riscv/tt_atlantis.h
> @@ -56,6 +56,7 @@ struct TTAtlantisState {
>
> MemoryRegion soc_memory;
> TTAtlantisSoCState soc;
> + uint32_t uart_chosen;
> };
>
> enum {