Commit 275854baeeec02e37c8f8bf718f94090f2a531ff
1 parent
f05ad9ba4c
Exists in
v2017.01-smarct4x
and in
30 other branches
efi: Add a serial driver
Add a serial driver which makes use of EFI's console in/out service. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Tested on Intel Crown Bay and QEMU Tested-by: Bin Meng <bmeng.cn@gmail.com>
Showing 3 changed files with 167 additions and 0 deletions Side-by-side Diff
drivers/serial/Kconfig
... | ... | @@ -44,6 +44,15 @@ |
44 | 44 | will need to provide parameters to make this work. The driver will |
45 | 45 | be available until the real driver model serial is running. |
46 | 46 | |
47 | +config DEBUG_EFI_CONSOLE | |
48 | + bool "EFI" | |
49 | + depends on EFI_APP | |
50 | + help | |
51 | + Select this to enable a debug console which calls back to EFI to | |
52 | + output to the console. This can be useful for early debugging of | |
53 | + U-Boot when running on top of EFI (Extensive Firmware Interface). | |
54 | + This is a type of BIOS used by PCs. | |
55 | + | |
47 | 56 | endchoice |
48 | 57 | |
49 | 58 | config DEBUG_UART_BASE |
drivers/serial/Makefile
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | obj-$(CONFIG_ARM_DCC) += arm_dcc.o |
22 | 22 | obj-$(CONFIG_ATMEL_USART) += atmel_usart.o |
23 | 23 | obj-$(CONFIG_DW_SERIAL) += serial_dw.o |
24 | +obj-$(CONFIG_EFI_APP) += serial_efi.o | |
24 | 25 | obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o |
25 | 26 | obj-$(CONFIG_MCFUART) += mcfuart.o |
26 | 27 | obj-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o |
drivers/serial/serial_efi.c
1 | +/* | |
2 | + * Copyright (c) 2015 Google, Inc | |
3 | + * Written by Simon Glass <sjg@chromium.org> | |
4 | + * | |
5 | + * SPDX-License-Identifier: GPL-2.0+ | |
6 | + */ | |
7 | + | |
8 | +#include <common.h> | |
9 | +#include <debug_uart.h> | |
10 | +#include <dm.h> | |
11 | +#include <efi.h> | |
12 | +#include <efi_api.h> | |
13 | +#include <errno.h> | |
14 | +#include <fdtdec.h> | |
15 | +#include <linux/compiler.h> | |
16 | +#include <asm/io.h> | |
17 | +#include <serial.h> | |
18 | + | |
19 | +/* Information about the efi console */ | |
20 | +struct serial_efi_priv { | |
21 | + struct efi_simple_input_interface *con_in; | |
22 | + struct efi_simple_text_output_protocol *con_out; | |
23 | + struct efi_input_key key; | |
24 | + bool have_key; | |
25 | +}; | |
26 | + | |
27 | +int serial_efi_setbrg(struct udevice *dev, int baudrate) | |
28 | +{ | |
29 | + return 0; | |
30 | +} | |
31 | + | |
32 | +static int serial_efi_get_key(struct serial_efi_priv *priv) | |
33 | +{ | |
34 | + int ret; | |
35 | + | |
36 | + if (priv->have_key) | |
37 | + return 0; | |
38 | + ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key); | |
39 | + if (ret == EFI_NOT_READY) | |
40 | + return -EAGAIN; | |
41 | + else if (ret != EFI_SUCCESS) | |
42 | + return -EIO; | |
43 | + | |
44 | + priv->have_key = true; | |
45 | + | |
46 | + return 0; | |
47 | +} | |
48 | + | |
49 | +static int serial_efi_getc(struct udevice *dev) | |
50 | +{ | |
51 | + struct serial_efi_priv *priv = dev_get_priv(dev); | |
52 | + int ret, ch; | |
53 | + | |
54 | + ret = serial_efi_get_key(priv); | |
55 | + if (ret) | |
56 | + return ret; | |
57 | + | |
58 | + priv->have_key = false; | |
59 | + ch = priv->key.unicode_char; | |
60 | + | |
61 | + /* | |
62 | + * Unicode char 8 (for backspace) is never returned. Instead we get a | |
63 | + * key scan code of 8. Handle this so that backspace works correctly | |
64 | + * in the U-Boot command line. | |
65 | + */ | |
66 | + if (!ch && priv->key.scan_code == 8) | |
67 | + ch = 8; | |
68 | + debug(" [%x %x %x] ", ch, priv->key.unicode_char, priv->key.scan_code); | |
69 | + | |
70 | + return ch; | |
71 | +} | |
72 | + | |
73 | +static int serial_efi_putc(struct udevice *dev, const char ch) | |
74 | +{ | |
75 | + struct serial_efi_priv *priv = dev_get_priv(dev); | |
76 | + uint16_t ucode[2]; | |
77 | + int ret; | |
78 | + | |
79 | + ucode[0] = ch; | |
80 | + ucode[1] = '\0'; | |
81 | + ret = priv->con_out->output_string(priv->con_out, ucode); | |
82 | + if (ret) | |
83 | + return -EIO; | |
84 | + | |
85 | + return 0; | |
86 | +} | |
87 | + | |
88 | +static int serial_efi_pending(struct udevice *dev, bool input) | |
89 | +{ | |
90 | + struct serial_efi_priv *priv = dev_get_priv(dev); | |
91 | + int ret; | |
92 | + | |
93 | + /* We assume that EFI will stall if its output buffer fills up */ | |
94 | + if (!input) | |
95 | + return 0; | |
96 | + | |
97 | + ret = serial_efi_get_key(priv); | |
98 | + if (ret == -EAGAIN) | |
99 | + return 0; | |
100 | + else if (ret) | |
101 | + return ret; | |
102 | + | |
103 | + return 1; | |
104 | +} | |
105 | + | |
106 | +/* | |
107 | + * There is nothing to init here since the EFI console is already running by | |
108 | + * the time we enter U-Boot. | |
109 | + */ | |
110 | +void debug_uart_init(void) | |
111 | +{ | |
112 | +} | |
113 | + | |
114 | +static inline void _debug_uart_putc(int ch) | |
115 | +{ | |
116 | + struct efi_system_table *sys_table = efi_get_sys_table(); | |
117 | + uint16_t ucode[2]; | |
118 | + | |
119 | + ucode[0] = ch; | |
120 | + ucode[1] = '\0'; | |
121 | + sys_table->con_out->output_string(sys_table->con_out, ucode); | |
122 | +} | |
123 | + | |
124 | +DEBUG_UART_FUNCS | |
125 | + | |
126 | +static int serial_efi_probe(struct udevice *dev) | |
127 | +{ | |
128 | + struct efi_system_table *table = efi_get_sys_table(); | |
129 | + struct serial_efi_priv *priv = dev_get_priv(dev); | |
130 | + | |
131 | + priv->con_in = table->con_in; | |
132 | + priv->con_out = table->con_out; | |
133 | + | |
134 | + return 0; | |
135 | +} | |
136 | + | |
137 | +static const struct dm_serial_ops serial_efi_ops = { | |
138 | + .putc = serial_efi_putc, | |
139 | + .getc = serial_efi_getc, | |
140 | + .pending = serial_efi_pending, | |
141 | + .setbrg = serial_efi_setbrg, | |
142 | +}; | |
143 | + | |
144 | +static const struct udevice_id serial_efi_ids[] = { | |
145 | + { .compatible = "efi,uart" }, | |
146 | + { } | |
147 | +}; | |
148 | + | |
149 | +U_BOOT_DRIVER(serial_efi) = { | |
150 | + .name = "serial_efi", | |
151 | + .id = UCLASS_SERIAL, | |
152 | + .of_match = serial_efi_ids, | |
153 | + .priv_auto_alloc_size = sizeof(struct serial_efi_priv), | |
154 | + .probe = serial_efi_probe, | |
155 | + .ops = &serial_efi_ops, | |
156 | + .flags = DM_FLAG_PRE_RELOC, | |
157 | +}; |