Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * UART support for U-Boot when launched from Coreboot |
| 4 | * |
| 5 | * Copyright 2019 Google LLC |
| 6 | */ |
| 7 | |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 8 | #define LOG_CATGEGORY UCLASS_SERIAL |
| 9 | |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 10 | #include <common.h> |
| 11 | #include <dm.h> |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 12 | #include <log.h> |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 13 | #include <ns16550.h> |
| 14 | #include <serial.h> |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 15 | #include <acpi/acpi_table.h> |
Simon Glass | 9a01fd9 | 2021-03-15 18:00:18 +1300 | [diff] [blame] | 16 | #include <asm/cb_sysinfo.h> |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 17 | |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 18 | DECLARE_GLOBAL_DATA_PTR; |
| 19 | |
| 20 | static int read_dbg2(struct ns16550_plat *plat) |
| 21 | { |
| 22 | struct acpi_table_header *tab; |
| 23 | struct acpi_dbg2_header *hdr; |
| 24 | struct acpi_dbg2_device *dbg; |
| 25 | struct acpi_gen_regaddr *addr; |
| 26 | u32 *addr_size; |
| 27 | |
| 28 | log_debug("Looking for DBG2 in ACPI tables\n"); |
| 29 | if (!gd->acpi_start) { |
| 30 | log_debug("No ACPI tables\n"); |
| 31 | return -ENOENT; |
| 32 | } |
| 33 | |
| 34 | tab = acpi_find_table("DBG2"); |
| 35 | if (!tab) { |
| 36 | log_debug("No DBG2 table\n"); |
| 37 | return -ENOENT; |
| 38 | } |
| 39 | hdr = container_of(tab, struct acpi_dbg2_header, header); |
| 40 | |
| 41 | /* We only use the first device, but check that there is at least one */ |
| 42 | if (!hdr->devices_count) { |
| 43 | log_debug("No devices\n"); |
| 44 | return -ENOENT; |
| 45 | } |
| 46 | if (hdr->devices_offset >= tab->length) { |
| 47 | log_debug("Invalid offset\n"); |
| 48 | return -EINVAL; |
| 49 | } |
| 50 | dbg = (void *)hdr + hdr->devices_offset; |
| 51 | if (dbg->revision) { |
| 52 | log_debug("Invalid revision %d\n", dbg->revision); |
| 53 | return -EINVAL; |
| 54 | } |
| 55 | if (!dbg->address_count) { |
| 56 | log_debug("No addresses\n"); |
| 57 | return -EINVAL; |
| 58 | } |
| 59 | if (dbg->port_type != ACPI_DBG2_SERIAL_PORT) { |
| 60 | log_debug("Not a serial port\n"); |
| 61 | return -EPROTOTYPE; |
| 62 | } |
| 63 | if (dbg->port_subtype != ACPI_DBG2_16550_COMPATIBLE) { |
| 64 | log_debug("Incompatible serial port\n"); |
| 65 | return -EPROTOTYPE; |
| 66 | } |
| 67 | if (dbg->base_address_offset >= dbg->length || |
| 68 | dbg->address_size_offset >= dbg->length) { |
| 69 | log_debug("Invalid base address/size offsets %d, %d\n", |
| 70 | dbg->base_address_offset, dbg->address_size_offset); |
| 71 | return -EINVAL; |
| 72 | } |
| 73 | addr_size = (void *)dbg + dbg->address_size_offset; |
| 74 | if (!*addr_size) { |
| 75 | log_debug("Zero address size\n"); |
| 76 | return -EINVAL; |
| 77 | } |
| 78 | addr = (void *)dbg + dbg->base_address_offset; |
| 79 | if (addr->space_id != ACPI_ADDRESS_SPACE_MEMORY) { |
| 80 | log_debug("Incompatible space %d\n", addr->space_id); |
| 81 | return -EPROTOTYPE; |
| 82 | } |
| 83 | |
| 84 | plat->base = addr->addrl; |
| 85 | |
| 86 | /* ACPI_ACCESS_SIZE_DWORD_ACCESS is 3; we want 2 */ |
| 87 | plat->reg_shift = addr->access_size - 1; |
| 88 | plat->reg_width = 4; /* coreboot sets bit_width to 0 */ |
| 89 | plat->clock = 1843200; |
| 90 | plat->fcr = UART_FCR_DEFVAL; |
| 91 | plat->flags = 0; |
| 92 | log_debug("Collected UART from ACPI DBG2 table\n"); |
| 93 | |
| 94 | return 0; |
| 95 | } |
| 96 | |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 97 | static int coreboot_of_to_plat(struct udevice *dev) |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 98 | { |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 99 | struct ns16550_plat *plat = dev_get_plat(dev); |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 100 | struct cb_serial *cb_info = lib_sysinfo.serial; |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 101 | int ret = -ENOENT; |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 102 | |
Simon Glass | 684bc74 | 2023-05-04 16:55:00 -0600 | [diff] [blame] | 103 | if (cb_info) { |
| 104 | plat->base = cb_info->baseaddr; |
| 105 | plat->reg_shift = cb_info->regwidth == 4 ? 2 : 0; |
| 106 | plat->reg_width = cb_info->regwidth; |
| 107 | plat->clock = cb_info->input_hertz; |
| 108 | plat->fcr = UART_FCR_DEFVAL; |
| 109 | plat->flags = 0; |
| 110 | if (cb_info->type == CB_SERIAL_TYPE_IO_MAPPED) |
| 111 | plat->flags |= NS16550_FLAG_IO; |
| 112 | ret = 0; |
| 113 | } else if (IS_ENABLED(CONFIG_COREBOOT_SERIAL_FROM_DBG2)) { |
| 114 | ret = read_dbg2(plat); |
| 115 | } |
| 116 | |
| 117 | if (ret) { |
| 118 | /* |
| 119 | * Returning an error will cause U-Boot to complain that |
| 120 | * there is no UART, which may panic. So stay silent and |
| 121 | * pray that the video console will work. |
| 122 | */ |
| 123 | log_debug("Cannot detect UART\n"); |
| 124 | } |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 125 | |
| 126 | return 0; |
| 127 | } |
| 128 | |
| 129 | static const struct udevice_id coreboot_serial_ids[] = { |
| 130 | { .compatible = "coreboot-serial" }, |
| 131 | { }, |
| 132 | }; |
| 133 | |
| 134 | U_BOOT_DRIVER(coreboot_uart) = { |
| 135 | .name = "coreboot_uart", |
| 136 | .id = UCLASS_SERIAL, |
| 137 | .of_match = coreboot_serial_ids, |
Simon Glass | 119e7ef | 2020-12-22 19:30:18 -0700 | [diff] [blame] | 138 | .priv_auto = sizeof(struct ns16550), |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 139 | .plat_auto = sizeof(struct ns16550_plat), |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 140 | .of_to_plat = coreboot_of_to_plat, |
Simon Glass | 4d22107 | 2019-12-19 17:58:20 -0700 | [diff] [blame] | 141 | .probe = ns16550_serial_probe, |
| 142 | .ops = &ns16550_serial_ops, |
| 143 | .flags = DM_FLAG_PRE_RELOC, |
| 144 | }; |