ec/system76: Add console support

This adds support for line-buffered console output to System76 EC firmware.

Once the print command is received, the EC firmware multiplexes the output
to any enabled console on the EC. This can be a memory ringbuffer, a
parallel port (using the keyboard connector), or i2c (using the battery
connector). Once the entire buffer is sent, it sets the command register
to 0, indicating completion. For more information, please see:
https://github.com/system76/ec/blob/master/doc/debugging.md

Tested on system76/lemp9 with CONSOLE_SYSTEM76_EC enabled.

Signed-off-by: Jeremy Soller <jeremy@system76.com>
Change-Id: I861bf3e22f40dd6c3ec7ba1d73711b399358e332
Reviewed-on: https://review.coreboot.org/c/coreboot/+/43718
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Michael Niewöhner
This commit is contained in:
Jeremy Soller
2020-07-22 06:40:28 -06:00
committed by Michael Niewöhner
parent 7e396f380e
commit 52785ab327
5 changed files with 113 additions and 0 deletions

View File

@ -302,6 +302,13 @@ config SPI_CONSOLE
This is currently working only in ramstage due to how the spi This is currently working only in ramstage due to how the spi
drivers are written. drivers are written.
config CONSOLE_SYSTEM76_EC
bool "System76 EC console output"
default n
depends on EC_SYSTEM76_EC
help
Send coreboot debug output to a System76 embedded controller.
config CONSOLE_OVERRIDE_LOGLEVEL config CONSOLE_OVERRIDE_LOGLEVEL
bool bool
help help

View File

@ -9,6 +9,7 @@
#include <console/usb.h> #include <console/usb.h>
#include <console/spi.h> #include <console/spi.h>
#include <console/flash.h> #include <console/flash.h>
#include <console/system76_ec.h>
void console_hw_init(void) void console_hw_init(void)
{ {
@ -21,6 +22,7 @@ void console_hw_init(void)
__usbdebug_init(); __usbdebug_init();
__spiconsole_init(); __spiconsole_init();
__flashconsole_init(); __flashconsole_init();
__system76_ec_init();
} }
void console_tx_byte(unsigned char byte) void console_tx_byte(unsigned char byte)
@ -42,6 +44,7 @@ void console_tx_byte(unsigned char byte)
__usb_tx_byte(byte); __usb_tx_byte(byte);
__spiconsole_tx_byte(byte); __spiconsole_tx_byte(byte);
__flashconsole_tx_byte(byte); __flashconsole_tx_byte(byte);
__system76_ec_tx_byte(byte);
} }
void console_tx_flush(void) void console_tx_flush(void)
@ -50,6 +53,7 @@ void console_tx_flush(void)
__ne2k_tx_flush(); __ne2k_tx_flush();
__usb_tx_flush(); __usb_tx_flush();
__flashconsole_tx_flush(); __flashconsole_tx_flush();
__system76_ec_tx_flush();
} }
void console_write_line(uint8_t *buffer, size_t number_of_bytes) void console_write_line(uint8_t *buffer, size_t number_of_bytes)

View File

@ -0,0 +1,6 @@
ifeq ($(CONFIG_EC_SYSTEM76_EC),y)
all-y += system76_ec.c
smm-$(CONFIG_DEBUG_SMI) += system76_ec.c
endif

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <arch/io.h>
#include <console/system76_ec.h>
#include <timer.h>
// This is the command region for System76 EC firmware. It must be
// enabled for LPC in the mainboard.
#define SYSTEM76_EC_BASE 0x0E00
#define SYSTEM76_EC_SIZE 256
#define REG_CMD 0
#define REG_RESULT 1
// When command register is 0, command is complete
#define CMD_FINISHED 0
// Print command. Registers are unique for each command
#define CMD_PRINT 4
#define CMD_PRINT_REG_FLAGS 2
#define CMD_PRINT_REG_LEN 3
#define CMD_PRINT_REG_DATA 4
static inline uint8_t system76_ec_read(uint8_t addr)
{
return inb(SYSTEM76_EC_BASE + (uint16_t)addr);
}
static inline void system76_ec_write(uint8_t addr, uint8_t data)
{
outb(data, SYSTEM76_EC_BASE + (uint16_t)addr);
}
void system76_ec_init(void)
{
// Clear entire command region
for (int i = 0; i < SYSTEM76_EC_SIZE; i++)
system76_ec_write((uint8_t)i, 0);
}
void system76_ec_flush(void)
{
system76_ec_write(REG_CMD, CMD_PRINT);
// Wait for command completion, for up to 10 milliseconds, with a
// test period of 1 microsecond
wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED);
system76_ec_write(CMD_PRINT_REG_LEN, 0);
}
void system76_ec_print(uint8_t byte)
{
uint8_t len = system76_ec_read(CMD_PRINT_REG_LEN);
system76_ec_write(CMD_PRINT_REG_DATA + len, byte);
system76_ec_write(CMD_PRINT_REG_LEN, len + 1);
// If we hit the end of the buffer, or were given a newline, flush
if (byte == '\n' || len >= (SYSTEM76_EC_SIZE - CMD_PRINT_REG_DATA))
system76_ec_flush();
}

View File

@ -0,0 +1,35 @@
#ifndef CONSOLE_SYSTEM76_EC_H
#define CONSOLE_SYSTEM76_EC_H 1
#include <stddef.h>
#include <stdint.h>
void system76_ec_init(void);
void system76_ec_flush(void);
void system76_ec_print(uint8_t byte);
#define __CONSOLE_SYSTEM76_EC_ENABLE__ (CONFIG(CONSOLE_SYSTEM76_EC) && \
(ENV_BOOTBLOCK || ENV_ROMSTAGE || ENV_RAMSTAGE \
|| ENV_SEPARATE_VERSTAGE || ENV_POSTCAR \
|| (ENV_SMM && CONFIG(DEBUG_SMI))))
#if __CONSOLE_SYSTEM76_EC_ENABLE__
static inline void __system76_ec_init(void)
{
system76_ec_init();
}
static inline void __system76_ec_tx_flush(void)
{
system76_ec_flush();
}
static inline void __system76_ec_tx_byte(unsigned char byte)
{
system76_ec_print(byte);
}
#else
static inline void __system76_ec_init(void) {}
static inline void __system76_ec_tx_flush(void) {}
static inline void __system76_ec_tx_byte(unsigned char byte) {}
#endif
#endif