| 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 |
| |