diff options
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/uart.h | 38 | ||||
-rw-r--r-- | src/soc/intel/common/block/uart/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/intel/common/block/uart/uart.c | 97 |
3 files changed, 133 insertions, 3 deletions
diff --git a/src/soc/intel/common/block/include/intelblocks/uart.h b/src/soc/intel/common/block/include/intelblocks/uart.h index 9ec5004e08..78feb3e246 100644 --- a/src/soc/intel/common/block/include/intelblocks/uart.h +++ b/src/soc/intel/common/block/include/intelblocks/uart.h @@ -18,6 +18,7 @@ #include <arch/io.h> #include <device/device.h> +#include <stdint.h> /* * Common routine to initialize UART controller PCI config space, take it out of @@ -25,7 +26,44 @@ */ void uart_common_init(device_t dev, uintptr_t baseaddr); +/* + * Check if UART debug controller is initialized + * Returns: + * true = If debug controller PCI config space is initialized and device is + * out of reset + * false = otherwise + */ +bool uart_debug_controller_is_initialized(void); + +/* + * Check if dev corresponds to UART debug port controller. + * + * Returns: + * true: UART dev is debug port + * false: otherwise + */ +bool uart_is_debug_controller(struct device *dev); + +/**************************** SoC callbacks ***********************************/ void pch_uart_read_resources(struct device *dev); +/* + * Check if UART debug port controller needs to be initialized on resume. + * + * Returns: + * true = when SoC wants common code to do the UART debug port initialization + * false = otherwise + */ +bool pch_uart_init_debug_controller_on_resume(void); + +/* + * Get UART debug controller device structure + * + * Returns: + * Pointer to device structure = If device has a UART debug controller. + * NULL = otherwise + */ +device_t pch_uart_get_debug_controller(void); + #endif /* SOC_INTEL_COMMON_BLOCK_UART_H */ diff --git a/src/soc/intel/common/block/uart/Makefile.inc b/src/soc/intel/common/block/uart/Makefile.inc index 1a2c8be933..348b153907 100644 --- a/src/soc/intel/common/block/uart/Makefile.inc +++ b/src/soc/intel/common/block/uart/Makefile.inc @@ -1,2 +1,3 @@ bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_UART) += uart.c ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_UART) += uart.c +smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_UART) += uart.c diff --git a/src/soc/intel/common/block/uart/uart.c b/src/soc/intel/common/block/uart/uart.c index be30464b13..7685415d94 100644 --- a/src/soc/intel/common/block/uart/uart.c +++ b/src/soc/intel/common/block/uart/uart.c @@ -12,13 +12,18 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ + +#include <arch/acpi.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_def.h> #include <device/pci_ids.h> +#include <device/pci_ops.h> #include <intelblocks/lpss.h> #include <intelblocks/uart.h> +#define UART_PCI_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) + static void uart_lpss_init(uintptr_t baseaddr) { /* Take UART out of reset */ @@ -35,11 +40,40 @@ void uart_common_init(device_t dev, uintptr_t baseaddr) pci_write_config32(dev, PCI_BASE_ADDRESS_0, baseaddr); /* Enable memory access and bus master */ - pci_write_config32(dev, PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config32(dev, PCI_COMMAND, UART_PCI_ENABLE); uart_lpss_init(baseaddr); +} + +__attribute__((weak)) device_t pch_uart_get_debug_controller(void) +{ + /* + * device_t can either be a pointer to struct device (e.g. ramstage) or + * a simple integer (e.g. SMM) depending upon whether __SIMPLE_DEVICE__ + * is defined for the stage. Thus, the return requires additional + * casting to uintptr_t. + */ + return (device_t)(uintptr_t)NULL; +} + +bool uart_debug_controller_is_initialized(void) +{ + device_t dev; + uintptr_t base; + dev = pch_uart_get_debug_controller(); + if (!dev) + return false; + + base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF; + if (!base) + return false; + + if ((pci_read_config32(dev, PCI_COMMAND) & UART_PCI_ENABLE) + != UART_PCI_ENABLE) + return false; + + return !lpss_is_controller_in_reset(base); } #if ENV_RAMSTAGE @@ -49,10 +83,67 @@ __attribute__((weak)) void pch_uart_read_resources(struct device *dev) pci_dev_read_resources(dev); } +__attribute__((weak)) bool pch_uart_init_debug_controller_on_resume(void) +{ + /* By default, do not initialize controller. */ + return false; +} + +bool uart_is_debug_controller(struct device *dev) +{ + return dev == pch_uart_get_debug_controller(); +} + +/* + * This is a workaround to enable UART controller for the debug port if: + * 1. CONSOLE_SERIAL is not enabled in coreboot, and + * 2. This boot is S3 resume, and + * 3. SoC wants to initialize debug UART controller. + * + * This workaround is required because Linux kernel hangs on resume if console + * is not enabled in coreboot, but it is enabled in kernel and not suspended. + */ +static bool uart_controller_needs_init(struct device *dev) +{ + /* + * If coreboot has CONSOLE_SERIAL enabled, the skip re-initalizing + * controller here. + */ + if (IS_ENABLED(CONFIG_CONSOLE_SERIAL)) + return false; + + /* If this device does not correspond to debug port, then skip. */ + if (!uart_is_debug_controller(dev)) + return false; + + /* Initialize UART controller only on S3 resume. */ + if (!acpi_is_wakeup_s3()) + return false; + + /* + * Call SoC specific routine to confirm it wants to initialize + * controller. + */ + return pch_uart_init_debug_controller_on_resume(); +} + +static void uart_common_enable_resources(struct device *dev) +{ + pci_dev_enable_resources(dev); + + if (uart_controller_needs_init(dev)) { + uintptr_t base; + + base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF; + if (base) + uart_lpss_init(base); + } +} + static struct device_operations device_ops = { .read_resources = &pch_uart_read_resources, .set_resources = &pci_dev_set_resources, - .enable_resources = &pci_dev_enable_resources, + .enable_resources = &uart_common_enable_resources, }; static const unsigned short pci_device_ids[] = { |