blob: 7c7a0bba3954f2f05779b50668e9e86d58d9f5d1 [file] [log] [blame]
From bf78d9acd43075d887bc1232df872955d0b7c5b4 Mon Sep 17 00:00:00 2001
From: Grzegorz Jaszczyk <jaz@semihalf.com>
Date: Wed, 6 Jul 2022 10:48:30 +0000
Subject: [PATCH] FROMLIST: platform/x86: Add virtual PMC driver used for
S2Idle
Virtual PMC driver is meant for the guest VMs for the S2Idle
notification. Its purpose is to register S2Idle dev ops check handler,
which will evaluate ACPI _DSM just before the guest enters S2Idle power
state.
This allows to trap on MMIO access done as a consequence of _DSM
evaluation and therefore notify the VMM about the guest entering S2Idle
state.
Signed-off-by: Grzegorz Jaszczyk <jaz@semihalf.com>
(am from https://patchwork.kernel.org/patch/13138148/)
UPSTREAM-TASK=b:280760527
BUG=b:194391015
TEST=Perform several s2idle suspend/resume cycle and make sure that
s2idle notifications are caught by crosvm.
Change-Id: Iacfa14aa302b50151385e1d7909c538590e672b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4506679
Reviewed-by: Sean Paul <sean@poorly.run>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Grzegorz Jaszczyk <jaszczyk@google.com>
Reviewed-by: Tomasz Nowicki <tnowicki@google.com>
Tested-by: Grzegorz Jaszczyk <jaszczyk@google.com>
---
drivers/platform/x86/Kconfig | 7 +++
drivers/platform/x86/Makefile | 3 ++
drivers/platform/x86/virt_pmc.c | 83 +++++++++++++++++++++++++++++++++
3 files changed, 93 insertions(+)
create mode 100644 drivers/platform/x86/virt_pmc.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2a10705433911d9aa2c0d9fd7d9c1cf81b663c4a..b75dafef562d9ed4223b80963b17749fb13f72b3 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1099,6 +1099,13 @@ config SEL3350_PLATFORM
To compile this driver as a module, choose M here: the module
will be called sel3350-platform.
+config VIRT_PMC
+ tristate "Virtual Power Management Controller"
+ depends on ACPI && SUSPEND && HYPERVISOR_GUEST
+ help
+ The Virtual PMC driver is meant for the guest VMs and its main
+ purpose is to notify about guest entering s2idle state.
+
endif # X86_PLATFORM_DEVICES
config P2SB
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index b457de5abf7d798a2ea441976bfd81716e7550bd..7c3c8a403254d1df0fbc65edd69a7f17c6ee853c 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -130,6 +130,9 @@ obj-$(CONFIG_INTEL_SCU_WDT) += intel_scu_wdt.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o
+# Virtual PMC
+obj-$(CONFIG_VIRT_PMC) += virt_pmc.o
+
# Siemens Simatic Industrial PCs
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += siemens/
diff --git a/drivers/platform/x86/virt_pmc.c b/drivers/platform/x86/virt_pmc.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5966bb9048fcaf8d97e1ee2cedff982afc57dc6
--- /dev/null
+++ b/drivers/platform/x86/virt_pmc.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Virtual Power Management Controller Driver
+ *
+ * Author: Grzegorz Jaszczyk <jaz@semihalf.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+#define ACPI_VIRT_PMC_DSM_UUID "9ea49ba3-434a-49a6-be30-37cc55c4d397"
+#define ACPI_VIRT_PMC_NOTIFY 1
+
+static acpi_handle virt_pmc_handle;
+
+static void virt_pmc_s2idle_notify(void)
+{
+ union acpi_object *out_obj;
+ guid_t dsm_guid;
+
+ guid_parse(ACPI_VIRT_PMC_DSM_UUID, &dsm_guid);
+
+ out_obj = acpi_evaluate_dsm(virt_pmc_handle, &dsm_guid,
+ 0, ACPI_VIRT_PMC_NOTIFY, NULL);
+
+ acpi_handle_debug(virt_pmc_handle, "_DSM function %u evaluation %s\n",
+ ACPI_VIRT_PMC_NOTIFY, out_obj ? "successful" : "failed");
+
+ ACPI_FREE(out_obj);
+}
+
+static struct acpi_s2idle_dev_ops pmc_s2idle_dev_ops = {
+ .check = virt_pmc_s2idle_notify,
+};
+
+static int virt_pmc_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ guid_t dsm_guid;
+
+ virt_pmc_handle = ACPI_HANDLE(&pdev->dev);
+
+ guid_parse(ACPI_VIRT_PMC_DSM_UUID, &dsm_guid);
+
+ if (!acpi_check_dsm(virt_pmc_handle, &dsm_guid, 0,
+ 1 << ACPI_VIRT_PMC_NOTIFY)) {
+ dev_err(&pdev->dev, "DSM method doesn't support ACPI_VIRT_PMC_NOTIFY\n");
+ return -ENODEV;
+ }
+
+ err = acpi_register_lps0_dev(&pmc_s2idle_dev_ops);
+ if (err)
+ dev_err(&pdev->dev, "failed to register LPS0 sleep handler\n");
+
+ return err;
+}
+
+static int virt_pmc_remove(struct platform_device *pdev)
+{
+ acpi_unregister_lps0_dev(&pmc_s2idle_dev_ops);
+
+ return 0;
+}
+
+static const struct acpi_device_id virt_pmc_acpi_ids[] = {
+ {"HYPE0001", 0}, /* _HID for XXX Power Engine, _CID PNP0D80*/
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, virt_pmc_acpi_ids);
+
+static struct platform_driver virt_pmc_driver = {
+ .driver = {
+ .name = "virtual_pmc",
+ .acpi_match_table = ACPI_PTR(virt_pmc_acpi_ids),
+ },
+ .probe = virt_pmc_probe,
+ .remove = virt_pmc_remove,
+};
+
+module_platform_driver(virt_pmc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtual PMC Driver");
--
2.34.1