blob: 23066e4d05433b44179c4c379a072455869d5e6d [file] [log] [blame]
Simon Glass4d221072019-12-19 17:58:20 -07001// 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 Glass684bc742023-05-04 16:55:00 -06008#define LOG_CATGEGORY UCLASS_SERIAL
9
Simon Glass4d221072019-12-19 17:58:20 -070010#include <common.h>
11#include <dm.h>
Simon Glass684bc742023-05-04 16:55:00 -060012#include <log.h>
Simon Glass4d221072019-12-19 17:58:20 -070013#include <ns16550.h>
14#include <serial.h>
Simon Glass684bc742023-05-04 16:55:00 -060015#include <acpi/acpi_table.h>
Simon Glass9a01fd92021-03-15 18:00:18 +130016#include <asm/cb_sysinfo.h>
Simon Glass4d221072019-12-19 17:58:20 -070017
Simon Glass684bc742023-05-04 16:55:00 -060018DECLARE_GLOBAL_DATA_PTR;
19
20static 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 Glassaad29ae2020-12-03 16:55:21 -070097static int coreboot_of_to_plat(struct udevice *dev)
Simon Glass4d221072019-12-19 17:58:20 -070098{
Simon Glassb75b15b2020-12-03 16:55:23 -070099 struct ns16550_plat *plat = dev_get_plat(dev);
Simon Glass4d221072019-12-19 17:58:20 -0700100 struct cb_serial *cb_info = lib_sysinfo.serial;
Simon Glass684bc742023-05-04 16:55:00 -0600101 int ret = -ENOENT;
Simon Glass4d221072019-12-19 17:58:20 -0700102
Simon Glass684bc742023-05-04 16:55:00 -0600103 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 Glass4d221072019-12-19 17:58:20 -0700125
126 return 0;
127}
128
129static const struct udevice_id coreboot_serial_ids[] = {
130 { .compatible = "coreboot-serial" },
131 { },
132};
133
134U_BOOT_DRIVER(coreboot_uart) = {
135 .name = "coreboot_uart",
136 .id = UCLASS_SERIAL,
137 .of_match = coreboot_serial_ids,
Simon Glass119e7ef2020-12-22 19:30:18 -0700138 .priv_auto = sizeof(struct ns16550),
Simon Glassb75b15b2020-12-03 16:55:23 -0700139 .plat_auto = sizeof(struct ns16550_plat),
Simon Glassaad29ae2020-12-03 16:55:21 -0700140 .of_to_plat = coreboot_of_to_plat,
Simon Glass4d221072019-12-19 17:58:20 -0700141 .probe = ns16550_serial_probe,
142 .ops = &ns16550_serial_ops,
143 .flags = DM_FLAG_PRE_RELOC,
144};