// SPDX-License-Identifier: GPL-2.0+ /* * UART support for U-Boot when launched from Coreboot * * Copyright 2019 Google LLC */ #define LOG_CATGEGORY UCLASS_SERIAL #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; static int read_dbg2(struct ns16550_plat *plat) { struct acpi_table_header *tab; struct acpi_dbg2_header *hdr; struct acpi_dbg2_device *dbg; struct acpi_gen_regaddr *addr; u32 *addr_size; log_debug("Looking for DBG2 in ACPI tables\n"); if (!gd->acpi_start) { log_debug("No ACPI tables\n"); return -ENOENT; } tab = acpi_find_table("DBG2"); if (!tab) { log_debug("No DBG2 table\n"); return -ENOENT; } hdr = container_of(tab, struct acpi_dbg2_header, header); /* We only use the first device, but check that there is at least one */ if (!hdr->devices_count) { log_debug("No devices\n"); return -ENOENT; } if (hdr->devices_offset >= tab->length) { log_debug("Invalid offset\n"); return -EINVAL; } dbg = (void *)hdr + hdr->devices_offset; if (dbg->revision) { log_debug("Invalid revision %d\n", dbg->revision); return -EINVAL; } if (!dbg->address_count) { log_debug("No addresses\n"); return -EINVAL; } if (dbg->port_type != ACPI_DBG2_SERIAL_PORT) { log_debug("Not a serial port\n"); return -EPROTOTYPE; } if (dbg->port_subtype != ACPI_DBG2_16550_COMPATIBLE) { log_debug("Incompatible serial port\n"); return -EPROTOTYPE; } if (dbg->base_address_offset >= dbg->length || dbg->address_size_offset >= dbg->length) { log_debug("Invalid base address/size offsets %d, %d\n", dbg->base_address_offset, dbg->address_size_offset); return -EINVAL; } addr_size = (void *)dbg + dbg->address_size_offset; if (!*addr_size) { log_debug("Zero address size\n"); return -EINVAL; } addr = (void *)dbg + dbg->base_address_offset; if (addr->space_id != ACPI_ADDRESS_SPACE_MEMORY) { log_debug("Incompatible space %d\n", addr->space_id); return -EPROTOTYPE; } plat->base = addr->addrl; /* ACPI_ACCESS_SIZE_DWORD_ACCESS is 3; we want 2 */ plat->reg_shift = addr->access_size - 1; plat->reg_width = 4; /* coreboot sets bit_width to 0 */ plat->clock = 1843200; plat->fcr = UART_FCR_DEFVAL; plat->flags = 0; log_debug("Collected UART from ACPI DBG2 table\n"); return 0; } static int coreboot_of_to_plat(struct udevice *dev) { struct ns16550_plat *plat = dev_get_plat(dev); struct cb_serial *cb_info = lib_sysinfo.serial; int ret = -ENOENT; if (cb_info) { plat->base = cb_info->baseaddr; plat->reg_shift = cb_info->regwidth == 4 ? 2 : 0; plat->reg_width = cb_info->regwidth; plat->clock = cb_info->input_hertz; plat->fcr = UART_FCR_DEFVAL; plat->flags = 0; if (cb_info->type == CB_SERIAL_TYPE_IO_MAPPED) plat->flags |= NS16550_FLAG_IO; ret = 0; } else if (IS_ENABLED(CONFIG_COREBOOT_SERIAL_FROM_DBG2)) { ret = read_dbg2(plat); } if (ret) { /* * Returning an error will cause U-Boot to complain that * there is no UART, which may panic. So stay silent and * pray that the video console will work. */ log_debug("Cannot detect UART\n"); } return 0; } static const struct udevice_id coreboot_serial_ids[] = { { .compatible = "coreboot-serial" }, { }, }; U_BOOT_DRIVER(coreboot_uart) = { .name = "coreboot_uart", .id = UCLASS_SERIAL, .of_match = coreboot_serial_ids, .priv_auto = sizeof(struct ns16550), .plat_auto = sizeof(struct ns16550_plat), .of_to_plat = coreboot_of_to_plat, .probe = ns16550_serial_probe, .ops = &ns16550_serial_ops, .flags = DM_FLAG_PRE_RELOC, };