[U-Boot] [PATCH 28/40] x86: Add a sysreset driver for the Intel PCH

Simon Glass sjg at chromium.org
Wed Jan 30 03:59:23 UTC 2019


Intel SoCs support a fairly stardard reset mechanism which can support
powering off the device. Add support for this and enable it by default on
broadwell, which already has the necessary pm.h header file.

This driver augments the standard x86 sysreset driver.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 arch/x86/cpu/broadwell/Kconfig        |   1 +
 drivers/sysreset/Kconfig              |   9 ++
 drivers/sysreset/Makefile             |   1 +
 drivers/sysreset/sysreset_intel_pch.c | 125 ++++++++++++++++++++++++++
 4 files changed, 136 insertions(+)
 create mode 100644 drivers/sysreset/sysreset_intel_pch.c

diff --git a/arch/x86/cpu/broadwell/Kconfig b/arch/x86/cpu/broadwell/Kconfig
index 5b015c89d9..2955ffc55b 100644
--- a/arch/x86/cpu/broadwell/Kconfig
+++ b/arch/x86/cpu/broadwell/Kconfig
@@ -18,6 +18,7 @@ config INTEL_BROADWELL
 	imply USB
 	imply USB_EHCI_HCD
 	imply VIDEO_BROADWELL_IGD
+	imply SYSRESET_INTEL_PCH
 
 if INTEL_BROADWELL
 
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 8ce3e2e207..f88412adcc 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -23,6 +23,15 @@ config SYSRESET_GPIO
 	  example on Microblaze where reset logic can be controlled via GPIO
 	  pin which triggers cpu reset.
 
+config SYSRESET_INTEL_PCH
+	bool "Enable support for Intel PCH reset driver"
+	depends on X86
+	help
+	  Enable this option to get reset support on Intel SoCs which have
+	  a common Platform-Controller Hub (PCH). This driver supports powering
+	  off the device. It augments the standard x86 sysreset driver which
+	  provides normal reset options.
+
 config SYSRESET_MICROBLAZE
 	bool "Enable support for Microblaze soft reset"
 	depends on MICROBLAZE
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index b3728ac17f..2add6cb37a 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o
 obj-$(CONFIG_ARCH_STI) += sysreset_sti.o
 obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
 obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o
+obj-$(CONFIG_SYSRESET_INTEL_PCH) += sysreset_intel_pch.o
 obj-$(CONFIG_SYSRESET_MCP83XX) += sysreset_mpc83xx.o
 obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o
 obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o
diff --git a/drivers/sysreset/sysreset_intel_pch.c b/drivers/sysreset/sysreset_intel_pch.c
new file mode 100644
index 0000000000..b60fa40dda
--- /dev/null
+++ b/drivers/sysreset/sysreset_intel_pch.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Google Inc,
+ * Written by Simon Glass <sjg at chromium.org>
+ *
+ * Reset driver for intel x86 processors with a PCH. Supports powering the
+ * device off.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <sysreset.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/arch/pch.h>
+#include <asm/arch/pm.h>
+
+struct x86_reset_platdata {
+	struct udevice *pch;
+};
+
+/*
+ * Power down the machine by using the power management sleep control
+ * of the chipset. This will currently only work on Intel chipsets.
+ * However, adapting it to new chipsets is fairly simple. You will
+ * have to find the IO address of the power management register block
+ * in your southbridge, and look up the appropriate SLP_TYP_S5 value
+ * from your southbridge's data sheet.
+ *
+ * This function never returns.
+ */
+int pch_sysreset_power_off(struct udevice *dev)
+{
+	struct x86_reset_platdata *plat = dev_get_platdata(dev);
+	u16 pmbase;
+	u32 reg32;
+	int ret;
+
+	if (!plat->pch)
+		return -ENOENT;
+
+	/* Find the base address of the powermanagement registers */
+	ret = dm_pci_read_config16(plat->pch, 0x40, &pmbase);
+	if (ret)
+		return ret;
+
+	pmbase &= 0xfffe;
+
+	/* Mask interrupts or system might stay in a coma
+	 * (not executing code anymore, but not powered off either)
+	 */
+	asm("cli");
+
+	/*
+	 * Avoid any GPI waking the system from S5* or the system might stay in
+	 * a coma
+	 */
+	outl(0x00000000, pmbase + GPE0_EN(0));
+
+	/* Clear Power Button Status */
+	outw(PWRBTN_STS, pmbase + PM1_STS);
+
+	/* PMBASE + 4, Bit 10-12, Sleeping Type, * set to 111 -> S5, soft_off */
+	reg32 = inl(pmbase + PM1_CNT);
+
+	/* Set Sleeping Type to S5 (poweroff) */
+	reg32 &= ~(SLP_EN | SLP_TYP);
+	reg32 |= SLP_TYP_S5;
+	outl(reg32, pmbase + PM1_CNT);
+
+	/* Now set the Sleep Enable bit */
+	reg32 |= SLP_EN;
+	outl(reg32, pmbase + PM1_CNT);
+
+	for (;;)
+		asm("hlt");
+}
+
+static int pch_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+	int ret;
+
+	switch (type) {
+	case SYSRESET_POWER_OFF:
+		ret = pch_sysreset_power_off(dev);
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	return -EINPROGRESS;
+}
+
+static int pch_sysreset_ofdata_to_platdata(struct udevice *dev)
+{
+	struct x86_reset_platdata *plat = dev_get_platdata(dev);
+	int ret;
+
+	ret = uclass_get_device_by_phandle(UCLASS_PCH, dev, "intel,pch",
+					   &plat->pch);
+	if (ret && ret != -ENOENT)
+		return log_ret(ret);
+
+	return 0;
+}
+
+static const struct udevice_id pch_sysreset_ids[] = {
+	{ .compatible = "intel,pch-reset" },
+	{ }
+};
+
+static struct sysreset_ops pch_sysreset_ops = {
+	.request = pch_sysreset_request,
+};
+
+U_BOOT_DRIVER(pch_sysreset) = {
+	.name = "pch-sysreset",
+	.id = UCLASS_SYSRESET,
+	.of_match = pch_sysreset_ids,
+	.ops = &pch_sysreset_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+	.ofdata_to_platdata	= pch_sysreset_ofdata_to_platdata,
+};
-- 
2.20.1.495.gaa96b0ce6b-goog



More information about the U-Boot mailing list