aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/intel/common/block/include/intelblocks/uart.h38
-rw-r--r--src/soc/intel/common/block/uart/Makefile.inc1
-rw-r--r--src/soc/intel/common/block/uart/uart.c97
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[] = {