Attention is currently required from: Intel coreboot Reviewers.
Keith Hui has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/86390?usp=email )
Change subject: sb/intel/common: Add GPIO serial blink console support ......................................................................
sb/intel/common: Add GPIO serial blink console support
Intel ICH/PCHs from ICH8 onwards have the ability to blink arbitrary messages (in this case, our console) onto a GPIO line. Idea is to place an optical sensor over an LED controlled by the GPIO line with serial blink enabled to "watch" the blinking and extract the messages.
Add support and documentation for serializing coreboot console over this channel.
Change-Id: I0741142fe21eba4989d28b96e795d3bfa3085466 Signed-off-by: Keith Hui buurin@gmail.com --- M Documentation/technotes/console.md M src/console/Kconfig M src/console/console.c M src/include/console/console.h A src/include/console/gpsb.h M src/southbridge/intel/common/gpio.c 6 files changed, 218 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/90/86390/1
diff --git a/Documentation/technotes/console.md b/Documentation/technotes/console.md index 9c028a4..4be975f 100644 --- a/Documentation/technotes/console.md +++ b/Documentation/technotes/console.md @@ -158,3 +158,35 @@ following blog posts for more details. * [coreboot Console via SMBus — Part I](https://medium.com/@husnifaiz/coreboot-console-via-smbus-introduction-382736...) * [coreboot Console via SMBus — Part II](https://medium.com/@husnifaiz/coreboot-console-via-smbus-part-ii-bc324fdd2f2...) + +## Intel ICH8 ~ 9-series PCH GPIO Serial Blink Console + +This is a capability introduced with ICH8 southbridge for accessing debug messages without a +dedicated LED. The ICH8 scheme is also supported by PCHs up to 9-series. One or more GPIO pins +among GPIO0 to GPIO31, configured as output, can be further configured to pulse arbitrary data +written to an I/O port in the GPIO block, with a programmable pulse duration in steps of 120 +nanoseconds. The chosen GPIO pin(s) will be reconfigured into an open-drain output with the PCH +pulsing the pin low for transmission. + +The protocol is described in section 5.15.5, "Serial POST Codes over GPIO", in the +[Intel 7-series PCH](https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7-se...) +datasheet. It is based on [Manchester coding] with a high-low transition for a "1" bit, and +low-high for a 0. It begins with a sync byte of 0xfe, or `11111110`, followed by one to four +8-bit bytes, transmitted MSB first, and ends with a high state for 2 clocks. + +In Intel's example, the chosen GPIO would be connected to a LED shared with the Super +I/O chip, and would momentarily pulls low then tristated on an active pulse, blinking the LED. +The pin would be pulled high at idle, so the LED can stay lit. + +An external circuit is needed to decode this pulse train, probably a phototransistor as well +if the scheme is implemented over a LED like Intel envisioned. An [Arduino example] (https://github.com/victornpb/manch_decode) +is available to base your circuit off of. + +coreboot driver to send console using this capability is developed on mainboards in the +Asus P8Z77 series, with a GPIO-controlled power LED. Within `make menuconfig` the GPIO pin +used and the pulse duration can be configured, as well as whether to leave this function +active after coreboot finishes its deeds. Unless so configured, coreboot cleans up the +GPIO alterations it made for this feature before handing off to payload or OS resume +vector, deactivating the console. + +[Manchester coding]: https://en.wikipedia.org/wiki/Manchester_code diff --git a/src/console/Kconfig b/src/console/Kconfig index db71204..bf958b8 100644 --- a/src/console/Kconfig +++ b/src/console/Kconfig @@ -386,6 +386,54 @@ help Send coreboot debug output to IO ports for SimNow
+config CONSOLE_INTEL_GPSB + bool "Intel GPIO serial blink console output" + depends on SOUTHBRIDGE_INTEL_COMMON_GPIO && !SOUTHBRIDGE_INTEL_I82801GX + default n + help + Send coreboot debug output to a GPIO pin of the southbridge. + + See Documentation/technotes/console.md for details. + +config CONSOLE_INTEL_GPSB_GPO + int "GPIO output pin (0-31)" + depends on CONSOLE_INTEL_GPSB + help + Select the GPIO pin number to receive coreboot console output. + Only GPIOs 0-31 support this functionality. + + This pin needs to be configured as an GPIO output in mainboard code, and be + either externally accessible via a header pin, or have a LED installed. + +config CONSOLE_INTEL_GPSB_DRS + int "Pulse interval (1-63 * 120ns)" + depends on CONSOLE_INTEL_GPSB + default 8 + help + Select the output pulse interval, in multiples of 120 nanoseconds. + Zero produces undefined behavior and is not allowed. + + Use the shortest your decoding circuit can reliably handle. + +config CONSOLE_INTEL_GPSB_LEAVE_ENABLED + bool "Leave console enabled past ramstage" + depends on CONSOLE_INTEL_GPSB + default n + help + By default, the GPIO configuration is restored at the end of ramstage, before + coreboot passes control to payload or (for S3 resume path) the OS wakeup vector. + This disables the serial blink console, and because there are more messages being + sent over the console after the last opportunity to do this cleanup, you will + likely miss out on them, as well as anything from SMM mode code. + + If you have available (and chose) an externally accessible GPIO that is not + used for anything else, say Y here to leave serial blink enabled after coreboot + proper leaves the scene, so to speak. This allows you to get everything. + However, if you piggyback on another GPIO, doing so will alter its configuration + and may interfere with its normal use. + + If unsure say N. + config CONSOLE_OVERRIDE_LOGLEVEL bool help diff --git a/src/console/console.c b/src/console/console.c index bb9d374..3b81bce 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -2,6 +2,7 @@
#include <console/cbmem_console.h> #include <console/flash.h> +#include <console/gpsb.h> #include <console/i2c_smbus.h> #include <console/ne2k.h> #include <console/qemu_debugcon.h> @@ -49,6 +50,7 @@ __spiconsole_tx_byte(byte); __system76_ec_tx_byte(byte); __i2c_smbus_console_tx_byte(byte); + __gpsb_tx_byte(byte); __simnow_console_tx_byte(byte); }
@@ -71,6 +73,7 @@ __usb_tx_flush(); __flashconsole_tx_flush(); __system76_ec_tx_flush(); + __gpsb_tx_flush(); }
void console_write_line(uint8_t *buffer, size_t number_of_bytes) diff --git a/src/include/console/console.h b/src/include/console/console.h index 8d1a5c5..48f5d0e 100644 --- a/src/include/console/console.h +++ b/src/include/console/console.h @@ -73,7 +73,7 @@ CONFIG(CONSOLE_SERIAL) || CONFIG(CONSOLE_NE2K) || CONFIG(CONSOLE_USB) || \ CONFIG(EM100PRO_SPI_CONSOLE) || CONFIG(CONSOLE_SPI_FLASH) || \ CONFIG(CONSOLE_SYSTEM76_EC) || CONFIG(CONSOLE_AMD_SIMNOW) || \ - CONFIG(CONSOLE_I2C_SMBUS)) + CONFIG(CONSOLE_I2C_SMBUS) || CONFIG(CONSOLE_INTEL_GPSB))
#else static inline int get_log_level(void) { return -1; } diff --git a/src/include/console/gpsb.h b/src/include/console/gpsb.h new file mode 100644 index 0000000..4f8b26a --- /dev/null +++ b/src/include/console/gpsb.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef CONSOLE_GPSB_H +#define CONSOLE_GPSB_H + +#include <stdint.h> + +/* This console is initialized immediately after GPIOs are set up. */ +void gpsb_tx_byte(unsigned char c); +void gpsb_tx_flush(void); + +#define __CONSOLE_GPSB_ENABLE__ (CONFIG(CONSOLE_INTEL_GPSB) && \ + (ENV_BOOTBLOCK || ENV_SEPARATE_ROMSTAGE || ENV_RAMSTAGE || ENV_SEPARATE_VERSTAGE \ + || ENV_POSTCAR || (ENV_SMM && CONFIG(DEBUG_SMI)))) + +#if __CONSOLE_GPSB_ENABLE__ +static inline void __gpsb_tx_byte(u8 data) { gpsb_tx_byte(data); } +static inline void __gpsb_tx_flush(void) { gpsb_tx_flush(); } +#else +static inline void __gpsb_tx_byte(u8 data) {} +static inline void __gpsb_tx_flush(void) {} +#endif + +#endif diff --git a/src/southbridge/intel/common/gpio.c b/src/southbridge/intel/common/gpio.c index f32532c1..3820783 100644 --- a/src/southbridge/intel/common/gpio.c +++ b/src/southbridge/intel/common/gpio.c @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-only */
#include <arch/io.h> +#include <console/gpsb.h> #include <device/pci_ops.h> #include <device/pci_type.h> +#include <bootstate.h> #include <stdint.h>
#include "gpio.h" @@ -12,6 +14,14 @@ /* LPC GPIO Base Address Register */ #define GPIO_BASE 0x48
+#define GP_SER_BLINK 0x1c +#define GP_SB_CMDSTS 0x20 +#define GP_SB_DATA 0x24 + +#if (CONFIG_CONSOLE_INTEL_GPSB_GPO > 31) +#error "Invalid GPIO for serial blink; must be 0-31" +#endif + static u16 get_gpio_base(void) { #ifdef __SIMPLE_DEVICE__ @@ -29,6 +39,22 @@ #endif }
+static void enable_serial_blink(const u16 gpiobase) +{ + const u32 gpio1mask = 1 << CONFIG_CONSOLE_INTEL_GPSB_GPO; + u32 gpio1 = inl(gpiobase + GP_IO_SEL); + + /* Temporarily set the GPIO to input, enable serial blink, then change it to output. + * This ensures the open-drain buffer is proper. + * - 7-series PCH datasheet, page 200 + */ + outl(gpio1 | gpio1mask, gpiobase + GP_IO_SEL); + outl(gpio1mask, gpiobase + GP_SER_BLINK); + /* Clear output data & allow a bit of time for chipset to ready its buffer. */ + outl(0, gpiobase + GP_SB_DATA); + outl(gpio1 & ~gpio1mask, gpiobase + GP_IO_SEL); +} + void setup_pch_gpios(const struct pch_gpio_map *gpio) { u16 gpiobase = get_gpio_base(); @@ -79,6 +105,10 @@ outl(*((u32 *)gpio->set3.level), gpiobase + GP_LVL3); if (gpio->set3.reset) outl(*((u32 *)gpio->set3.reset), gpiobase + GP_RST_SEL3); + + if (CONFIG(CONSOLE_INTEL_GPSB) && ENV_INITIAL_STAGE) { + enable_serial_blink(gpiobase); + } }
/* @@ -161,3 +191,83 @@ config = inl(gpio_base + gpio_reg_offsets[index]); return !(config & (1 << bit)); } + +#if CONFIG(CONSOLE_INTEL_GPSB) +#define GP_SB_CMDSTS_GO 1 +#define GP_SB_CMDSTS_BUSY 1 << 8 +static void gpsb_go(const u16 gpiobase, u8 dls) +{ + const u16 gpsbcmd = gpiobase + GP_SB_CMDSTS; + u8 drs = CONFIG_CONSOLE_INTEL_GPSB_DRS; + /* Force a default if CONFIG'd with a bad value. */ + if (drs == 0 || drs > 63) + drs = 8; + + u32 cmd = dls << 22 | drs << 16 | GP_SB_CMDSTS_GO; + outl(cmd, gpsbcmd); + + /* Wait for the blink to finish. */ + while (inl(gpsbcmd) & GP_SB_CMDSTS_BUSY) + ; +} + +void gpsb_tx_byte(unsigned char c) +{ + const u16 gpiobase = get_gpio_base(); + u32 data = inl(gpiobase + GP_SB_DATA); + /* Already have 4 bytes pending; get them out first. */ + if (data > 0xffffff) { + gpsb_go(gpiobase, 3); + data = c; + } else { + data <<= 8; + data |= (c & 0xff); + } + outl(data, gpiobase + GP_SB_DATA); +} + +void gpsb_tx_flush(void) +{ + const u16 gpiobase = get_gpio_base(); + u32 data = inl(gpiobase + GP_SB_DATA); + u8 byte3 = 0, dls = 0; + + if (data == 0) + return; + + if (data > 0xffff) { + dls = (data > 0xffffff) ? 3 : 2; + } else { + dls = (data > 0xff) ? 1 : 0; + } + /* Cannot serialize 3 bytes. Do 2 first. */ + if (dls == 2) { + byte3 = data & 0xff; + dls = 1; + data >>= 8; + outl(data, gpiobase + GP_SB_DATA); + } + + gpsb_go(gpiobase, dls); + + /* Serialize the last byte. */ + if (byte3) { + outl(byte3, gpiobase + GP_SB_DATA); + gpsb_go(gpiobase, 0); + } + + /* Clear data register. */ + outl(0, gpiobase + GP_SB_DATA); +} + +#if !CONFIG(CONSOLE_INTEL_GPSB_LEAVE_ENABLED) +static void disable_serial_blink(void *unused) +{ + gpsb_tx_flush(); + outl(0, get_gpio_base() + GP_SER_BLINK); +} + +BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, disable_serial_blink, NULL); +BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_BOOT, BS_ON_ENTRY, disable_serial_blink, NULL); +#endif +#endif