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