Commit 275854baeeec02e37c8f8bf718f94090f2a531ff

Authored by Simon Glass
1 parent f05ad9ba4c

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 +};