Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 71 additions & 23 deletions hw/arm/rk3588.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ enum {
RK3588_IRAM,
RK3588_BROM,
RK3588_UART2,
RK3588_UART3,
};

static const MemMapEntry rk3588_memmap[] = {
Expand Down Expand Up @@ -386,6 +387,7 @@ static const MemMapEntry rk3588_memmap[] = {
[RK3588_IRAM] = { 0xff000000, RK3588_IRAM_SIZE },
[RK3588_BROM] = { RK3588_BROM_TRAMPOLINE, 0x00001000 },
[RK3588_UART2] = { 0xfeb50000, 0x00000100 },
[RK3588_UART3] = { 0xfeb60000, 0x00000100 },
};

static hwaddr rk3588_ram_base(const RK3588MachineState *s)
Expand Down Expand Up @@ -416,6 +418,7 @@ enum {
RK3588_PCIE3X4_SYS_SPI = 263,
RK3588_GPIO0_SPI = 277,
RK3588_UART2_SPI = 333,
RK3588_UART3_SPI = 334,
};

static const char *rk3588_cpu_type(unsigned int n)
Expand Down Expand Up @@ -517,36 +520,46 @@ static void rk3588_fdt_add_timer_node(void *fdt)
qemu_fdt_setprop(fdt, timer, "always-on", NULL, 0);
}

static void rk3588_fdt_add_uart_node(void *fdt)
static void rk3588_fdt_add_one_uart_node(void *fdt, const char *uart,
int memmap_idx, uint32_t spi)
{
const char *uart = "/serial@feb50000";
static const char * const compat[] = {
"rockchip,rk3588-uart",
"snps,dw-apb-uart",
"ns16550a",
};

qemu_fdt_add_subnode(fdt, "/aliases");
qemu_fdt_setprop_string(fdt, "/aliases", "serial2", uart);

qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "serial2:1500000n8");

qemu_fdt_add_subnode(fdt, uart);
qemu_fdt_setprop_string_array(fdt, uart, "compatible",
(char **)&compat, ARRAY_SIZE(compat));
qemu_fdt_setprop_sized_cells(fdt, uart, "reg",
2, rk3588_memmap[RK3588_UART2].base,
2, rk3588_memmap[RK3588_UART2].size);
2, rk3588_memmap[memmap_idx].base,
2, rk3588_memmap[memmap_idx].size);
qemu_fdt_setprop_cells(fdt, uart, "interrupts",
FDT_GIC_SPI, RK3588_UART2_SPI,
FDT_GIC_SPI, spi,
FDT_IRQ_TYPE_LEVEL_HIGH, 0);
qemu_fdt_setprop_cell(fdt, uart, "clock-frequency", RK3588_GTIMER_HZ);
qemu_fdt_setprop_cell(fdt, uart, "current-speed", RK3588_UART_BAUDBASE);
qemu_fdt_setprop_cell(fdt, uart, "reg-shift", 2);
qemu_fdt_setprop_cell(fdt, uart, "reg-io-width", 4);
qemu_fdt_setprop_string(fdt, uart, "status", "okay");
}

static void rk3588_fdt_add_uart_node(void *fdt)
{
const char *uart2 = "/serial@feb50000";
const char *uart3 = "/serial@feb60000";

qemu_fdt_add_subnode(fdt, "/aliases");
qemu_fdt_setprop_string(fdt, "/aliases", "serial2", uart2);
qemu_fdt_setprop_string(fdt, "/aliases", "serial3", uart3);

qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "serial2:1500000n8");

rk3588_fdt_add_one_uart_node(fdt, uart2, RK3588_UART2, RK3588_UART2_SPI);
rk3588_fdt_add_one_uart_node(fdt, uart3, RK3588_UART3, RK3588_UART3_SPI);
}
static uint32_t rk3588_fdt_add_fixed_clock_node(void *fdt)
{
const char *clk = "/xin24m";
Expand Down Expand Up @@ -2494,31 +2507,45 @@ static void rk3588_create_its(RK3588MachineState *s)
}
}

