9
0
Fork 0

efi: add serial driver support

So now we can stop to use the efi-stdio as this driver
print on the Framebuffer and the serial at the same time.

This is specially usefull if we want to use the framebuffer via efi-gop for
something else.

Do not forget to disable the efi-stdio device before enabling the console
otherwise you will get double printing.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Jean-Christophe PLAGNIOL-VILLARD 2017-03-06 10:34:47 +01:00 committed by Sascha Hauer
parent 6518b21c6c
commit 5f03ddd47d
3 changed files with 226 additions and 0 deletions

View File

@ -21,6 +21,10 @@ config DRIVER_SERIAL_AR933X
If you have an Atheros AR933X SOC based board and want to use the
built-in UART of the SoC, say Y to this option.
config DRIVER_SERIAL_EFI
bool "EFI serial"
depends on EFI_BOOTUP
config DRIVER_SERIAL_IMX
depends on ARCH_IMX
default y

View File

@ -1,6 +1,7 @@
obj-$(CONFIG_DRIVER_SERIAL_ARM_DCC) += arm_dcc.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
obj-$(CONFIG_DRIVER_SERIAL_AR933X) += serial_ar933x.o
obj-$(CONFIG_DRIVER_SERIAL_EFI) += serial_efi.o
obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o
obj-$(CONFIG_DRIVER_SERIAL_STM378X) += stm-serial.o
obj-$(CONFIG_DRIVER_SERIAL_ATMEL) += atmel.o

221
drivers/serial/serial_efi.c Normal file
View File

