uboot/drivers/serial/serial_efi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <debug_uart.h>
   9#include <dm.h>
  10#include <efi.h>
  11#include <efi_api.h>
  12#include <errno.h>
  13#include <fdtdec.h>
  14#include <log.h>
  15#include <linux/compiler.h>
  16#include <asm/io.h>
  17#include <serial.h>
  18
  19/* Information about the efi console */
  20struct serial_efi_priv {
  21        struct efi_simple_text_input_protocol *con_in;
  22        struct efi_simple_text_output_protocol *con_out;
  23        struct efi_input_key key;
  24        bool have_key;
  25};
  26
  27int serial_efi_setbrg(struct udevice *dev, int baudrate)
  28{
  29        return 0;
  30}
  31
  32static 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
  49static 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
  73static 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
  88static 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 */
 110static inline void _debug_uart_init(void)
 111{
 112}
 113
 114static 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
 124DEBUG_UART_FUNCS
 125
 126static 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
 137static 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
 144static const struct udevice_id serial_efi_ids[] = {
 145        { .compatible = "efi,uart" },
 146        { }
 147};
 148
 149U_BOOT_DRIVER(serial_efi) = {
 150        .name   = "serial_efi",
 151        .id     = UCLASS_SERIAL,
 152        .of_match = serial_efi_ids,
 153        .priv_auto      = sizeof(struct serial_efi_priv),
 154        .probe = serial_efi_probe,
 155        .ops    = &serial_efi_ops,
 156};
 157