static void rk3588_create_uart(RK3588MachineState *s)
static void rk3588_create_one_uart(RK3588MachineState *s, int memmap_idx,
int spi, int serial_idx,
const char *vendor_name)
{
DeviceState *vendor;
SysBusDevice *vendor_sbd;

/*
* UART2 is a Synopsys dw-apb-uart (16550-compatible). serial_mm models the
* standard 16550 range (8 registers, regshift 2 -> a 0x20-byte window). The
* DesignWare extension registers (USR @0x7c, DMASA, CPR/UCV/CTR) sit
* above that window. Cover only that range so it does not overlap serial_mm.
*/
serial_mm_init(get_system_memory(), rk3588_memmap[RK3588_UART2].base, 2,
qdev_get_gpio_in(s->gic, RK3588_UART2_SPI),
serial_mm_init(get_system_memory(), rk3588_memmap[memmap_idx].base, 2,
qdev_get_gpio_in(s->gic, spi),
RK3588_UART_BAUDBASE,
serial_hd(s->zephyr_ram ? 1 : 0), DEVICE_LITTLE_ENDIAN);
serial_hd(serial_idx), DEVICE_LITTLE_ENDIAN);

vendor = qdev_new(TYPE_DW_APB_UART_VENDOR);
vendor_sbd = SYS_BUS_DEVICE(vendor);
object_property_add_child(OBJECT(s), "uart2-vendor", OBJECT(vendor));
object_property_add_child(OBJECT(s), vendor_name, OBJECT(vendor));
sysbus_realize(vendor_sbd, &error_fatal);
sysbus_mmio_map(vendor_sbd, 0,
rk3588_memmap[RK3588_UART2].base +
rk3588_memmap[memmap_idx].base +
DW_APB_UART_VENDOR_BASE);
}

static void rk3588_create_uart(RK3588MachineState *s)
{
/*
* UART2/UART3 are Synopsys dw-apb-uart (16550-compatible). serial_mm
* models the standard 16550 range (8 registers, regshift 2 -> a 0x20-byte
* window). The DesignWare extension registers (USR @0x7c, DMASA,
* CPR/UCV/CTR) sit above that window. Cover only that range so it does
* not overlap serial_mm.
*
* In zephyr-ram mode serial_hd(0) is the DWC3 UDC CDC bridge and
* serial_hd(1) is the UART2 console, so UART3 takes serial_hd(2).
*/
rk3588_create_one_uart(s, RK3588_UART2, RK3588_UART2_SPI,
s->zephyr_ram ? 1 : 0, "uart2-vendor");
rk3588_create_one_uart(s, RK3588_UART3, RK3588_UART3_SPI,
s->zephyr_ram ? 2 : 1, "uart3-vendor");
Comment on lines +2545 to +2546

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe UART3 in the generated device tree

For direct Linux boots that omit -dtb, rk3588_get_dtb() still calls rk3588_fdt_add_uart_node(), which emits only /serial@feb50000 for UART2. Consequently Linux cannot discover or bind a driver to the new UART3 at 0xfeb60000, leaving its serial_hd(1) backend unusable despite the MMIO device being instantiated. Add the corresponding UART3 node and interrupt information to the generated DT.

Useful? React with 👍 / 👎.

}

static void rk3588_attach_emmc_card(RK3588MachineState *s)
{
DriveInfo *di = drive_get(IF_SD, 0, 0);
Expand Down Expand Up @@ -3183,6 +3210,27 @@ static void rk3588_init(MachineState *machine)
}

if (machine->kernel_filename) {
/*
* arm/boot.c only invokes the ARMLinuxBootIf handoff for raw
* Linux images; ELF kernels skip it, leaving a TZ-aware GIC with
* all interrupts in Group 0 that NS guests can neither reassign
* nor enable. Zephyr-ram direct boot is known to run NonSecure,
* so run the same handoff for it: the GIC resets with all
* interrupts in NonSecure Group 1, as secure firmware would have
* configured it on real hardware.
*
* Other ELF payloads (e.g. U-Boot or BL31) boot at secure EL3
* and may rely on or test the hardware Group 0 reset state, so
* leave the GIC untouched for them.
*/
if (s->zephyr_ram) {
ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(s->gic);
ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(albif);

if (albifc->arm_linux_init) {
albifc->arm_linux_init(albif, false);
}
}
arm_load_kernel(s->cpu[0], machine, &s->bootinfo);
} else {
Error *local_err = NULL;
Expand Down