@ -0,0 +1,221 @@
/*
* Copyright (C) 2017 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
*
* Under GPLv2 Only
*/
#include <common.h>
#include <driver.h>
#include <init.h>
#include <malloc.h>
#include <efi.h>
#include <efi/efi.h>
#include <efi/efi-device.h>
/*
* define for Control bits, grouped by read only, write only, and read write
*
* Read Only
*/
#define EFI_SERIAL_CLEAR_TO_SEND 0x00000010
#define EFI_SERIAL_DATA_SET_READY 0x00000020
#define EFI_SERIAL_RING_INDICATE 0x00000040
#define EFI_SERIAL_CARRIER_DETECT 0x00000080
#define EFI_SERIAL_INPUT_BUFFER_EMPTY 0x00000100
#define EFI_SERIAL_OUTPUT_BUFFER_EMPTY 0x00000200
/*
* Write Only
*/
#define EFI_SERIAL_REQUEST_TO_SEND 0x00000002
#define EFI_SERIAL_DATA_TERMINAL_READY 0x00000001
/*
* Read Write
*/
#define EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE 0x00001000
#define EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE 0x00002000
#define EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE 0x00004000
typedef enum {
DefaultParity,
NoParity,
EvenParity,
OddParity,
MarkParity,
SpaceParity
} efi_parity_type;
typedef enum {
DefaultStopBits,
OneStopBit,
OneFiveStopBits,
TwoStopBits
} efi_stop_bits_type;
struct efi_serial_io_mode {
uint32_t controlmask;
uint32_t timeout;
uint64_t baudrate;
uint32_t receivefifodepth;
uint32_t databits;
uint32_t parity;
uint32_t stopbits;
};
struct efi_serial_io_protocol {
uint32_t revision;
efi_status_t (EFIAPI *reset) (struct efi_serial_io_protocol *This);
efi_status_t (EFIAPI *set_attributes) (struct efi_serial_io_protocol *This,
uint64_t baudrate, uint32_t receivefifodepth,
uint32_t timeout, efi_parity_type parity,
uint8_t databits, efi_stop_bits_type stopbits);
efi_status_t (EFIAPI *setcontrol) (struct efi_serial_io_protocol *This,
uint32_t control);
efi_status_t (EFIAPI *getcontrol) (struct efi_serial_io_protocol *This,
uint32_t *control);
efi_status_t (EFIAPI *write) (struct efi_serial_io_protocol *This,
unsigned long *buffersize, void *buffer);
efi_status_t (EFIAPI *read) (struct efi_serial_io_protocol *This,
unsigned long *buffersize, void *buffer);
struct efi_serial_io_mode *mode;
};
/*
* We wrap our port structure around the generic console_device.
*/
struct efi_serial_port {
struct efi_serial_io_protocol *serial;
struct console_device uart; /* uart */
struct efi_device *efidev;
};
static inline struct efi_serial_port *
to_efi_serial_port(struct console_device *uart)
{
return container_of(uart, struct efi_serial_port, uart);
}
static int efi_serial_setbaudrate(struct console_device *cdev, int baudrate)
{
struct efi_serial_port *uart = to_efi_serial_port(cdev);
struct efi_serial_io_protocol *serial = uart->serial;
efi_status_t efiret;
efiret = serial->set_attributes(serial, baudrate, 0, 0, NoParity, 8,
OneStopBit);
if (EFI_ERROR(efiret))
return -efi_errno(efiret);
return 0;
}
static void efi_serial_putc(struct console_device *cdev, char c)
{
struct efi_serial_port *uart = to_efi_serial_port(cdev);
struct efi_serial_io_protocol *serial = uart->serial;
uint32_t control;
efi_status_t efiret;
unsigned long buffersize = sizeof(char);
do {
efiret = serial->getcontrol(serial, &control);
if (EFI_ERROR(efiret))
return;
} while(!(control & EFI_SERIAL_CLEAR_TO_SEND));
serial->write(serial, &buffersize, &c);
}
static int efi_serial_puts(struct console_device *cdev, const char *s)
{
struct efi_serial_port *uart = to_efi_serial_port(cdev);
struct efi_serial_io_protocol *serial = uart->serial;
uint32_t control;
efi_status_t efiret;
unsigned long buffersize = strlen(s) * sizeof(char);
do {
efiret = serial->getcontrol(serial, &control);
if (EFI_ERROR(efiret))
return 0;
} while(!(control & EFI_SERIAL_CLEAR_TO_SEND));
serial->write(serial, &buffersize, (void*)s);
return strlen(s);
}
static int efi_serial_getc(struct console_device *cdev)
{
struct efi_serial_port *uart = to_efi_serial_port(cdev);
struct efi_serial_io_protocol *serial = uart->serial;
uint32_t control;
efi_status_t efiret;
unsigned long buffersize = sizeof(char);
char c;
do {
efiret = serial->getcontrol(serial, &control);
if (EFI_ERROR(efiret))
return (int)-1;
} while(!(control & EFI_SERIAL_DATA_SET_READY));
serial->read(serial, &buffersize, &c);
return (int)c;
}
static int efi_serial_tstc(struct console_device *cdev)
{
struct efi_serial_port *uart = to_efi_serial_port(cdev);
struct efi_serial_io_protocol *serial = uart->serial;
uint32_t control;
efi_status_t efiret;
efiret = serial->getcontrol(serial, &control);
if (EFI_ERROR(efiret))
return 0;
return !(control & EFI_SERIAL_INPUT_BUFFER_EMPTY);
}
static int efi_serial_probe(struct efi_device *efidev)
{
struct efi_serial_port *uart;
struct console_device *cdev;
uart = xzalloc(sizeof(struct efi_serial_port));
cdev = &uart->uart;
cdev->dev = &efidev->dev;
cdev->tstc = efi_serial_tstc;
cdev->putc = efi_serial_putc;
cdev->puts = efi_serial_puts;
cdev->getc = efi_serial_getc;
cdev->setbrg = efi_serial_setbaudrate;
uart->serial = efidev->protocol;
uart->serial->reset(uart->serial);
/* Enable UART */
console_register(cdev);
return 0;
}
static struct efi_driver efi_serial_driver = {
.driver = {
.name = "efi-serial",
},
.probe = efi_serial_probe,
.guid = EFI_SERIAL_IO_PROTOCOL_GUID,
};
device_efi_driver(efi_serial_driver);