blob: 3cf3d63b75d95e07f7e85f7b62d93cb603decdd0 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2017, Linaro Limited
*/
#include <console.h>
#include <compiler.h>
#include <drivers/serial.h>
#include <kernel/generic_boot.h>
#include <kernel/panic.h>
#include <stdlib.h>
#include <string.h>
#include <string_ext.h>
#ifdef CFG_DT
#include <kernel/dt.h>
#include <libfdt.h>
#endif
static struct serial_chip *serial_console __nex_bss;
void __weak console_putc(int ch)
{
if (!serial_console)
return;
if (ch == '\n')
serial_console->ops->putc(serial_console, '\r');
serial_console->ops->putc(serial_console, ch);
}
void __weak console_flush(void)
{
if (!serial_console)
return;
serial_console->ops->flush(serial_console);
}
void register_serial_console(struct serial_chip *chip)
{
serial_console = chip;
}
#ifdef CFG_DT
static int find_chosen_node(void *fdt)
{
if (!fdt)
return -1;
int offset = fdt_path_offset(fdt, "/secure-chosen");
if (offset < 0)
offset = fdt_path_offset(fdt, "/chosen");
return offset;
}
TEE_Result get_console_node_from_dt(void *fdt, int *offs_out,
char **path_out, char **params_out)
{
const struct fdt_property *prop;
const char *uart;
const char *parms = NULL;
int offs;
char *stdout_data;
char *p;
TEE_Result rc = TEE_ERROR_GENERIC;
/* Probe console from secure DT and fallback to non-secure DT */
offs = find_chosen_node(fdt);
if (offs < 0) {
DMSG("No console directive from DTB");
return TEE_ERROR_ITEM_NOT_FOUND;
}
prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
if (!prop) {
/*
* A secure-chosen or chosen node is present but defined
* no stdout-path property: no console expected
*/
IMSG("Switching off console");
register_serial_console(NULL);
return TEE_ERROR_ITEM_NOT_FOUND;
}
stdout_data = nex_strdup(prop->data);
if (!stdout_data)
panic();
p = strchr(stdout_data, ':');
if (p) {
*p = '\0';
parms = p + 1;
}
/* stdout-path may refer to an alias */
uart = fdt_get_alias(fdt, stdout_data);
if (!uart) {
/* Not an alias, assume we have a node path */
uart = stdout_data;
}
offs = fdt_path_offset(fdt, uart);
if (offs >= 0) {
if (offs_out)
*offs_out = offs;
if (params_out)
*params_out = parms ? nex_strdup(parms) : NULL;
if (path_out)
*path_out = uart ? nex_strdup(uart) : NULL;
rc = TEE_SUCCESS;
}
nex_free(stdout_data);
return rc;
}
void configure_console_from_dt(void)
{
const struct dt_driver *dt_drv;
const struct serial_driver *sdrv;
struct serial_chip *dev;
char *uart = NULL;
char *parms = NULL;
void *fdt;
int offs;
fdt = get_external_dt();
if (get_console_node_from_dt(fdt, &offs, &uart, &parms))
return;
dt_drv = dt_find_compatible_driver(fdt, offs);
if (!dt_drv)
goto out;
sdrv = (const struct serial_driver *)dt_drv->driver;
if (!sdrv)
goto out;
dev = sdrv->dev_alloc();
if (!dev)
goto out;
/*
* If the console is the same as the early console, dev_init() might
* clear pending data. Flush to avoid that.
*/
console_flush();
if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
sdrv->dev_free(dev);
goto out;
}
IMSG("Switching console to device: %s", uart);
register_serial_console(dev);
out:
nex_free(uart);
nex_free(parms);
}
#endif /* CFG_DT */