platform-driver-x86.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver
@ 2022-07-12 14:58 Shyam Sundar S K
  2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
                   ` (14 more replies)
  0 siblings, 15 replies; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

AMD PMF Driver (a.k.a Platform Management Framework) provides a
centralized framework based on sensor inputs, OS hints, platform state
and APU metrics to dynamically manage perf, power and system thermals.

The goal of the driver is to enhance end user experience by making AMD
PCs smarter, quieter, power efficient by adapting to user behavior and
environment.

The larger goals include:

- Enable easy customization of smart PC solutions by OEMs.
- Provide a framework for OEMs to add in custom algorithms and solutions
- Improve standby and dynamic platform power through active power
  management of platform devices.

In this series, support for following features has been added.
- "Static Power Slider" meant to manage the power budget based on the
  power modes or the slider position.
- "Auto Mode" tracks the moving power average of the APU and takes
  intelligent decisions to switch between different modes.
- "Cool n Quiet Framework (CnQF)" is an extension to the static slider,
  where the system power can be boosted or throttled independent
  of the selected slider position.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Mario Limonciello (1):
  ACPI: platform_profile: Add support for notification chains

Shyam Sundar S K (14):
  platform/x86/amd/pmf: Add support for PMF core layer
  platform/x86/amd/pmf: Add support for PMF APCI layer
  platform/x86/amd/pmf: Add support SPS PMF feature
  platform/x86/amd/pmf: Add debugfs information
  platform/x86/amd/pmf: Add heartbeat signal support
  platform/x86/amd/pmf: Add fan control support
  platform/x86/amd/pmf: Get performance metrics from PMFW
  platform/x86/amd/pmf: Add support for CnQF
  platform/x86/amd/pmf: Add sysfs to toggle CnQF
  Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF
  platform/x86/amd/pmf: Add support for Auto mode feature
  platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  platform/x86/amd/pmf: Force load driver on older supported platforms
  MAINTAINERS: Add AMD PMF driver entry

 Documentation/ABI/testing/sysfs-amd-pmf  |  11 +
 MAINTAINERS                              |   7 +
 drivers/acpi/platform_profile.c          |  26 ++
 drivers/platform/x86/amd/Kconfig         |   2 +
 drivers/platform/x86/amd/Makefile        |   1 +
 drivers/platform/x86/amd/pmf/Kconfig     |  16 +
 drivers/platform/x86/amd/pmf/Makefile    |   9 +
 drivers/platform/x86/amd/pmf/acpi.c      | 453 ++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/auto-mode.c | 339 +++++++++++++++++
 drivers/platform/x86/amd/pmf/cnqf.c      | 379 +++++++++++++++++++
 drivers/platform/x86/amd/pmf/core.c      | 431 +++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h       | 457 +++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/sps.c       | 166 ++++++++
 include/linux/platform_profile.h         |   1 +
 14 files changed, 2298 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-amd-pmf
 create mode 100644 drivers/platform/x86/amd/pmf/Kconfig
 create mode 100644 drivers/platform/x86/amd/pmf/Makefile
 create mode 100644 drivers/platform/x86/amd/pmf/acpi.c
 create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c
 create mode 100644 drivers/platform/x86/amd/pmf/cnqf.c
 create mode 100644 drivers/platform/x86/amd/pmf/core.c
 create mode 100644 drivers/platform/x86/amd/pmf/pmf.h
 create mode 100644 drivers/platform/x86/amd/pmf/sps.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-12 15:03   ` Limonciello, Mario
  2022-07-27 20:38   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer Shyam Sundar S K
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mario Limonciello, Shyam Sundar S K

From: Mario Limonciello <mario.limonciello@amd.com>

Allow other drivers to react to determine current active profile
and react to platform profile changes.

Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/acpi/platform_profile.c  | 26 ++++++++++++++++++++++++++
 include/linux/platform_profile.h |  1 +
 2 files changed, 27 insertions(+)

diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index d418462ab791..7e12a1f30f06 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -49,6 +49,32 @@ static ssize_t platform_profile_choices_show(struct device *dev,
 	return len;
 }
 
+int platform_profile_get(enum platform_profile_option *profile)
+{
+	int err;
+
+	err = mutex_lock_interruptible(&profile_lock);
+	if (err)
+		return err;
+
+	if (!cur_profile) {
+		mutex_unlock(&profile_lock);
+		return -ENODEV;
+	}
+
+	err = cur_profile->profile_get(cur_profile, profile);
+	mutex_unlock(&profile_lock);
+	if (err)
+		return err;
+
+	/* Check that profile is valid index */
+	if (WARN_ON((*profile < 0) || (*profile >= ARRAY_SIZE(profile_names))))
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(platform_profile_get);
+
 static ssize_t platform_profile_show(struct device *dev,
 					struct device_attribute *attr,
 					char *buf)
diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
index e5cbb6841f3a..2395be670dfd 100644
--- a/include/linux/platform_profile.h
+++ b/include/linux/platform_profile.h
@@ -37,5 +37,6 @@ struct platform_profile_handler {
 int platform_profile_register(struct platform_profile_handler *pprof);
 int platform_profile_remove(void);
 void platform_profile_notify(void);
+int platform_profile_get(enum platform_profile_option *profile);
 
 #endif  /*_PLATFORM_PROFILE_H_*/
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
  2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 13:57   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer Shyam Sundar S K
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

PMF core layer is meant to abstract the common functionalities
across PMF features. This layer also does the plumbing work
like setting up the mailbox channel for the communication
between the PMF driver and the PMFW (Power Management Firmware)
running on the SMU.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/Kconfig      |   2 +
 drivers/platform/x86/amd/Makefile     |   1 +
 drivers/platform/x86/amd/pmf/Kconfig  |  16 ++
 drivers/platform/x86/amd/pmf/Makefile |   8 +
 drivers/platform/x86/amd/pmf/core.c   | 235 ++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h    |  47 ++++++
 6 files changed, 309 insertions(+)
 create mode 100644 drivers/platform/x86/amd/pmf/Kconfig
 create mode 100644 drivers/platform/x86/amd/pmf/Makefile
 create mode 100644 drivers/platform/x86/amd/pmf/core.c
 create mode 100644 drivers/platform/x86/amd/pmf/pmf.h

diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig
index c0d0a3c5170c..a825af8126c8 100644
--- a/drivers/platform/x86/amd/Kconfig
+++ b/drivers/platform/x86/amd/Kconfig
@@ -3,6 +3,8 @@
 # AMD x86 Platform Specific Drivers
 #
 
+source "drivers/platform/x86/amd/pmf/Kconfig"
+
 config AMD_PMC
 	tristate "AMD SoC PMC driver"
 	depends on ACPI && PCI && RTC_CLASS
diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile
index a03fbb08e808..2c229198e24c 100644
--- a/drivers/platform/x86/amd/Makefile
+++ b/drivers/platform/x86/amd/Makefile
@@ -8,3 +8,4 @@ amd-pmc-y			:= pmc.o
 obj-$(CONFIG_AMD_PMC)		+= amd-pmc.o
 amd_hsmp-y			:= hsmp.o
 obj-$(CONFIG_AMD_HSMP)		+= amd_hsmp.o
+obj-$(CONFIG_AMD_PMF)		+= pmf/
diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig
new file mode 100644
index 000000000000..2a5f72419515
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# AMD PMF Driver
+#
+
+config AMD_PMF
+	tristate "AMD Platform Management Framework"
+	depends on ACPI
+	help
+	  This driver provides support for the AMD Platform Management Framework.
+	  The goal is to enhance end user experience by making AMD PCs smarter,
+	  quiter, power efficient by adapting to user behavior and environment.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called amd_pmf.
+
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
new file mode 100644
index 000000000000..459005f659e5
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for linux/drivers/platform/x86/amd/pmf
+# AMD Platform Management Framework
+#
+
+obj-$(CONFIG_AMD_PMF) += amd-pmf.o
+amd-pmf-objs := core.o
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
new file mode 100644
index 000000000000..aef97965c181
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Platform Management Framework Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include "pmf.h"
+
+/* PMF-SMU communication registers */
+#define AMD_PMF_REGISTER_MESSAGE	0xA18
+#define AMD_PMF_REGISTER_RESPONSE	0xA78
+#define AMD_PMF_REGISTER_ARGUMENT	0xA58
+
+/* Base address of SMU for mapping physical address to virtual address */
+#define AMD_PMF_SMU_INDEX_ADDRESS	0xB8
+#define AMD_PMF_SMU_INDEX_DATA		0xBC
+#define AMD_PMF_MAPPING_SIZE		0x01000
+#define AMD_PMF_BASE_ADDR_OFFSET	0x10000
+#define AMD_PMF_BASE_ADDR_LO		0x13B102E8
+#define AMD_PMF_BASE_ADDR_HI		0x13B102EC
+#define AMD_PMF_BASE_ADDR_LO_MASK	GENMASK(15, 0)
+#define AMD_PMF_BASE_ADDR_HI_MASK	GENMASK(31, 20)
+
+/* SMU Response Codes */
+#define AMD_PMF_RESULT_OK                    0x01
+#define AMD_PMF_RESULT_CMD_REJECT_BUSY       0xFC
+#define AMD_PMF_RESULT_CMD_REJECT_PREREQ     0xFD
+#define AMD_PMF_RESULT_CMD_UNKNOWN           0xFE
+#define AMD_PMF_RESULT_FAILED                0xFF
+
+/* List of supported CPU ids */
+#define AMD_CPU_ID_PS			0x14e8
+
+#define PMF_MSG_DELAY_MIN_US		50
+#define RESPONSE_REGISTER_LOOP_MAX	20000
+
+#define DELAY_MIN_US	2000
+#define DELAY_MAX_US	3000
+
+static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
+{
+	return ioread32(dev->regbase + reg_offset);
+}
+
+static inline void amd_pmf_reg_write(struct amd_pmf_dev *dev, int reg_offset, u32 val)
+{
+	iowrite32(val, dev->regbase + reg_offset);
+}
+
+static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
+{
+	u32 value;
+
+	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE);
+	dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
+
+	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
+	dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
+
+	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE);
+	dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
+}
+
+int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
+{
+	int rc;
+	u32 val;
+
+	mutex_lock(&dev->lock);
+
+	/* Wait until we get a valid response */
+	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
+				val, val != 0, PMF_MSG_DELAY_MIN_US,
+				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
+	if (rc) {
+		dev_err(dev->dev, "failed to talk to SMU\n");
+		goto out_unlock;
+	}
+
+	/* Write zero to response register */
+	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0);
+
+	/* Write argument into argument register */
+	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg);
+
+	/* Write message ID to message ID register */
+	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message);
+
+	/* Wait until we get a valid response */
+	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
+				val, val != 0, PMF_MSG_DELAY_MIN_US,
+				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
+	if (rc) {
+		dev_err(dev->dev, "SMU response timed out\n");
+		goto out_unlock;
+	}
+
+	switch (val) {
+	case AMD_PMF_RESULT_OK:
+		if (get) {
+			/* PMFW may take longer time to return back the data */
+			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
+			*data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
+		}
+		break;
+	case AMD_PMF_RESULT_CMD_REJECT_BUSY:
+		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
+		rc = -EBUSY;
+		goto out_unlock;
+	case AMD_PMF_RESULT_CMD_UNKNOWN:
+		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
+		rc = -EINVAL;
+		goto out_unlock;
+	case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
+	case AMD_PMF_RESULT_FAILED:
+	default:
+		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
+		rc = -EIO;
+		goto out_unlock;
+	}
+
+out_unlock:
+	mutex_unlock(&dev->lock);
+	amd_pmf_dump_registers(dev);
+	return rc;
+}
+
+static const struct pci_device_id pmf_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
+	{ }
+};
+
+static const struct acpi_device_id amd_pmf_acpi_ids[] = {
+	{"AMDI0102", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
+
+static int amd_pmf_probe(struct platform_device *pdev)
+{
+	struct amd_pmf_dev *dev;
+	struct pci_dev *rdev;
+	u32 base_addr_lo;
+	u32 base_addr_hi;
+	u64 base_addr;
+	u32 val;
+	int err;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev = &pdev->dev;
+
+	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
+	if (!rdev || !pci_match_id(pmf_pci_ids, rdev)) {
+		pci_dev_put(rdev);
+		return -ENODEV;
+	}
+
+	dev->cpu_id = rdev->device;
+	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO);
+	if (err) {
+		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
+		pci_dev_put(rdev);
+		return pcibios_err_to_errno(err);
+	}
+
+	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
+	if (err) {
+		pci_dev_put(rdev);
+		return pcibios_err_to_errno(err);
+	}
+
+	base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
+
+	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI);
+	if (err) {
+		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
+		pci_dev_put(rdev);
+		return pcibios_err_to_errno(err);
+	}
+
+	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
+	if (err) {
+		pci_dev_put(rdev);
+		return pcibios_err_to_errno(err);
+	}
+
+	base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK;
+	pci_dev_put(rdev);
+	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
+
+	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMF_BASE_ADDR_OFFSET,
+				    AMD_PMF_MAPPING_SIZE);
+	if (!dev->regbase)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, dev);
+
+	mutex_init(&dev->lock);
+	dev_info(dev->dev, "registered PMF device successfully\n");
+
+	return 0;
+}
+
+static int amd_pmf_remove(struct platform_device *pdev)
+{
+	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
+
+	mutex_destroy(&dev->lock);
+	kfree(dev->buf);
+	return 0;
+}
+
+static struct platform_driver amd_pmf_driver = {
+	.driver = {
+		.name = "amd-pmf",
+		.acpi_match_table = amd_pmf_acpi_ids,
+	},
+	.probe = amd_pmf_probe,
+	.remove = amd_pmf_remove,
+};
+module_platform_driver(amd_pmf_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
new file mode 100644
index 000000000000..ab773aa5a6e1
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Platform Management Framework Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+#ifndef PMF_H
+#define PMF_H
+
+/* Message Definitions */
+#define SET_SPL				0x03 /* SPL: Sustained Power Limit */
+#define SET_SPPT			0x05 /* SPPT: Slow Package Power Tracking */
+#define SET_FPPT			0x07 /* FPPT: Fast Package Power Tracking */
+#define GET_SPL				0x0B
+#define GET_SPPT			0x0D
+#define GET_FPPT			0x0F
+#define SET_DRAM_ADDR_HIGH	0x14
+#define SET_DRAM_ADDR_LOW	0x15
+#define SET_TRANSFER_TABLE	0x16
+#define SET_STT_MIN_LIMIT	0x18 /* STT: Skin Temperature Tracking */
+#define SET_STT_LIMIT_APU	0x19
+#define SET_STT_LIMIT_HS2	0x1A
+#define SET_SPPT_APU_ONLY	0x1D
+#define GET_SPPT_APU_ONLY	0x1E
+#define GET_STT_MIN_LIMIT	0x1F
+#define GET_STT_LIMIT_APU	0x20
+#define GET_STT_LIMIT_HS2	0x21
+
+struct amd_pmf_dev {
+	void __iomem *regbase;
+	void __iomem *smu_virt_addr;
+	void *buf;
+	u32 base_addr;
+	u32 cpu_id;
+	u32 hi;
+	u32 low;
+	struct device *dev;
+	struct mutex lock; /* protects the PMF interface */
+};
+
+/* Core Layer */
+int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
+
+#endif /* PMF_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
  2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
  2022-07-12 14:58 ` [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 13:57   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature Shyam Sundar S K
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

PMF driver implements the ACPI methods as defined by AMD for PMF Support.
The ACPI layer acts as a glue that helps in providing the infrastructure
for OEMs customization.

OEMs can refer to PMF support documentation to decide on the list of
functions to be supported on their specific platform model.

AMD mandates that PMF ACPI fn0 and fn1 to be implemented which
provides the set of functions, params and the notifications that
would be sent to PMF driver so that PMF driver can adapt and
react.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/Makefile |   2 +-
 drivers/platform/x86/amd/pmf/acpi.c   | 188 ++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/core.c   |   1 +
 drivers/platform/x86/amd/pmf/pmf.h    |  32 +++++
 4 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/x86/amd/pmf/acpi.c

diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 459005f659e5..2617eba773ce 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -5,4 +5,4 @@
 #
 
 obj-$(CONFIG_AMD_PMF) += amd-pmf.o
-amd-pmf-objs := core.o
+amd-pmf-objs := core.o acpi.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
new file mode 100644
index 000000000000..addcaae5675c
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Platform Management Framework Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+
+#include <linux/acpi.h>
+#include "pmf.h"
+
+#define APMF_METHOD "\\_SB_.PMF_.APMF"
+
+static unsigned long supported_func;
+
+static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask);
+
+static union acpi_object *apmf_if_call(struct apmf_if *apmf_if, int func, struct acpi_buffer *param)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_object_list apmf_if_arg_list;
+	union acpi_object apmf_if_args[2];
+	acpi_status status;
+
+	apmf_if_arg_list.count = 2;
+	apmf_if_arg_list.pointer = &apmf_if_args[0];
+
+	apmf_if_args[0].type = ACPI_TYPE_INTEGER;
+	apmf_if_args[0].integer.value = func;
+
+	if (param) {
+		apmf_if_args[1].type = ACPI_TYPE_BUFFER;
+		apmf_if_args[1].buffer.length = param->length;
+		apmf_if_args[1].buffer.pointer = param->pointer;
+	} else {
+		apmf_if_args[1].type = ACPI_TYPE_INTEGER;
+		apmf_if_args[1].integer.value = 0;
+	}
+
+	status = acpi_evaluate_object(apmf_if->handle, NULL, &apmf_if_arg_list, &buffer);
+	if (ACPI_FAILURE(status)) {
+		pr_err("PMF: APMF method call failed\n");
+		if (status != AE_NOT_FOUND)
+			kfree(buffer.pointer);
+
+		return NULL;
+	}
+
+	return buffer.pointer;
+}
+
+static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
+{
+	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
+}
+
+static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
+{
+	struct apmf_verify_interface output;
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_VERIFY_INTERFACE, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(output)) {
+		dev_err(pdev->dev, "buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(output), size);
+	memset(&output, 0, sizeof(output));
+	memcpy(&output, info->buffer.pointer, size);
+	apmf_if_parse_functions(&ampf_if->func, output.supported_functions);
+	supported_func = output.supported_functions;
+	dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
+		output.supported_functions, output.notification_mask);
+
+out:
+	kfree(info);
+	return err;
+}
+
+int is_apmf_func_supported(unsigned long index)
+{
+	/* If bit-n is set, that indicates function n+1 is supported */
+	return ((supported_func & (1 << (index - 1))) != 0);
+}
+
+static int apmf_get_system_params(struct apmf_if *ampf_if)
+{
+	struct apmf_system_params params;
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_GET_SYS_PARAMS, NULL);
+	if (!info) {
+		err = -EIO;
+		goto out;
+	}
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(params)) {
+		pr_err("PMF: buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(params), size);
+	memset(&params, 0, sizeof(params));
+	memcpy(&params, info->buffer.pointer, size);
+
+	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
+		 params.valid_mask,
+		 params.flags,
+		 params.command_code);
+	params.flags = params.flags & params.valid_mask;
+
+out:
+	kfree(info);
+	return err;
+}
+
+static acpi_handle apmf_if_probe_handle(void)
+{
+	acpi_handle handle = NULL;
+	char acpi_method_name[255] = { 0 };
+	struct acpi_buffer buffer = { sizeof(acpi_method_name), acpi_method_name };
+	acpi_status status;
+
+	status = acpi_get_handle(NULL, (acpi_string) APMF_METHOD, &handle);
+	if (ACPI_SUCCESS(status))
+		goto out;
+
+	pr_err("PMF: APMF handle not found\n");
+	return NULL;
+
+out:
+	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+	pr_debug("PMF: APMF handle %s found\n", acpi_method_name);
+	return handle;
+}
+
+int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
+{
+	acpi_handle apmf_if_handle;
+	struct apmf_if *apmf_if;
+	int ret;
+
+	apmf_if_handle = apmf_if_probe_handle();
+	if (!apmf_if_handle)
+		goto out;
+
+	apmf_if = kzalloc(sizeof(*apmf_if), GFP_KERNEL);
+	if (!apmf_if)
+		goto out;
+
+	apmf_if->handle = apmf_if_handle;
+
+	ret = apmf_if_verify_interface(pmf_dev, apmf_if);
+	if (ret) {
+		dev_err(pmf_dev->dev, "APMF verify interface failed :%d\n", ret);
+		kfree(apmf_if);
+		goto out;
+	}
+	pmf_dev->apmf_if = apmf_if;
+
+	if (apmf_if->func.system_params) {
+		ret = apmf_get_system_params(apmf_if);
+		if (ret) {
+			dev_err(pmf_dev->dev, "APMF apmf_get_system_params failed :%d\n", ret);
+			kfree(apmf_if);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index aef97965c181..c5002b7bb904 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -204,6 +204,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
 	if (!dev->regbase)
 		return -ENOMEM;
 
+	apmf_acpi_init(dev);
 	platform_set_drvdata(pdev, dev);
 
 	mutex_init(&dev->lock);
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index ab773aa5a6e1..40f038eb6197 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -10,6 +10,12 @@
 #ifndef PMF_H
 #define PMF_H
 
+#include <linux/acpi.h>
+
+/* APMF Functions */
+#define APMF_FUNC_VERIFY_INTERFACE			0
+#define APMF_FUNC_GET_SYS_PARAMS			1
+
 /* Message Definitions */
 #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
 #define SET_SPPT			0x05 /* SPPT: Slow Package Power Tracking */
@@ -29,6 +35,30 @@
 #define GET_STT_LIMIT_APU	0x20
 #define GET_STT_LIMIT_HS2	0x21
 
+/* AMD PMF BIOS interfaces */
+struct apmf_if_functions {
+	bool system_params;
+};
+
+struct apmf_if {
+	acpi_handle handle;
+	struct apmf_if_functions func;
+};
+
+struct apmf_verify_interface {
+	u16 size;
+	u16 version;
+	u32 notification_mask;
+	u32 supported_functions;
+} __packed;
+
+struct apmf_system_params {
+	u16 size;
+	u32 valid_mask;
+	u32 flags;
+	u8 command_code;
+} __packed;
+
 struct amd_pmf_dev {
 	void __iomem *regbase;
 	void __iomem *smu_virt_addr;
@@ -38,10 +68,12 @@ struct amd_pmf_dev {
 	u32 hi;
 	u32 low;
 	struct device *dev;
+	struct apmf_if *apmf_if;
 	struct mutex lock; /* protects the PMF interface */
 };
 
 /* Core Layer */
+int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
 int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
 
 #endif /* PMF_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (2 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 19:29   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information Shyam Sundar S K
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

SPS (a.k.a. Static Power Slider) gives a feel of Windows performance
power slider for the Linux users, where the user selects a certain
mode (like "balanced", "low-power" or "performance") and the thermals
associated with each selected mode gets applied from the silicon
side via the mailboxes defined through PMFW.

PMF driver hooks to platform_profile by reading the PMF ACPI fn9 to
see if the support is being advertised by ACPI interface.

If supported, the PMF driver reacts to platform_profile selection choices
made by the user and adjust the system thermal behavior.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/Makefile |   2 +-
 drivers/platform/x86/amd/pmf/acpi.c   |  29 +++++
 drivers/platform/x86/amd/pmf/core.c   |  26 ++++
 drivers/platform/x86/amd/pmf/pmf.h    |  65 ++++++++++
 drivers/platform/x86/amd/pmf/sps.c    | 166 ++++++++++++++++++++++++++
 5 files changed, 287 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/x86/amd/pmf/sps.c

diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 2617eba773ce..557521a80427 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -5,4 +5,4 @@
 #
 
 obj-$(CONFIG_AMD_PMF) += amd-pmf.o
-amd-pmf-objs := core.o acpi.o
+amd-pmf-objs := core.o acpi.o sps.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index addcaae5675c..fd5ab7a116f0 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -54,6 +54,7 @@ static union acpi_object *apmf_if_call(struct apmf_if *apmf_if, int func, struct
 static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
 {
 	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
+	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
 }
 
 static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
@@ -130,6 +131,34 @@ static int apmf_get_system_params(struct apmf_if *ampf_if)
 	return err;
 }
 
+int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
+				    struct apmf_static_slider_granular_output *data)
+{
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_STATIC_SLIDER_GRANULAR, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(*data)) {
+		pr_err("PMF: buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(*data), size);
+	memset(data, 0, sizeof(*data));
+	memcpy(data, info->buffer.pointer, size);
+
+out:
+	kfree(info);
+	return err;
+}
+
 static acpi_handle apmf_if_probe_handle(void)
 {
 	acpi_handle handle = NULL;
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index c5002b7bb904..b6006e8ee1a1 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
 #include "pmf.h"
 
 /* PMF-SMU communication registers */
@@ -45,6 +46,14 @@
 #define DELAY_MIN_US	2000
 #define DELAY_MAX_US	3000
 
+int amd_pmf_get_power_source(void)
+{
+	if (power_supply_is_system_supplied() > 0)
+		return POWER_SOURCE_AC;
+	else
+		return POWER_SOURCE_DC;
+}
+
 static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
 {
 	return ioread32(dev->regbase + reg_offset);
@@ -138,6 +147,21 @@ static const struct pci_device_id pmf_pci_ids[] = {
 	{ }
 };
 
+static void amd_pmf_init_features(struct amd_pmf_dev *dev)
+{
+	/* Enable Static Slider */
+	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
+		amd_pmf_init_sps(dev);
+		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
+	}
+}
+
+static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
+{
+	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
+		amd_pmf_deinit_sps(dev);
+}
+
 static const struct acpi_device_id amd_pmf_acpi_ids[] = {
 	{"AMDI0102", 0},
 	{ }
@@ -206,6 +230,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
 
 	apmf_acpi_init(dev);
 	platform_set_drvdata(pdev, dev);
+	amd_pmf_init_features(dev);
 
 	mutex_init(&dev->lock);
 	dev_info(dev->dev, "registered PMF device successfully\n");
@@ -218,6 +243,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
 	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
 
 	mutex_destroy(&dev->lock);
+	amd_pmf_deinit_features(dev);
 	kfree(dev->buf);
 	return 0;
 }
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 40f038eb6197..a405987ae653 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -7,14 +7,17 @@
  *
  * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
  */
+
 #ifndef PMF_H
 #define PMF_H
 
 #include <linux/acpi.h>
+#include <linux/platform_profile.h>
 
 /* APMF Functions */
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
+#define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
 
 /* Message Definitions */
 #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
@@ -35,9 +38,12 @@
 #define GET_STT_LIMIT_APU	0x20
 #define GET_STT_LIMIT_HS2	0x21
 
+#define ARG_NONE 0
+
 /* AMD PMF BIOS interfaces */
 struct apmf_if_functions {
 	bool system_params;
+	bool static_slider_granular;
 };
 
 struct apmf_if {
@@ -59,6 +65,30 @@ struct apmf_system_params {
 	u8 command_code;
 } __packed;
 
+enum amd_stt_skin_temp {
+	STT_TEMP_APU,
+	STT_TEMP_HS2,
+	STT_TEMP_COUNT,
+};
+
+enum amd_slider_op {
+	SLIDER_OP_GET,
+	SLIDER_OP_SET,
+};
+
+enum power_source {
+	POWER_SOURCE_AC,
+	POWER_SOURCE_DC,
+	POWER_SOURCE_MAX,
+};
+
+enum power_modes {
+	POWER_MODE_PERFORMANCE,
+	POWER_MODE_BALANCED_POWER,
+	POWER_MODE_POWER_SAVER,
+	POWER_MODE_MAX,
+};
+
 struct amd_pmf_dev {
 	void __iomem *regbase;
 	void __iomem *smu_virt_addr;
@@ -69,11 +99,46 @@ struct amd_pmf_dev {
 	u32 low;
 	struct device *dev;
 	struct apmf_if *apmf_if;
+	enum platform_profile_option current_profile;
+	struct platform_profile_handler pprof;
 	struct mutex lock; /* protects the PMF interface */
 };
 
+struct apmf_sps_prop_granular {
+	u32 fppt;
+	u32 sppt;
+	u32 sppt_apu_only;
+	u32 spl;
+	u32 stt_min;
+	u8 stt_skin_temp[STT_TEMP_COUNT];
+	u32 fan_id;
+} __packed;
+
+/* Static Slider */
+struct apmf_static_slider_granular_output {
+	u16 size;
+	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX * POWER_MODE_MAX];
+};
+
+struct amd_pmf_static_slider_granular {
+	u16 size;
+	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
+};
+
 /* Core Layer */
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
+int is_apmf_func_supported(unsigned long index);
 int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
+int amd_pmf_get_power_source(void);
+
+/* SPS Layer */
+u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
+void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
+			   struct amd_pmf_static_slider_granular *table);
+int amd_pmf_init_sps(struct amd_pmf_dev *dev);
+void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
+int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
+				    struct apmf_static_slider_granular_output *output);
+void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
 
 #endif /* PMF_H */
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
new file mode 100644
index 000000000000..25289cf5608c
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Platform Management Framework (PMF) Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+
+#include "pmf.h"
+
+static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
+			       enum platform_profile_option *profile);
+static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
+			       enum platform_profile_option profile);
+static struct amd_pmf_static_slider_granular config_store;
+
+void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
+{
+	struct apmf_static_slider_granular_output output;
+	int i, j, idx = 0;
+
+	memset(&config_store, 0, sizeof(config_store));
+
+	if (dev->apmf_if->func.static_slider_granular) {
+		apmf_get_static_slider_granular(dev->apmf_if, &output);
+
+		for (i = 0; i < POWER_SOURCE_MAX; i++) {
+			for (j = 0; j < POWER_MODE_MAX; j++) {
+				config_store.prop[i][j].spl = output.prop[idx].spl;
+				config_store.prop[i][j].sppt = output.prop[idx].sppt;
+				config_store.prop[i][j].sppt_apu_only =
+							output.prop[idx].sppt_apu_only;
+				config_store.prop[i][j].fppt = output.prop[idx].fppt;
+				config_store.prop[i][j].stt_min = output.prop[idx].stt_min;
+				config_store.prop[i][j].stt_skin_temp[STT_TEMP_APU] =
+						output.prop[idx].stt_skin_temp[STT_TEMP_APU];
+				config_store.prop[i][j].stt_skin_temp[STT_TEMP_HS2] =
+						output.prop[idx].stt_skin_temp[STT_TEMP_HS2];
+				config_store.prop[i][j].fan_id = output.prop[idx].fan_id;
+				idx++;
+			}
+		}
+	}
+}
+
+void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
+			   struct amd_pmf_static_slider_granular *table)
+{
+	int src = amd_pmf_get_power_source();
+
+	if (op == SLIDER_OP_SET) {
+		amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
+		amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
+				 config_store.prop[src][idx].sppt_apu_only, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
+				 config_store.prop[src][idx].stt_min, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
+	} else if (op == SLIDER_OP_GET) {
+		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
+		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
+		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt);
+		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
+				 &table->prop[src][idx].sppt_apu_only);
+		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
+				 &table->prop[src][idx].stt_min);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
+				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
+				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]);
+	}
+}
+
+static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
+			       enum platform_profile_option *profile)
+{
+	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
+
+	*profile = pmf->current_profile;
+	return 0;
+}
+
+u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
+{
+	u8 mode;
+
+	switch (pmf->current_profile) {
+	case PLATFORM_PROFILE_PERFORMANCE:
+		mode = 0;
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		mode = 1;
+		break;
+	case PLATFORM_PROFILE_LOW_POWER:
+		mode = 2;
+		break;
+	default:
+		dev_err(pmf->dev, "Unknown Platform Profile.\n");
+		break;
+	}
+
+	return mode;
+}
+
+int amd_pmf_profile_set(struct platform_profile_handler *pprof,
+			enum platform_profile_option profile)
+{
+	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
+	u8 mode;
+
+	pmf->current_profile = profile;
+	mode = amd_pmf_get_pprof_modes(pmf);
+	amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
+	return 0;
+}
+
+static void amd_pmf_profile_refresh(struct amd_pmf_dev *dev)
+{
+	enum platform_profile_option profile;
+
+	/* Set intially to Balanced Mode */
+	profile = PLATFORM_PROFILE_BALANCED;
+
+	if (profile != dev->current_profile) {
+		dev->current_profile = profile;
+		platform_profile_notify();
+	}
+}
+
+int amd_pmf_init_sps(struct amd_pmf_dev *dev)
+{
+	int err = 0;
+
+	dev->pprof.profile_get = amd_pmf_profile_get;
+	dev->pprof.profile_set = amd_pmf_profile_set;
+
+	/* Setup supported modes */
+	set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
+
+	/* Create platform_profile structure and register */
+	err = platform_profile_register(&dev->pprof);
+	if (err) {
+		dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
+			err);
+		return -EEXIST;
+	}
+	/* Ensure initial values are correct */
+	amd_pmf_profile_refresh(dev);
+
+	amd_pmf_load_defaults_sps(dev);
+
+	return err;
+}
+
+void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
+{
+	platform_profile_remove();
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (3 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 19:50   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support Shyam Sundar S K
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

Add debugfs support to the PMF driver so that using this interface the
live counters from the PMFW can be queried to see if the power parameters
are getting set properly when a certain power mode change happens.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/core.c | 46 +++++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h  |  3 ++
 2 files changed, 49 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index b6006e8ee1a1..3d2af6a8e5e4 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -8,6 +8,7 @@
  * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
  */
 
+#include <linux/debugfs.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/pci.h>
@@ -46,6 +47,49 @@
 #define DELAY_MIN_US	2000
 #define DELAY_MAX_US	3000
 
+#ifdef CONFIG_DEBUG_FS
+static int current_power_limits_show(struct seq_file *seq, void *unused)
+{
+	struct amd_pmf_dev *dev = seq->private;
+	struct amd_pmf_static_slider_granular table;
+	int mode, src = 0;
+
+	mode = amd_pmf_get_pprof_modes(dev);
+	src = amd_pmf_get_power_source();
+	amd_pmf_update_slider(dev, SLIDER_OP_GET, mode, &table);
+	seq_printf(seq, "spl:%u fppt:%u sppt:%u sppt_apu_only:%u stt_min:%u stt[APU]:%u stt[HS2]: %u\n",
+		   table.prop[src][mode].spl,
+		   table.prop[src][mode].fppt,
+		   table.prop[src][mode].sppt,
+		   table.prop[src][mode].sppt_apu_only,
+		   table.prop[src][mode].stt_min,
+		   table.prop[src][mode].stt_skin_temp[STT_TEMP_APU],
+		   table.prop[src][mode].stt_skin_temp[STT_TEMP_HS2]);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(current_power_limits);
+
+static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
+{
+	debugfs_remove_recursive(dev->dbgfs_dir);
+}
+
+static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
+{
+	dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
+	debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
+			    &current_power_limits_fops);
+}
+#else
+static inline void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
+{
+}
+
+static inline void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
 int amd_pmf_get_power_source(void)
 {
 	if (power_supply_is_system_supplied() > 0)
@@ -231,6 +275,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
 	apmf_acpi_init(dev);
 	platform_set_drvdata(pdev, dev);
 	amd_pmf_init_features(dev);
+	amd_pmf_dbgfs_register(dev);
 
 	mutex_init(&dev->lock);
 	dev_info(dev->dev, "registered PMF device successfully\n");
@@ -244,6 +289,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
 
 	mutex_destroy(&dev->lock);
 	amd_pmf_deinit_features(dev);
+	amd_pmf_dbgfs_unregister(dev);
 	kfree(dev->buf);
 	return 0;
 }
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index a405987ae653..77021ef4bfde 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -102,6 +102,9 @@ struct amd_pmf_dev {
 	enum platform_profile_option current_profile;
 	struct platform_profile_handler pprof;
 	struct mutex lock; /* protects the PMF interface */
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
 };
 
 struct apmf_sps_prop_granular {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (4 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 19:53   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support Shyam Sundar S K
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
not receive the signal after a period of time, it can infer that AMDPMF
has hung or failed to load.

In this situation, BIOS can fallback to legacy operations. OEM can modify
the time interval of the signal or completely disable signals through
ACPI Method.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/acpi.c | 42 +++++++++++++++++++++++++++--
 drivers/platform/x86/amd/pmf/core.c |  1 +
 drivers/platform/x86/amd/pmf/pmf.h  | 10 +++++++
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index fd5ab7a116f0..60ffc9ba4adc 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -97,6 +97,7 @@ int is_apmf_func_supported(unsigned long index)
 
 static int apmf_get_system_params(struct apmf_if *ampf_if)
 {
+	struct apmf_notification_cfg *n = &ampf_if->notify_cfg;
 	struct apmf_system_params params;
 	union acpi_object *info;
 	size_t size;
@@ -120,11 +121,13 @@ static int apmf_get_system_params(struct apmf_if *ampf_if)
 	memset(&params, 0, sizeof(params));
 	memcpy(&params, info->buffer.pointer, size);
 
-	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
+	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
 		 params.valid_mask,
 		 params.flags,
-		 params.command_code);
+		 params.command_code,
+		 params.heartbeat_int);
 	params.flags = params.flags & params.valid_mask;
+	n->hb_interval = params.heartbeat_int;
 
 out:
 	kfree(info);
@@ -159,6 +162,26 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
 	return err;
 }
 
+static void apmf_sbios_heartbeat_notify(struct work_struct *work)
+{
+	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
+	struct apmf_notification_cfg *n = &dev->apmf_if->notify_cfg;
+	union acpi_object *info;
+	int err = 0;
+
+	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
+	info = apmf_if_call(dev->apmf_if, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
+	if (!info) {
+		err = -EIO;
+		goto out;
+	}
+
+	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(n->hb_interval * 1000));
+
+out:
+	kfree(info);
+}
+
 static acpi_handle apmf_if_probe_handle(void)
 {
 	acpi_handle handle = NULL;
@@ -179,8 +202,15 @@ static acpi_handle apmf_if_probe_handle(void)
 	return handle;
 }
 
+void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
+{
+	if (pmf_dev->apmf_if->func.sbios_heartbeat)
+		cancel_delayed_work_sync(&pmf_dev->heart_beat);
+}
+
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 {
+	struct apmf_notification_cfg *n;
 	acpi_handle apmf_if_handle;
 	struct apmf_if *apmf_if;
 	int ret;
@@ -202,6 +232,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 		goto out;
 	}
 	pmf_dev->apmf_if = apmf_if;
+	n = &apmf_if->notify_cfg;
 
 	if (apmf_if->func.system_params) {
 		ret = apmf_get_system_params(apmf_if);
@@ -212,6 +243,13 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 		}
 	}
 
+	if (n->hb_interval) {
+		apmf_if->func.sbios_heartbeat = true;
+		/* send heartbeats only if the interval is not zero */
+		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
+		schedule_delayed_work(&pmf_dev->heart_beat, 0);
+	}
+
 out:
 	return ret;
 }
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 3d2af6a8e5e4..ff26928e6a49 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -289,6 +289,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
 
 	mutex_destroy(&dev->lock);
 	amd_pmf_deinit_features(dev);
+	apmf_acpi_deinit(dev);
 	amd_pmf_dbgfs_unregister(dev);
 	kfree(dev->buf);
 	return 0;
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 77021ef4bfde..504e1ae79706 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -17,6 +17,7 @@
 /* APMF Functions */
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
+#define APMF_FUNC_SBIOS_HEARTBEAT			4
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
 
 /* Message Definitions */
@@ -43,12 +44,18 @@
 /* AMD PMF BIOS interfaces */
 struct apmf_if_functions {
 	bool system_params;
+	bool sbios_heartbeat;
 	bool static_slider_granular;
 };
 
+struct apmf_notification_cfg {
+	int hb_interval; /* SBIOS heartbeat interval */
+};
+
 struct apmf_if {
 	acpi_handle handle;
 	struct apmf_if_functions func;
+	struct apmf_notification_cfg notify_cfg;
 };
 
 struct apmf_verify_interface {
@@ -63,6 +70,7 @@ struct apmf_system_params {
 	u32 valid_mask;
 	u32 flags;
 	u8 command_code;
+	u32 heartbeat_int;
 } __packed;
 
 enum amd_stt_skin_temp {
@@ -101,6 +109,7 @@ struct amd_pmf_dev {
 	struct apmf_if *apmf_if;
 	enum platform_profile_option current_profile;
 	struct platform_profile_handler pprof;
+	struct delayed_work heart_beat;
 	struct mutex lock; /* protects the PMF interface */
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	struct dentry *dbgfs_dir;
@@ -130,6 +139,7 @@ struct amd_pmf_static_slider_granular {
 
 /* Core Layer */
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
+void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
 int is_apmf_func_supported(unsigned long index);
 int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
 int amd_pmf_get_power_source(void);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (5 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 20:11   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW Shyam Sundar S K
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

PMF has a generic interface defined via PMF ACPI fn9 for influencing fan
policy during mode switch.

PMF ACPI fn9 will normally be invoked when AMDPMF needs to change the fan
table index for the EC. When AMDPMF is loaded this function will be invoked
to change fan speed. OEM can also choose to report the actual fan table
index and fan RPM to PMF through OEM structure.

This information will be communicated by PMF driver to customer BIOS.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/acpi.c | 26 ++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h  | 17 +++++++++++++++++
 2 files changed, 43 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index 60ffc9ba4adc..e4822c6f4259 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
 {
 	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
 	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
+	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
 }
 
 static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
@@ -182,6 +183,31 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
 	kfree(info);
 }
 
+int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
+{
+	union acpi_object *info;
+	struct apmf_fan_idx args;
+	struct acpi_buffer params;
+	int err = 0;
+
+	args.size = sizeof(args);
+	args.fan_ctl_mode = manual;
+	args.fan_ctl_idx = idx;
+
+	params.length = sizeof(args);
+	params.pointer = (void *)&args;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_SET_FAN_IDX, &params);
+	if (!info) {
+		err = -EIO;
+		goto out;
+	}
+
+out:
+	kfree(info);
+	return err;
+}
+
 static acpi_handle apmf_if_probe_handle(void)
 {
 	acpi_handle handle = NULL;
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 504e1ae79706..1e9e2e498d15 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -18,6 +18,7 @@
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
 #define APMF_FUNC_SBIOS_HEARTBEAT			4
+#define APMF_FUNC_SET_FAN_IDX				7
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
 
 /* Message Definitions */
@@ -39,12 +40,16 @@
 #define GET_STT_LIMIT_APU	0x20
 #define GET_STT_LIMIT_HS2	0x21
 
+/* Fan Index for Auto Mode */
+#define FAN_INDEX_AUTO 0xFFFFFFFF
+
 #define ARG_NONE 0
 
 /* AMD PMF BIOS interfaces */
 struct apmf_if_functions {
 	bool system_params;
 	bool sbios_heartbeat;
+	bool fan_table_idx;
 	bool static_slider_granular;
 };
 
@@ -73,6 +78,12 @@ struct apmf_system_params {
 	u32 heartbeat_int;
 } __packed;
 
+struct apmf_fan_idx {
+	u16 size;
+	u8 fan_ctl_mode;
+	u32 fan_ctl_idx;
+} __packed;
+
 enum amd_stt_skin_temp {
 	STT_TEMP_APU,
 	STT_TEMP_HS2,
@@ -137,6 +148,11 @@ struct amd_pmf_static_slider_granular {
 	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
 };
 
+struct fan_table_control {
+	bool manual;
+	unsigned long fan_id;
+};
+
 /* Core Layer */
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
 void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
@@ -154,4 +170,5 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
 				    struct apmf_static_slider_granular_output *output);
 void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
 
+int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
 #endif /* PMF_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (6 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 20:36   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF Shyam Sundar S K
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

PMF driver polls for metrics information from PMFW to understand the system
behavior, power consumption etc.

This metrics table information will be used the PMF features to tweak the
thermal heuristics. The poll duration can also be changed by the user
by changing the poll duration time.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/core.c | 57 +++++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h  | 39 ++++++++++++++++++++
 2 files changed, 96 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index ff26928e6a49..c6fd52c46818 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -47,6 +47,11 @@
 #define DELAY_MIN_US	2000
 #define DELAY_MAX_US	3000
 
+/* override Metrics Table sample size time (in ms) */
+static int metrics_table_loop_ms = 1000;
+module_param(metrics_table_loop_ms, int, 0644);
+MODULE_PARM_DESC(metrics_table_loop_ms, " Metrics Table sample size time (default = 1000ms) ");
+
 #ifdef CONFIG_DEBUG_FS
 static int current_power_limits_show(struct seq_file *seq, void *unused)
 {
@@ -98,6 +103,30 @@ int amd_pmf_get_power_source(void)
 		return POWER_SOURCE_DC;
 }
 
+static void amd_pmf_get_metrics(struct work_struct *work)
+{
+	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
+	enum platform_profile_option current_profile;
+	ktime_t time_elapsed_ms;
+	int socket_power;
+
+	/* Get the current profile information */
+	platform_profile_get(&current_profile);
+
+	/* Transfer table contents */
+	memset(&dev->m_table, 0, sizeof(dev->m_table));
+	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
+	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
+
+	time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
+	/* Calculate the avg SoC power consumption */
+	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
+
+	dev->start_time = ktime_to_ms(ktime_get());
+	dev_dbg(dev->dev, "Metrics table sample size time:%d\n", metrics_table_loop_ms);
+	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
+}
+
 static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
 {
 	return ioread32(dev->regbase + reg_offset);
@@ -191,6 +220,34 @@ static const struct pci_device_id pmf_pci_ids[] = {
 	{ }
 };
 
+int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
+{
+	struct smu_pmf_metrics *m_table;
+	u64 phys_addr;
+
+	INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
+
+	/*
+	 * Start collecting the metrics data after a small delay
+	 * or else, we might end up getting stale values from PMFW.
+	 */
+	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
+
+	/* Get Metrics Table Address */
+	dev->buf = kzalloc(sizeof(*m_table), GFP_KERNEL);
+	if (!dev->buf)
+		return -ENOMEM;
+
+	phys_addr = virt_to_phys(dev->buf);
+	dev->hi = phys_addr >> 32;
+	dev->low = phys_addr & GENMASK(31, 0);
+
+	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, dev->hi, NULL);
+	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, dev->low, NULL);
+
+	return 0;
+}
+
 static void amd_pmf_init_features(struct amd_pmf_dev *dev)
 {
 	/* Enable Static Slider */
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 1e9e2e498d15..8f318ff59c2e 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -84,6 +84,41 @@ struct apmf_fan_idx {
 	u32 fan_ctl_idx;
 } __packed;
 
+struct smu_pmf_metrics {
+	u16 gfxclk_freq; /* in MHz */
+	u16 socclk_freq; /* in MHz */
+	u16 vclk_freq; /* in MHz */
+	u16 dclk_freq; /* in MHz */
+	u16 memclk_freq; /* in MHz */
+	u16 spare;
+	u16 gfx_activity; /* in Centi */
+	u16 uvd_activity; /* in Centi */
+	u16 voltage[2]; /* in mV */
+	u16 currents[2]; /* in mA */
+	u16 power[2];/* in mW */
+	u16 core_freq[8]; /* in MHz */
+	u16 core_power[8]; /* in mW */
+	u16 core_temp[8]; /* in centi-Celsius */
+	u16 l3_freq; /* in MHz */
+	u16 l3_temp; /* in centi-Celsius */
+	u16 gfx_temp; /* in centi-Celsius */
+	u16 soc_temp; /* in centi-Celsius */
+	u16 throttler_status;
+	u16 current_socketpower; /* in mW */
+	u16 stapm_orig_limit; /* in W */
+	u16 stapm_cur_limit; /* in W */
+	u32 apu_power; /* in mW */
+	u32 dgpu_power; /* in mW */
+	u16 vdd_tdc_val; /* in mA */
+	u16 soc_tdc_val; /* in mA */
+	u16 vdd_edc_val; /* in mA */
+	u16 soc_edcv_al; /* in mA */
+	u16 infra_cpu_maxfreq; /* in MHz */
+	u16 infra_gfx_maxfreq; /* in MHz */
+	u16 skin_temp; /* in centi-Celsius */
+	u16 device_state;
+};
+
 enum amd_stt_skin_temp {
 	STT_TEMP_APU,
 	STT_TEMP_HS2,
@@ -120,8 +155,11 @@ struct amd_pmf_dev {
 	struct apmf_if *apmf_if;
 	enum platform_profile_option current_profile;
 	struct platform_profile_handler pprof;
+	struct smu_pmf_metrics m_table;
+	struct delayed_work work_buffer;
 	struct delayed_work heart_beat;
 	struct mutex lock; /* protects the PMF interface */
+	ktime_t start_time;
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	struct dentry *dbgfs_dir;
 #endif /* CONFIG_DEBUG_FS */
@@ -158,6 +196,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
 void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
 int is_apmf_func_supported(unsigned long index);
 int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
+int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev);
 int amd_pmf_get_power_source(void);
 
 /* SPS Layer */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (7 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 20:51   ` Hans de Goede
  2022-07-27 21:00   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF Shyam Sundar S K
                   ` (5 subsequent siblings)
  14 siblings, 2 replies; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

CnQF (a.k.a Cool and Quiet Framework) extends the static slider concept.
PMF dynamically manages system power limits and fan policy based on system
power trends which is representative of workload trend.

Static slider and CnQF controls are mutually exclusive for system power
budget adjustments. CnQF supports configurable number of modes which can
be unique for AC and DC. Every mode is representative of a system state
characterized by unique steady state and boost behavior.

OEMs can configure the different modes/system states and how the
transition to a mode happens. Whether to have CnQF manage system power
budget dynamically in AC or DC or both is also configurable. Mode changes
due to CnQF don’t result in slider position change.

The default OEM values are obtained after evaluating the PMF ACPI function
idx 11 & 12 for AC and DC respectively. Whether to turn ON/OFF by default
is guided by a "flag" passed by the OEM BIOS.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/Makefile |   3 +-
 drivers/platform/x86/amd/pmf/acpi.c   |  56 +++++
 drivers/platform/x86/amd/pmf/cnqf.c   | 327 ++++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/core.c   |  14 ++
 drivers/platform/x86/amd/pmf/pmf.h    | 115 +++++++++
 5 files changed, 514 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/x86/amd/pmf/cnqf.c

diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 557521a80427..d02a0bdc6429 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -5,4 +5,5 @@
 #
 
 obj-$(CONFIG_AMD_PMF) += amd-pmf.o
-amd-pmf-objs := core.o acpi.o sps.o
+amd-pmf-objs := core.o acpi.o sps.o \
+		cnqf.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index e4822c6f4259..a3ff91c605b5 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -56,6 +56,8 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
 	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
 	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
 	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
+	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
+	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
 }
 
 static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
@@ -208,6 +210,60 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
 	return err;
 }
 
+int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
+{
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_AC, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(*data)) {
+		pr_debug("buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(*data), size);
+	memset(data, 0, sizeof(*data));
+	memcpy(data, info->buffer.pointer, size);
+
+out:
+	kfree(info);
+	return err;
+}
+
+int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
+{
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_DC, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(*data)) {
+		pr_debug("buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(*data), size);
+	memset(data, 0, sizeof(*data));
+	memcpy(data, info->buffer.pointer, size);
+
+out:
+	kfree(info);
+	return err;
+}
+
 static acpi_handle apmf_if_probe_handle(void)
 {
 	acpi_handle handle = NULL;
diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
new file mode 100644
index 000000000000..8c6756faab25
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/cnqf.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Platform Management Framework Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+
+#include <linux/workqueue.h>
+#include "pmf.h"
+
+static struct cnqf_config config_store;
+
+static int amd_pmf_handle_cnqf(struct amd_pmf_dev *dev, bool op, int src, int idx,
+			       struct cnqf_config *table)
+{
+	struct power_table_control *pc;
+
+	if (op == SLIDER_OP_SET) {
+		pc = &config_store.mode_set[src][idx].power_control;
+
+		amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL);
+		amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
+				 NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
+				 NULL);
+	} else if (op == SLIDER_OP_GET) {
+		pc = &table->mode_set[src][idx].power_control;
+
+		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &pc->spl);
+		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &pc->fppt);
+		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &pc->sppt);
+		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE, &pc->sppt_apu_only);
+		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE, &pc->stt_min);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
+				 &pc->stt_skin_temp[STT_TEMP_APU]);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
+				 &pc->stt_skin_temp[STT_TEMP_HS2]);
+	}
+
+	if (dev->apmf_if->func.fan_table_idx)
+		apmf_update_fan_idx(dev->apmf_if,
+				    config_store.mode_set[src][idx].fan_control.manual,
+				    config_store.mode_set[src][idx].fan_control.fan_id);
+
+	return 0;
+}
+
+static void amd_pmf_update_power_threshold(void)
+{
+	struct cnqf_mode_settings *ts;
+	struct cnqf_tran_params *tp;
+	int i;
+
+	for (i = 0; i < POWER_SOURCE_MAX; i++) {
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_QUIET];
+		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_TURBO];
+		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
+		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
+		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
+		ts = &config_store.mode_set[i][CNQF_MODE_QUIET];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+
+		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
+		ts = &config_store.mode_set[i][CNQF_MODE_TURBO];
+		tp->power_threshold = ts->power_floor - tp->power_delta;
+	}
+}
+
+static const char *state_as_str(unsigned int state)
+{
+	switch (state) {
+	case CNQF_MODE_QUIET:
+		return "QUIET";
+	case CNQF_MODE_BALANCE:
+		return "BALANCED";
+	case CNQF_MODE_TURBO:
+		return "TURBO";
+	case CNQF_MODE_PERFORMANCE:
+		return "PERFORMANCE";
+	default:
+		return "Unknown CnQF mode";
+	}
+}
+
+void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
+{
+	struct cnqf_tran_params *tp;
+	int src, i, j, index = 0;
+	u32 avg_power = 0;
+
+	src = amd_pmf_get_power_source();
+
+	if (!dev->cnqf_running) {
+		pr_debug("setting CNQF thermals\n");
+		amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
+		dev->cnqf_running = true;
+		return;
+	}
+
+	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
+		config_store.trans_param[src][i].timer += time_lapsed_ms;
+		config_store.trans_param[src][i].total_power += socket_power;
+		config_store.trans_param[src][i].count++;
+
+		tp = &config_store.trans_param[src][i];
+		if (tp->timer >= tp->time_constant && tp->count) {
+			avg_power = tp->total_power / tp->count;
+
+			/* Reset the indices */
+			tp->timer = 0;
+			tp->total_power = 0;
+			tp->count = 0;
+
+			if ((tp->shifting_up && avg_power >= tp->power_threshold) ||
+			    (!tp->shifting_up && avg_power <= tp->power_threshold)) {
+				tp->priority = true;
+			} else {
+				tp->priority = false;
+			}
+		}
+	}
+
+	dev_dbg(dev->dev, "[CNQF] Avg power: %u mW socket power: %u mW mode:%s\n",
+		avg_power, socket_power, state_as_str(config_store.current_mode));
+
+	for (j = 0; j < CNQF_TRANSITION_MAX; j++) {
+		/* apply the highest priority */
+		index = config_store.trans_prio[j];
+		if (config_store.trans_param[src][index].priority) {
+			if (config_store.current_mode !=
+			    config_store.trans_param[src][index].target_mode) {
+				config_store.current_mode =
+						config_store.trans_param[src][index].target_mode;
+				dev_dbg(dev->dev, "Moving to Mode :%s\n",
+					state_as_str(config_store.current_mode));
+				amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src,
+						    config_store.current_mode, NULL);
+			}
+			break;
+		}
+	}
+}
+
+static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output out)
+{
+	struct cnqf_tran_params *tp;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_QUIET];
+	tp->time_constant = out.t_balanced_to_quiet;
+	tp->target_mode = CNQF_MODE_QUIET;
+	tp->shifting_up = false;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
+	tp->time_constant = out.t_balanced_to_perf;
+	tp->target_mode = CNQF_MODE_PERFORMANCE;
+	tp->shifting_up = true;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
+	tp->time_constant = out.t_quiet_to_balanced;
+	tp->target_mode = CNQF_MODE_BALANCE;
+	tp->shifting_up = true;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
+	tp->time_constant = out.t_perf_to_balanced;
+	tp->target_mode = CNQF_MODE_BALANCE;
+	tp->shifting_up = false;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
+	tp->time_constant = out.t_turbo_to_perf;
+	tp->target_mode = CNQF_MODE_PERFORMANCE;
+	tp->shifting_up = false;
+
+	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_TURBO];
+	tp->time_constant = out.t_perf_to_turbo;
+	tp->target_mode = CNQF_MODE_TURBO;
+	tp->shifting_up = true;
+}
+
+static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output out)
+{
+	struct cnqf_mode_settings *ms;
+
+	/* Quiet Mode */
+	ms = &config_store.mode_set[idx][CNQF_MODE_QUIET];
+	ms->power_floor = out.ps[APMF_CNQF_QUIET].pfloor;
+	ms->power_control.fppt = out.ps[APMF_CNQF_QUIET].fppt;
+	ms->power_control.sppt = out.ps[APMF_CNQF_QUIET].sppt;
+	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_QUIET].sppt_apu_only;
+	ms->power_control.spl = out.ps[APMF_CNQF_QUIET].spl;
+	ms->power_control.stt_min = out.ps[APMF_CNQF_QUIET].stt_min_limit;
+	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
+		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU];
+	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
+		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2];
+	ms->fan_control.fan_id = out.ps[APMF_CNQF_QUIET].fan_id;
+
+	/* Balance Mode */
+	ms = &config_store.mode_set[idx][CNQF_MODE_BALANCE];
+	ms->power_floor = out.ps[APMF_CNQF_BALANCE].pfloor;
+	ms->power_control.fppt = out.ps[APMF_CNQF_BALANCE].fppt;
+	ms->power_control.sppt = out.ps[APMF_CNQF_BALANCE].sppt;
+	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_BALANCE].sppt_apu_only;
+	ms->power_control.spl = out.ps[APMF_CNQF_BALANCE].spl;
+	ms->power_control.stt_min = out.ps[APMF_CNQF_BALANCE].stt_min_limit;
+	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
+		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU];
+	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
+		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2];
+	ms->fan_control.fan_id = out.ps[APMF_CNQF_BALANCE].fan_id;
+
+	/* Performance Mode */
+	ms = &config_store.mode_set[idx][CNQF_MODE_PERFORMANCE];
+	ms->power_floor = out.ps[APMF_CNQF_PERFORMANCE].pfloor;
+	ms->power_control.fppt = out.ps[APMF_CNQF_PERFORMANCE].fppt;
+	ms->power_control.sppt = out.ps[APMF_CNQF_PERFORMANCE].sppt;
+	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_PERFORMANCE].sppt_apu_only;
+	ms->power_control.spl = out.ps[APMF_CNQF_PERFORMANCE].spl;
+	ms->power_control.stt_min = out.ps[APMF_CNQF_PERFORMANCE].stt_min_limit;
+	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
+		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU];
+	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
+		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2];
+	ms->fan_control.fan_id = out.ps[APMF_CNQF_PERFORMANCE].fan_id;
+
+	/* Turbo Mode */
+	ms = &config_store.mode_set[idx][CNQF_MODE_TURBO];
+	ms->power_floor = out.ps[APMF_CNQF_TURBO].pfloor;
+	ms->power_control.fppt = out.ps[APMF_CNQF_TURBO].fppt;
+	ms->power_control.sppt = out.ps[APMF_CNQF_TURBO].sppt;
+	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_TURBO].sppt_apu_only;
+	ms->power_control.spl = out.ps[APMF_CNQF_TURBO].spl;
+	ms->power_control.stt_min = out.ps[APMF_CNQF_TURBO].stt_min_limit;
+	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
+		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU];
+	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
+		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2];
+	ms->fan_control.fan_id = out.ps[APMF_CNQF_TURBO].fan_id;
+}
+
+static int amd_pmf_check_flags(struct amd_pmf_dev *dev)
+{
+	struct apmf_dyn_slider_output out;
+
+	if (dev->apmf_if->func.dyn_slider_ac)
+		apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
+	else if (dev->apmf_if->func.dyn_slider_dc)
+		apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
+
+	return out.flags;
+}
+
+void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
+{
+	struct apmf_dyn_slider_output out;
+	int i, j, ret;
+
+	for (i = 0; i < POWER_SOURCE_MAX; i++) {
+		if (i == POWER_SOURCE_AC && dev->apmf_if->func.dyn_slider_ac) {
+			ret = apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
+			if (ret)
+				dev_err(dev->dev,
+					"APMF apmf_get_dyn_slider_def_ac failed :%d\n", ret);
+		} else if (i == POWER_SOURCE_DC && dev->apmf_if->func.dyn_slider_dc) {
+			ret = apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
+			if (ret)
+				dev_err(dev->dev,
+					"APMF apmf_get_dyn_slider_def_dc failed :%d\n", ret);
+		}
+
+		amd_pmf_update_mode_set(i, out);
+		amd_pmf_update_trans_data(i, out);
+
+		for (j = 0; j < CNQF_MODE_MAX; j++) {
+			if (config_store.mode_set[i][j].fan_control.fan_id == FAN_INDEX_AUTO)
+				config_store.mode_set[i][j].fan_control.manual = false;
+			else
+				config_store.mode_set[i][j].fan_control.manual = true;
+		}
+	}
+	amd_pmf_update_power_threshold();
+
+	for (i = 0; i < CNQF_TRANSITION_MAX; i++)
+		config_store.trans_prio[i] = i;
+}
+
+void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
+{
+	cancel_delayed_work_sync(&dev->work_buffer);
+}
+
+void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
+{
+	int ret, src;
+
+	ret = amd_pmf_check_flags(dev);
+	if (!ret) {
+		dev_dbg(dev->dev, "CnQF bios default_enable flag not set\n");
+		return;
+	}
+
+	dev->cnqf_feat = true;
+	amd_pmf_load_defaults_cnqf(dev);
+	amd_pmf_init_metrics_table(dev);
+
+	/* update the thermal for CnQF */
+	src = amd_pmf_get_power_source();
+	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
+}
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index c6fd52c46818..bc267d333b76 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
 	/* Calculate the avg SoC power consumption */
 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
 
+	if (dev->cnqf_feat) {
+		/* Apply the CnQF transition */
+		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
+	}
+
 	dev->start_time = ktime_to_ms(ktime_get());
 	dev_dbg(dev->dev, "Metrics table sample size time:%d\n", metrics_table_loop_ms);
 	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
@@ -255,12 +260,21 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
 		amd_pmf_init_sps(dev);
 		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
 	}
+	/* Enable Cool n Quiet Framework (CnQF) */
+	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
+	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
+		amd_pmf_init_cnqf(dev);
+		dev_dbg(dev->dev, "CnQF Init done\n");
+	}
 }
 
 static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
 {
 	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
 		amd_pmf_deinit_sps(dev);
+	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
+	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
+		amd_pmf_deinit_cnqf(dev);
 }
 
 static const struct acpi_device_id amd_pmf_acpi_ids[] = {
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 8f318ff59c2e..452266809dfa 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -20,6 +20,8 @@
 #define APMF_FUNC_SBIOS_HEARTBEAT			4
 #define APMF_FUNC_SET_FAN_IDX				7
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
+#define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
+#define APMF_FUNC_DYN_SLIDER_GRANULAR_DC	12
 
 /* Message Definitions */
 #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
@@ -51,6 +53,8 @@ struct apmf_if_functions {
 	bool sbios_heartbeat;
 	bool fan_table_idx;
 	bool static_slider_granular;
+	bool dyn_slider_ac;
+	bool dyn_slider_dc;
 };
 
 struct apmf_notification_cfg {
@@ -163,6 +167,8 @@ struct amd_pmf_dev {
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	struct dentry *dbgfs_dir;
 #endif /* CONFIG_DEBUG_FS */
+	bool cnqf_feat;
+	bool cnqf_running;
 };
 
 struct apmf_sps_prop_granular {
@@ -191,6 +197,106 @@ struct fan_table_control {
 	unsigned long fan_id;
 };
 
+struct power_table_control {
+	u32 spl;
+	u32 sppt;
+	u32 fppt;
+	u32 sppt_apu_only;
+	u32 stt_min;
+	u32 stt_skin_temp[STT_TEMP_COUNT];
+	u32 reserved[16];
+};
+
+/* CnQF Layer */
+enum cnqf_trans_priority {
+	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
+	CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE, /* quiet/balance to Performance Mode */
+	CNQF_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
+	CNQF_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
+	CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance/Turbo to Balance Mode */
+	CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE, /* Turbo mode to Performance Mode */
+	CNQF_TRANSITION_MAX,
+};
+
+enum cnqf_mode {
+	CNQF_MODE_QUIET,
+	CNQF_MODE_BALANCE,
+	CNQF_MODE_PERFORMANCE,
+	CNQF_MODE_TURBO,
+	CNQF_MODE_MAX,
+};
+
+enum apmf_cnqf_pos {
+	APMF_CNQF_TURBO,
+	APMF_CNQF_PERFORMANCE,
+	APMF_CNQF_BALANCE,
+	APMF_CNQF_QUIET,
+	APMF_CNQF_MAX,
+};
+
+struct cnqf_mode_settings {
+	struct power_table_control power_control;
+	struct fan_table_control fan_control;
+	u32 power_floor;
+	bool enable;
+};
+
+struct cnqf_tran_params {
+	u32 time_constant; /* minimum time required to switch to next mode */
+	u32 power_delta; /* minimum power required to switch to next mode */
+	u32 power_threshold;
+	u32 timer; /* elapsed time. if timer > timethreshold, it will move to next mode */
+	u32 total_power;
+	u32 count;
+	bool enable;
+	bool priority;
+	bool shifting_up;
+	enum cnqf_mode target_mode;
+};
+
+struct cnqf_power_delta {
+	u32 to_turbo;
+	u32 balance_to_perf;
+	u32 quiet_to_balance;
+	u32 to_quiet;
+	u32 perf_to_balance;
+	u32 turbo_to_perf;
+};
+
+struct cnqf_config {
+	struct cnqf_tran_params trans_param[POWER_SOURCE_MAX][CNQF_TRANSITION_MAX];
+	struct cnqf_mode_settings mode_set[POWER_SOURCE_MAX][CNQF_MODE_MAX];
+	struct power_table_control defaults;
+	enum cnqf_mode current_mode;
+	struct cnqf_power_delta power_delta[POWER_SOURCE_MAX];
+	u32 power_src;
+	u32 avg_power;
+	enum cnqf_trans_priority trans_prio[CNQF_TRANSITION_MAX];
+};
+
+struct apmf_cnqf_power_set {
+	u32 pfloor;
+	u32 fppt;
+	u32 sppt;
+	u32 sppt_apu_only;
+	u32 spl;
+	u32 stt_min_limit;
+	u8 stt_skintemp[STT_TEMP_COUNT];
+	u32 fan_id;
+} __packed;
+
+struct apmf_dyn_slider_output {
+	u16 size;
+	u16 flags;
+	u32 t_perf_to_turbo;
+	u32 t_balanced_to_perf;
+	u32 t_quiet_to_balanced;
+	u32 t_balanced_to_quiet;
+	u32 t_perf_to_balanced;
+	u32 t_turbo_to_perf;
+	struct apmf_cnqf_power_set ps[APMF_CNQF_MAX];
+};
+
 /* Core Layer */
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
 void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
@@ -210,4 +316,13 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
 void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
 
 int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
+
+/* CnQF Layer */
+int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
+int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
+void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
+void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
+void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
+void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
+
 #endif /* PMF_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (8 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 20:52   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF Shyam Sundar S K
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

Whether to turn CnQF on/off by default upon driver load would be decided
by a BIOS flag. Add a sysfs node to provide a way to the user whether to
use static slider or CnQF .

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/cnqf.c | 52 +++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
index 8c6756faab25..2b03ae1ad37f 100644
--- a/drivers/platform/x86/amd/pmf/cnqf.c
+++ b/drivers/platform/x86/amd/pmf/cnqf.c
@@ -302,9 +302,59 @@ void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
 		config_store.trans_prio[i] = i;
 }
 
+static ssize_t feat_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
+	int result, src;
+	bool enabled;
+	u8 mode;
+
+	result = kstrtobool(buf, &enabled);
+	if (result)
+		return result;
+
+	src = amd_pmf_get_power_source();
+	pdev->cnqf_feat = enabled;
+	if (pdev->cnqf_feat) {
+		amd_pmf_handle_cnqf(pdev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
+	} else {
+		pdev->cnqf_running = false;
+		mode = amd_pmf_get_pprof_modes(pdev);
+		amd_pmf_update_slider(pdev, SLIDER_OP_SET, mode, NULL);
+	}
+
+	dev_dbg(pdev->dev, "Received CnQF %s\n", enabled ? "on" : "off");
+	return count;
+}
+
+static ssize_t feat_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", pdev->cnqf_feat ? "on" : "off");
+}
+
+static DEVICE_ATTR_RW(feat);
+
+static struct attribute *cnqf_feature_attrs[] = {
+	&dev_attr_feat.attr,
+	NULL
+};
+
+static const struct attribute_group cnqf_feature_attribute_group = {
+	.attrs = cnqf_feature_attrs,
+	.name = "cnqf"
+};
+
 void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
 {
 	cancel_delayed_work_sync(&dev->work_buffer);
+	sysfs_remove_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
+	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
 }
 
 void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
@@ -324,4 +374,6 @@ void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
 	/* update the thermal for CnQF */
 	src = amd_pmf_get_power_source();
 	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
+	ret = sysfs_create_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
+	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (9 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 20:52   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature Shyam Sundar S K
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

AMD PMF driver provides the flexibility to turn "on" or "off"
CnQF feature (introduced in the earlier patch).

Add corresponding ABI documentation for the new sysfs node.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 Documentation/ABI/testing/sysfs-amd-pmf | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-amd-pmf

diff --git a/Documentation/ABI/testing/sysfs-amd-pmf b/Documentation/ABI/testing/sysfs-amd-pmf
new file mode 100644
index 000000000000..5935dc549185
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-amd-pmf
@@ -0,0 +1,11 @@
+What:		/sys/devices/platform/AMDI0102\:00/cnqf/feat
+Date:		July 2022
+Contact:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+Description:	Reading this file tells if the AMD Platform Management(PMF)
+		Cool n Quiet Framework(CnQF) feature is enabled or not.
+
+		This feature is not enabled by default and gets only turned on
+		if OEM BIOS passes a "flag" to PMF ACPI function (index 11 or 12)
+		or in case the user writes "on".
+
+		To turn off CnQF user can write "off" to the sysfs node.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (10 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 21:22   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

The objective of this feature is to track the moving average of system
power over the time period specified and switch to the subsequent mode.

This feature has 3 modes: quiet, balanced, performance.

In order to do this, PMF driver will get the moving average of APU power
from PMFW and power threshold, time constants, system config parameters
from OEM inputs.

System power as read by PMF driver from PMFW is the filtered value over
the sampling window. Every sampling window, moving average of system power
is computed. At the end of the monitoring window, the moving average is
compared against the threshold for mode switch for decision making.

With AMD managing the system config limits, any mode switch within
auto-mode will result in limits of fPPT/sPPT/STAPM or STT being scaled
down.

When "auto mode" is enabled, the static slider control remains out of
the PMF driver, so the platform_profile registration would not
happen in PMF driver.

The transition to auto-mode only happens when the APMF fn5 is enabled
in BIOS, platform_profile set to "balanced" and a AMT
(Auto Mode transition) is received.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/Makefile    |   2 +-
 drivers/platform/x86/amd/pmf/acpi.c      |  28 ++
 drivers/platform/x86/amd/pmf/auto-mode.c | 317 +++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/core.c      |  25 +-
 drivers/platform/x86/amd/pmf/pmf.h       | 104 ++++++++
 5 files changed, 473 insertions(+), 3 deletions(-)
 create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c

diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index d02a0bdc6429..2a9568bf9064 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -6,4 +6,4 @@
 
 obj-$(CONFIG_AMD_PMF) += amd-pmf.o
 amd-pmf-objs := core.o acpi.o sps.o \
-		cnqf.o
+		cnqf.o auto-mode.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index a3ff91c605b5..e9f33e61659f 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
 {
 	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
 	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
+	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
 	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
 	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
 	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
@@ -210,6 +211,33 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
 	return err;
 }
 
+int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data)
+{
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_AUTO_MODE, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(*data)) {
+		pr_debug("buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(*data), size);
+	memset(data, 0, sizeof(*data));
+	memcpy(data, info->buffer.pointer, size);
+
+out:
+	kfree(info);
+	return err;
+}
+
 int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
 {
 	union acpi_object *info;
diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
new file mode 100644
index 000000000000..954fde25e71e
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/auto-mode.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Platform Management Framework Driver
+ *
+ * Copyright (c) 2022, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include "pmf.h"
+
+#define AVG_SAMPLE_SIZE 3
+
+struct power_history {
+	int socket_power;
+	struct list_head list;
+	int avg;
+	int total;
+};
+
+struct list_pdata {
+	int total;
+	int avg;
+};
+
+static struct power_history power_list;
+static struct list_pdata pdata;
+
+static struct auto_mode_mode_config config_store;
+static const char *state_as_str(unsigned int state);
+
+static void amd_pmf_handle_automode(struct amd_pmf_dev *dev, bool op, int idx,
+				    struct auto_mode_mode_config *table)
+{
+	if (op == SLIDER_OP_SET) {
+		struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control;
+
+		amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL);
+		amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+				 pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+				 pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
+	} else if (op == SLIDER_OP_GET) {
+		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.spl);
+		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.fppt);
+		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.sppt);
+		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.sppt_apu_only);
+		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.stt_min);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_APU]);
+		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
+				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_HS2]);
+	}
+
+	if (dev->apmf_if->func.fan_table_idx)
+		apmf_update_fan_idx(dev->apmf_if, config_store.mode_set[idx].fan_control.manual,
+				    config_store.mode_set[idx].fan_control.fan_id);
+}
+
+static int amd_pmf_get_moving_avg(int socket_power)
+{
+	struct power_history *tmp;
+	struct list_head *pos, *q;
+	static int count;
+
+	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+	tmp->socket_power = socket_power;
+	list_add_tail(&tmp->list, &power_list.list);
+
+	list_for_each_safe(pos, q, &power_list.list) {
+		if (count >= AVG_SAMPLE_SIZE) {
+			tmp = list_first_entry(pos, struct power_history, list);
+			list_del_init(pos);
+			goto next;
+		}
+	}
+
+next:
+	pdata.total = 0;
+	pdata.avg = 0;
+
+	list_for_each(pos, &power_list.list) {
+		tmp = list_entry(pos, struct power_history, list);
+		pdata.total += tmp->socket_power;
+		pdata.avg = pdata.total / AVG_SAMPLE_SIZE;
+	}
+
+	count++;
+	if (count >= AVG_SAMPLE_SIZE)
+		return pdata.avg;
+
+	return 0;
+}
+
+void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
+{
+	int avg_power = 0;
+	bool update = false;
+	int i, j;
+
+	/* Get the average moving average computed by auto mode algorithm */
+	avg_power = amd_pmf_get_moving_avg(socket_power);
+
+	for (i = 0; i < AUTO_TRANSITION_MAX; i++) {
+		if ((config_store.transition[i].shifting_up && avg_power >=
+		     config_store.transition[i].power_threshold) ||
+		    (!config_store.transition[i].shifting_up && avg_power <=
+		     config_store.transition[i].power_threshold)) {
+			if (config_store.transition[i].timer <
+			    config_store.transition[i].time_constant)
+				config_store.transition[i].timer += time_elapsed_ms;
+		} else {
+			config_store.transition[i].timer = 0;
+		}
+
+		if (config_store.transition[i].timer >=
+		    config_store.transition[i].time_constant &&
+		    !config_store.transition[i].applied) {
+			config_store.transition[i].applied = true;
+			update = true;
+		} else if (config_store.transition[i].timer <=
+			   config_store.transition[i].time_constant &&
+			   config_store.transition[i].applied) {
+			config_store.transition[i].applied = false;
+			update = true;
+		}
+	}
+
+	dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power,
+		state_as_str(config_store.current_mode));
+
+	if (update) {
+		for (j = 0; j < AUTO_TRANSITION_MAX; j++) {
+			/* Apply the mode with highest priority indentified */
+			if (config_store.transition[j].applied) {
+				if (config_store.current_mode !=
+				    config_store.transition[j].target_mode) {
+					config_store.current_mode =
+							config_store.transition[j].target_mode;
+					dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n",
+						state_as_str(config_store.current_mode));
+					amd_pmf_handle_automode(dev, SLIDER_OP_SET,
+								config_store.current_mode, NULL);
+				}
+				break;
+			}
+		}
+	}
+}
+
+static void amd_pmf_get_power_threshold(void)
+{
+	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
+				config_store.mode_set[AUTO_BALANCE].power_floor -
+				config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta;
+
+	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold =
+				config_store.mode_set[AUTO_BALANCE].power_floor -
+				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta;
+
+	config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold =
+			config_store.mode_set[AUTO_QUIET].power_floor -
+			config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta;
+
+	config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold =
+		config_store.mode_set[AUTO_PERFORMANCE].power_floor -
+		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta;
+}
+
+static const char *state_as_str(unsigned int state)
+{
+	switch (state) {
+	case AUTO_QUIET:
+		return "QUIET";
+	case AUTO_BALANCE:
+		return "BALANCED";
+	case AUTO_PERFORMANCE_ON_LAP:
+		return "ON_LAP";
+	case AUTO_PERFORMANCE:
+		return "PERFORMANCE";
+	default:
+		return "Unknown Auto Mode State";
+	}
+}
+
+void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev)
+{
+	struct apmf_auto_mode output;
+	struct power_table_control *pwr_ctrl;
+	int i;
+
+	if (dev->apmf_if->func.auto_mode_def) {
+		apmf_get_auto_mode_def(dev->apmf_if, &output);
+		/* time constant */
+		config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant =
+									output.balanced_to_quiet;
+		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant =
+									output.balanced_to_perf;
+		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant =
+									output.quiet_to_balanced;
+		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant =
+									output.perf_to_balanced;
+
+		/* power floor */
+		config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet;
+		config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced;
+		config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf;
+		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf;
+
+		/* Power delta for mode change */
+		config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta =
+									output.pd_balanced_to_quiet;
+		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta =
+									output.pd_balanced_to_perf;
+		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta =
+									output.pd_quiet_to_balanced;
+		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta =
+									output.pd_perf_to_balanced;
+
+		/* Power threshold */
+		amd_pmf_get_power_threshold();
+
+		/* skin temperature limits */
+		pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control;
+		pwr_ctrl->spl = output.spl_quiet;
+		pwr_ctrl->sppt = output.sppt_quiet;
+		pwr_ctrl->fppt = output.fppt_quiet;
+		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet;
+		pwr_ctrl->stt_min = output.stt_min_limit_quiet;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet;
+
+		pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control;
+		pwr_ctrl->spl = output.spl_balanced;
+		pwr_ctrl->sppt = output.sppt_balanced;
+		pwr_ctrl->fppt = output.fppt_balanced;
+		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced;
+		pwr_ctrl->stt_min = output.stt_min_limit_balanced;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced;
+
+		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control;
+		pwr_ctrl->spl = output.spl_perf;
+		pwr_ctrl->sppt = output.sppt_perf;
+		pwr_ctrl->fppt = output.fppt_perf;
+		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf;
+		pwr_ctrl->stt_min = output.stt_min_limit_perf;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf;
+
+		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control;
+		pwr_ctrl->spl = output.spl_perf_on_lap;
+		pwr_ctrl->sppt = output.sppt_perf_on_lap;
+		pwr_ctrl->fppt = output.fppt_perf_on_lap;
+		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap;
+		pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap;
+		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap;
+
+		/* Fan ID */
+		config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet;
+		config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced;
+		config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf;
+		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id =
+										output.fan_id_perf;
+
+		config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET;
+		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
+									AUTO_PERFORMANCE;
+		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode =
+										AUTO_BALANCE;
+		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode =
+										AUTO_BALANCE;
+
+		config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false;
+		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true;
+		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true;
+		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up =
+											false;
+
+		for (i = 0 ; i < AUTO_MODE_MAX ; i++) {
+			if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO)
+				config_store.mode_set[i].fan_control.manual = false;
+			else
+				config_store.mode_set[i].fan_control.manual = true;
+		}
+	}
+}
+
+void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev)
+{
+	cancel_delayed_work_sync(&dev->work_buffer);
+}
+
+void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev)
+{
+	INIT_LIST_HEAD(&power_list.list);
+
+	amd_pmf_init_metrics_table(dev);
+	amd_pmf_load_defaults_auto_mode(dev);
+
+	/* update the thermal for Automode */
+	amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
+}
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index bc267d333b76..674ddf599135 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
 	/* Calculate the avg SoC power consumption */
 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
 
+	if (current_profile == PLATFORM_PROFILE_BALANCED) {
+		/* Apply the Auto Mode transition */
+		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+	}
+
 	if (dev->cnqf_feat) {
 		/* Apply the CnQF transition */
 		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
@@ -260,9 +265,18 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
 		amd_pmf_init_sps(dev);
 		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
 	}
-	/* Enable Cool n Quiet Framework (CnQF) */
-	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
+
+	/* Enable Auto Mode */
+	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
+		amd_pmf_init_auto_mode(dev);
+		dev_dbg(dev->dev, "Auto Mode Init done\n");
+		/*
+		 * Auto mode and CnQF cannot co-exist. If auto mode is supported it takes
+		 * higher priority over CnQF.
+		 */
+	} else if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
 	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
+		/* Enable Cool n Quiet Framework (CnQF) */
 		amd_pmf_init_cnqf(dev);
 		dev_dbg(dev->dev, "CnQF Init done\n");
 	}
@@ -272,6 +286,13 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
 {
 	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
 		amd_pmf_deinit_sps(dev);
+
+	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
+		amd_pmf_deinit_auto_mode(dev);
+		/* If auto mode is supported, there is no need to proceed */
+		return;
+	}
+
 	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
 	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
 		amd_pmf_deinit_cnqf(dev);
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 452266809dfa..0532f49e9613 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -18,6 +18,7 @@
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
 #define APMF_FUNC_SBIOS_HEARTBEAT			4
+#define APMF_FUNC_AUTO_MODE					5
 #define APMF_FUNC_SET_FAN_IDX				7
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
 #define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
@@ -51,6 +52,7 @@
 struct apmf_if_functions {
 	bool system_params;
 	bool sbios_heartbeat;
+	bool auto_mode_def;
 	bool fan_table_idx;
 	bool static_slider_granular;
 	bool dyn_slider_ac;
@@ -197,6 +199,33 @@ struct fan_table_control {
 	unsigned long fan_id;
 };
 
+/* Auto Mode Layer */
+enum auto_mode_transition_priority {
+	AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */
+	AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
+	AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
+	AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */
+	AUTO_TRANSITION_MAX,
+};
+
+enum auto_mode_mode {
+	AUTO_QUIET,
+	AUTO_BALANCE,
+	AUTO_PERFORMANCE_ON_LAP,
+	AUTO_PERFORMANCE,
+	AUTO_MODE_MAX,
+};
+
+struct auto_mode_trans_params {
+	u32	time_constant; /* minimum time required to switch to next mode */
+	u32 power_delta; /* delta power to shift mode */
+	u32 power_threshold;
+	u32	timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */
+	u32 applied;
+	enum auto_mode_mode target_mode;
+	u32 shifting_up;
+};
+
 struct power_table_control {
 	u32 spl;
 	u32 sppt;
@@ -207,6 +236,74 @@ struct power_table_control {
 	u32 reserved[16];
 };
 
+struct auto_mode_mode_settings {
+	struct power_table_control power_control;
+	struct fan_table_control fan_control;
+	u32 power_floor;
+};
+
+struct auto_mode_mode_config {
+	struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX];
+	struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX];
+	enum auto_mode_mode current_mode;
+	bool on_lap;
+	bool better_perf;
+	u32 amt_enabled; /* Auto Mode Transition */
+	u32 avg_power;
+};
+
+struct apmf_auto_mode {
+	u16 size;
+	/* time constant */
+	u32 balanced_to_perf;
+	u32 perf_to_balanced;
+	u32 quiet_to_balanced;
+	u32 balanced_to_quiet;
+	/* power floor */
+	u32 pfloor_perf;
+	u32 pfloor_balanced;
+	u32 pfloor_quiet;
+	/* Power delta for mode change */
+	u32 pd_balanced_to_perf;
+	u32 pd_perf_to_balanced;
+	u32 pd_quiet_to_balanced;
+	u32 pd_balanced_to_quiet;
+	/* skin temperature limits */
+	u8 stt_apu_perf_on_lap; /* CQL ON */
+	u8 stt_hs2_perf_on_lap; /* CQL ON */
+	u8 stt_apu_perf;
+	u8 stt_hs2_perf;
+	u8 stt_apu_balanced;
+	u8 stt_hs2_balanced;
+	u8 stt_apu_quiet;
+	u8 stt_hs2_quiet;
+	u32 stt_min_limit_perf_on_lap; /* CQL ON */
+	u32 stt_min_limit_perf;
+	u32 stt_min_limit_balanced;
+	u32 stt_min_limit_quiet;
+	/* SPL based */
+	u32 fppt_perf_on_lap; /* CQL ON */
+	u32 sppt_perf_on_lap; /* CQL ON */
+	u32 spl_perf_on_lap; /* CQL ON */
+	u32 sppt_apu_only_perf_on_lap; /* CQL ON */
+	u32 fppt_perf;
+	u32 sppt_perf;
+	u32 spl_perf;
+	u32 sppt_apu_only_perf;
+	u32 fppt_balanced;
+	u32 sppt_balanced;
+	u32 spl_balanced;
+	u32 sppt_apu_only_balanced;
+	u32 fppt_quiet;
+	u32 sppt_quiet;
+	u32 spl_quiet;
+	u32 sppt_apu_only_quiet;
+	/* Fan ID */
+	u32 fan_id_perf;
+	u32 fan_id_balanced;
+	u32 fan_id_quiet;
+} __packed;
+
 /* CnQF Layer */
 enum cnqf_trans_priority {
 	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
@@ -317,6 +414,13 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
 
 int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
 
+/* Auto Mode Layer */
+void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
+int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
+void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
+void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
+void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
+
 /* CnQF Layer */
 int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
 int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (11 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 21:33   ` Hans de Goede
                     ` (2 more replies)
  2022-07-12 14:58 ` [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms Shyam Sundar S K
  2022-07-12 14:58 ` [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry Shyam Sundar S K
  14 siblings, 3 replies; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

The transition to auto-mode happens when the PMF driver receives
AMT (Auto Mode transition) event. transition logic will reside in the
PMF driver but the events would come from other supported drivers[1].

The thermal parameters would vary between when a performance "on-lap" mode
is detected and versus when not. The CQL event would get triggered from
other drivers, so that PMF driver would adjust the system thermal config
based on the ACPI inputs.

OEMs can control whether or not to enable AMT or CQL via other supported
drivers[1] but the actual transition logic resides in the AMD PMF driver.
When an AMT event is received the automatic mode transition RAPL algorithm
will run. When a CQL event is received an performance "on-lap" mode will
be enabled and thermal parameters will be adjusted accordingly.

[1]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-hans&id=755b249250df1b612d982f3b702c831b26ecdf73

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
 drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
 drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
 drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
 drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
 5 files changed, 156 insertions(+), 7 deletions(-)

diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index e9f33e61659f..4bde86aeafa0 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -12,6 +12,8 @@
 #include "pmf.h"
 
 #define APMF_METHOD "\\_SB_.PMF_.APMF"
+#define APMF_CQL_NOTIFICATION	2
+#define APMF_AMT_NOTIFICATION	3
 
 static unsigned long supported_func;
 
@@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
 {
 	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
 	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
+	func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
 	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
 	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
 	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
@@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
 	return err;
 }
 
+int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
+{
+	union acpi_object *info;
+	size_t size;
+	int err = 0;
+
+	info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
+	if (!info)
+		return -EIO;
+
+	size = *(u16 *)info->buffer.pointer;
+
+	if (size < sizeof(union acpi_object)) {
+		pr_err("PMF: buffer too small %zu\n", size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	size = min(sizeof(*req), size);
+	memset(req, 0, sizeof(*req));
+	memcpy(req, info->buffer.pointer, size);
+
+	pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
+		 req->pending_req, req->cql_event, req->amt_event);
+
+out:
+	kfree(info);
+	return err;
+}
+
 static acpi_handle apmf_if_probe_handle(void)
 {
 	acpi_handle handle = NULL;
@@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
 	return handle;
 }
 
+static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
+{
+	struct amd_pmf_dev *pmf_dev = data;
+	struct apmf_if *apmf_if = pmf_dev->apmf_if;
+	int ret;
+
+	if (apmf_if->func.sbios_requests) {
+		struct apmf_sbios_req req;
+
+		ret = apmf_get_sbios_requests(apmf_if, &req);
+		if (ret) {
+			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
+			return;
+		}
+		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
+			pr_debug("PMF: AMT is supported and notifications %s\n",
+				 req.amt_event ? "Enabled" : "Disabled");
+			if (req.amt_event)
+				pmf_dev->is_amt_event = true;
+			else
+				pmf_dev->is_amt_event = !!req.amt_event;
+		}
+
+		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
+			pr_debug("PMF: CQL is supported and notifications %s\n",
+				 req.cql_event ? "Enabled" : "Disabled");
+			if (req.cql_event)
+				pmf_dev->is_cql_event = true;
+			else
+				pmf_dev->is_cql_event = !!req.cql_event;
+
+			/* update the target mode information */
+			amd_pmf_update_2_cql(pmf_dev);
+		}
+	}
+}
+
 void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
 {
+	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
+
 	if (pmf_dev->apmf_if->func.sbios_heartbeat)
 		cancel_delayed_work_sync(&pmf_dev->heart_beat);
+
+	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
+		acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
+					   apmf_event_handler);
 }
 
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 {
+	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
 	struct apmf_notification_cfg *n;
 	acpi_handle apmf_if_handle;
 	struct apmf_if *apmf_if;
-	int ret;
+	int ret, status;
 
 	apmf_if_handle = apmf_if_probe_handle();
 	if (!apmf_if_handle)
@@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 		schedule_delayed_work(&pmf_dev->heart_beat, 0);
 	}
 
+	/* Install the APMF Notify handler */
+	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
+		status = acpi_install_notify_handler(ahandle,
+						     ACPI_ALL_NOTIFY,
+						     apmf_event_handler, pmf_dev);
+		if (ACPI_FAILURE(status)) {
+			dev_err(pmf_dev->dev, "failed to install notify handler\n");
+			return -ENODEV;
+		}
+	}
+
 out:
 	return ret;
 }
diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
index 954fde25e71e..a85ef4b95cdb 100644
--- a/drivers/platform/x86/amd/pmf/auto-mode.c
+++ b/drivers/platform/x86/amd/pmf/auto-mode.c
@@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
 	bool update = false;
 	int i, j;
 
+	if (!dev->amt_running && dev->is_amt_event) {
+		dev_dbg(dev->dev, "setting AMT thermals\n");
+		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
+		dev->amt_running = true;
+		return;
+	}
+
 	/* Get the average moving average computed by auto mode algorithm */
 	avg_power = amd_pmf_get_moving_avg(socket_power);
 
@@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
 	}
 }
 
+void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
+{
+	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
+			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
+	if ((config_store.current_mode == AUTO_PERFORMANCE ||
+	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
+	    config_store.current_mode !=
+	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
+		config_store.current_mode =
+				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
+		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
+	}
+	dev_dbg(dev->dev, "updated CQL thermals\n");
+}
+
 static void amd_pmf_get_power_threshold(void)
 {
 	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
index 2b03ae1ad37f..eba8f0d79a62 100644
--- a/drivers/platform/x86/amd/pmf/cnqf.c
+++ b/drivers/platform/x86/amd/pmf/cnqf.c
@@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
 	}
 }
 
-void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
+void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
 {
 	struct cnqf_tran_params *tp;
 	int src, i, j, index = 0;
@@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
 	}
 
 	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
-		config_store.trans_param[src][i].timer += time_lapsed_ms;
+		config_store.trans_param[src][i].timer += time_elapsed_ms;
 		config_store.trans_param[src][i].total_power += socket_power;
 		config_store.trans_param[src][i].count++;
 
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 674ddf599135..2a3dacfb2005 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
 	enum platform_profile_option current_profile;
 	ktime_t time_elapsed_ms;
 	int socket_power;
+	u8 mode;
 
 	/* Get the current profile information */
 	platform_profile_get(&current_profile);
 
+	if (!dev->is_amt_event)
+		dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
+			dev->is_amt_event ? "Enabled" : "Disabled");
+
 	/* Transfer table contents */
 	memset(&dev->m_table, 0, sizeof(dev->m_table));
 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
@@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
 
 	if (current_profile == PLATFORM_PROFILE_BALANCED) {
-		/* Apply the Auto Mode transition */
-		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+		if (dev->is_amt_event) {
+			/* Apply the Auto Mode transition */
+			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+		} else if (!dev->is_amt_event && dev->amt_running) {
+			pr_debug("resetting AMT thermals\n");
+			mode = amd_pmf_get_pprof_modes(dev);
+			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
+			dev->amt_running = false;
+		}
+	} else {
+		dev->amt_running = false;
 	}
 
 	if (dev->cnqf_feat) {
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 0532f49e9613..9ae9812353cd 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -17,6 +17,7 @@
 /* APMF Functions */
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
+#define APMF_FUNC_SBIOS_REQUESTS			2
 #define APMF_FUNC_SBIOS_HEARTBEAT			4
 #define APMF_FUNC_AUTO_MODE					5
 #define APMF_FUNC_SET_FAN_IDX				7
@@ -51,6 +52,7 @@
 /* AMD PMF BIOS interfaces */
 struct apmf_if_functions {
 	bool system_params;
+	bool sbios_requests;
 	bool sbios_heartbeat;
 	bool auto_mode_def;
 	bool fan_table_idx;
@@ -84,6 +86,24 @@ struct apmf_system_params {
 	u32 heartbeat_int;
 } __packed;
 
+struct apmf_sbios_req {
+	u16 size;
+	u32 pending_req;
+	u8 rsd;
+	u8 cql_event;
+	u8 amt_event;
+	u32 fppt;
+	u32 sppt;
+	u32 fppt_apu_only;
+	u32 spl;
+	u32 stt_min_limit;
+	u8 skin_temp_apu;
+	u8 skin_temp_hs2;
+	u8 dps_enable;
+	u32 custom_policy_1;
+	u32 custom_policy_2;
+} __packed;
+
 struct apmf_fan_idx {
 	u16 size;
 	u8 fan_ctl_mode;
@@ -171,6 +191,9 @@ struct amd_pmf_dev {
 #endif /* CONFIG_DEBUG_FS */
 	bool cnqf_feat;
 	bool cnqf_running;
+	bool is_amt_event;
+	bool is_cql_event;
+	bool amt_running;
 };
 
 struct apmf_sps_prop_granular {
@@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
 /* Auto Mode Layer */
 void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
 int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
+int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
 void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
 void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
-void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
+void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
+void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
 
 /* CnQF Layer */
 int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
@@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
 void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
 void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
 void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
-void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
+void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
 
 #endif /* PMF_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (12 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 21:40   ` Hans de Goede
  2022-07-12 14:58 ` [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry Shyam Sundar S K
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

Some of the older platforms with _HID "AMDI0100" PMF driver can be force
loaded by interested users but only for experimental purposes.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/core.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 2a3dacfb2005..ef50d67f6013 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -39,6 +39,7 @@
 #define AMD_PMF_RESULT_FAILED                0xFF
 
 /* List of supported CPU ids */
+#define AMD_CPU_ID_RMB			0x14b5
 #define AMD_CPU_ID_PS			0x14e8
 
 #define PMF_MSG_DELAY_MIN_US		50
@@ -52,6 +53,11 @@ static int metrics_table_loop_ms = 1000;
 module_param(metrics_table_loop_ms, int, 0644);
 MODULE_PARM_DESC(metrics_table_loop_ms, " Metrics Table sample size time (default = 1000ms) ");
 
+/* Force load on supported older platforms */
+static bool force_load;
+module_param(force_load, bool, 0444);
+MODULE_PARM_DESC(force_load, " Force load this driver on supported older platforms (experimental) ");
+
 #ifdef CONFIG_DEBUG_FS
 static int current_power_limits_show(struct seq_file *seq, void *unused)
 {
@@ -240,6 +246,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
 }
 
 static const struct pci_device_id pmf_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
 	{ }
 };
@@ -313,6 +320,7 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
 }
 
 static const struct acpi_device_id amd_pmf_acpi_ids[] = {
+	{"AMDI0100", 0x100},
 	{"AMDI0102", 0},
 	{ }
 };
@@ -320,6 +328,7 @@ MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
 
 static int amd_pmf_probe(struct platform_device *pdev)
 {
+	const struct acpi_device_id *id;
 	struct amd_pmf_dev *dev;
 	struct pci_dev *rdev;
 	u32 base_addr_lo;
@@ -328,6 +337,13 @@ static int amd_pmf_probe(struct platform_device *pdev)
 	u32 val;
 	int err;
 
+	id = acpi_match_device(amd_pmf_acpi_ids, &pdev->dev);
+	if (!id)
+		return -ENODEV;
+
+	if (id->driver_data == 0x100 && !force_load)
+		return -ENODEV;
+
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 	if (!dev)
 		return -ENOMEM;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry
  2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
                   ` (13 preceding siblings ...)
  2022-07-12 14:58 ` [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms Shyam Sundar S K
@ 2022-07-12 14:58 ` Shyam Sundar S K
  2022-07-27 21:41   ` Hans de Goede
  14 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-12 14:58 UTC (permalink / raw)
  To: hdegoede, markgross; +Cc: platform-driver-x86, Patil.Reddy, Shyam Sundar S K

Update the MAINTAINERS file with AMD PMF driver details.

Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e01478062c56..d3f6cabcaab2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -998,6 +998,13 @@ L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/amd/pmc.c
 
+AMD PMF DRIVER
+M:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	Documentation/ABI/testing/sysfs-amd-pmf
+F:	drivers/platform/x86/amd/pmf/
+
 AMD HSMP DRIVER
 M:	Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
 R:	Carlos Bilbao <carlos.bilbao@amd.com>
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* RE: [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains
  2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
@ 2022-07-12 15:03   ` Limonciello, Mario
  2022-07-27 13:24     ` Hans de Goede
  2022-07-27 20:38   ` Hans de Goede
  1 sibling, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-12 15:03 UTC (permalink / raw)
  To: S-k, Shyam-sundar, hdegoede, markgross; +Cc: platform-driver-x86, Patil Rajesh

[Public]

> -----Original Message-----
> From: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
> Sent: Tuesday, July 12, 2022 09:59
> To: hdegoede@redhat.com; markgross@kernel.org
> Cc: platform-driver-x86@vger.kernel.org; Patil Rajesh
> <Patil.Reddy@amd.com>; Limonciello, Mario
> <Mario.Limonciello@amd.com>; S-k, Shyam-sundar <Shyam-sundar.S-
> k@amd.com>
> Subject: [PATCH v1 01/15] ACPI: platform_profile: Add support for
> notification chains
> 
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> Allow other drivers to react to determine current active profile
> and react to platform profile changes.
> 

The original patch this came from had notification chains, but as this was
pared down to just export the get method, this commit message and title
should be updated.

> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/acpi/platform_profile.c  | 26 ++++++++++++++++++++++++++
>  include/linux/platform_profile.h |  1 +
>  2 files changed, 27 insertions(+)
> 
> diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> index d418462ab791..7e12a1f30f06 100644
> --- a/drivers/acpi/platform_profile.c
> +++ b/drivers/acpi/platform_profile.c
> @@ -49,6 +49,32 @@ static ssize_t platform_profile_choices_show(struct
> device *dev,
>  	return len;
>  }
> 
> +int platform_profile_get(enum platform_profile_option *profile)
> +{
> +	int err;
> +
> +	err = mutex_lock_interruptible(&profile_lock);
> +	if (err)
> +		return err;
> +
> +	if (!cur_profile) {
> +		mutex_unlock(&profile_lock);
> +		return -ENODEV;
> +	}
> +
> +	err = cur_profile->profile_get(cur_profile, profile);
> +	mutex_unlock(&profile_lock);
> +	if (err)
> +		return err;
> +
> +	/* Check that profile is valid index */
> +	if (WARN_ON((*profile < 0) || (*profile >=
> ARRAY_SIZE(profile_names))))
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(platform_profile_get);
> +
>  static ssize_t platform_profile_show(struct device *dev,
>  					struct device_attribute *attr,
>  					char *buf)
> diff --git a/include/linux/platform_profile.h
> b/include/linux/platform_profile.h
> index e5cbb6841f3a..2395be670dfd 100644
> --- a/include/linux/platform_profile.h
> +++ b/include/linux/platform_profile.h
> @@ -37,5 +37,6 @@ struct platform_profile_handler {
>  int platform_profile_register(struct platform_profile_handler *pprof);
>  int platform_profile_remove(void);
>  void platform_profile_notify(void);
> +int platform_profile_get(enum platform_profile_option *profile);
> 
>  #endif  /*_PLATFORM_PROFILE_H_*/
> --
> 2.25.1

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains
  2022-07-12 15:03   ` Limonciello, Mario
@ 2022-07-27 13:24     ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 13:24 UTC (permalink / raw)
  To: Limonciello, Mario, S-k, Shyam-sundar, markgross
  Cc: platform-driver-x86, Patil Rajesh

Hi,

On 7/12/22 17:03, Limonciello, Mario wrote:
> [Public]
> 
>> -----Original Message-----
>> From: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
>> Sent: Tuesday, July 12, 2022 09:59
>> To: hdegoede@redhat.com; markgross@kernel.org
>> Cc: platform-driver-x86@vger.kernel.org; Patil Rajesh
>> <Patil.Reddy@amd.com>; Limonciello, Mario
>> <Mario.Limonciello@amd.com>; S-k, Shyam-sundar <Shyam-sundar.S-
>> k@amd.com>
>> Subject: [PATCH v1 01/15] ACPI: platform_profile: Add support for
>> notification chains
>>
>> From: Mario Limonciello <mario.limonciello@amd.com>
>>
>> Allow other drivers to react to determine current active profile
>> and react to platform profile changes.
>>
> 
> The original patch this came from had notification chains, but as this was
> pared down to just export the get method, this commit message and title
> should be updated.
> 
>> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>  drivers/acpi/platform_profile.c  | 26 ++++++++++++++++++++++++++
>>  include/linux/platform_profile.h |  1 +
>>  2 files changed, 27 insertions(+)
>>
>> diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
>> index d418462ab791..7e12a1f30f06 100644
>> --- a/drivers/acpi/platform_profile.c
>> +++ b/drivers/acpi/platform_profile.c
>> @@ -49,6 +49,32 @@ static ssize_t platform_profile_choices_show(struct
>> device *dev,
>>  	return len;
>>  }
>>
>> +int platform_profile_get(enum platform_profile_option *profile)
>> +{
>> +	int err;
>> +
>> +	err = mutex_lock_interruptible(&profile_lock);

Besides Mario'r remark about the commit message, this must be
mutex_lock() not mutex_lock_interruptible() since this function
is intended to be called by other kernel code, rather then from
userspace.

Regards,

Hans


>> +	if (err)
>> +		return err;
>> +
>> +	if (!cur_profile) {
>> +		mutex_unlock(&profile_lock);
>> +		return -ENODEV;
>> +	}
>> +
>> +	err = cur_profile->profile_get(cur_profile, profile);
>> +	mutex_unlock(&profile_lock);
>> +	if (err)
>> +		return err;
>> +
>> +	/* Check that profile is valid index */
>> +	if (WARN_ON((*profile < 0) || (*profile >=
>> ARRAY_SIZE(profile_names))))
>> +		return -EIO;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(platform_profile_get);
>> +
>>  static ssize_t platform_profile_show(struct device *dev,
>>  					struct device_attribute *attr,
>>  					char *buf)
>> diff --git a/include/linux/platform_profile.h
>> b/include/linux/platform_profile.h
>> index e5cbb6841f3a..2395be670dfd 100644
>> --- a/include/linux/platform_profile.h
>> +++ b/include/linux/platform_profile.h
>> @@ -37,5 +37,6 @@ struct platform_profile_handler {
>>  int platform_profile_register(struct platform_profile_handler *pprof);
>>  int platform_profile_remove(void);
>>  void platform_profile_notify(void);
>> +int platform_profile_get(enum platform_profile_option *profile);
>>
>>  #endif  /*_PLATFORM_PROFILE_H_*/
>> --
>> 2.25.1
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer
  2022-07-12 14:58 ` [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer Shyam Sundar S K
@ 2022-07-27 13:57   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 13:57 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> PMF driver implements the ACPI methods as defined by AMD for PMF Support.
> The ACPI layer acts as a glue that helps in providing the infrastructure
> for OEMs customization.
> 
> OEMs can refer to PMF support documentation to decide on the list of
> functions to be supported on their specific platform model.
> 
> AMD mandates that PMF ACPI fn0 and fn1 to be implemented which
> provides the set of functions, params and the notifications that
> would be sent to PMF driver so that PMF driver can adapt and
> react.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/Makefile |   2 +-
>  drivers/platform/x86/amd/pmf/acpi.c   | 188 ++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/core.c   |   1 +
>  drivers/platform/x86/amd/pmf/pmf.h    |  32 +++++
>  4 files changed, 222 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/x86/amd/pmf/acpi.c
> 
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index 459005f659e5..2617eba773ce 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -5,4 +5,4 @@
>  #
>  
>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> -amd-pmf-objs := core.o
> +amd-pmf-objs := core.o acpi.o
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> new file mode 100644
> index 000000000000..addcaae5675c
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -0,0 +1,188 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include <linux/acpi.h>
> +#include "pmf.h"
> +
> +#define APMF_METHOD "\\_SB_.PMF_.APMF"
> +
> +static unsigned long supported_func;
> +
> +static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask);
> +
> +static union acpi_object *apmf_if_call(struct apmf_if *apmf_if, int func, struct acpi_buffer *param)
> +{
> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> +	struct acpi_object_list apmf_if_arg_list;
> +	union acpi_object apmf_if_args[2];
> +	acpi_status status;
> +
> +	apmf_if_arg_list.count = 2;
> +	apmf_if_arg_list.pointer = &apmf_if_args[0];
> +
> +	apmf_if_args[0].type = ACPI_TYPE_INTEGER;
> +	apmf_if_args[0].integer.value = func;
> +
> +	if (param) {
> +		apmf_if_args[1].type = ACPI_TYPE_BUFFER;
> +		apmf_if_args[1].buffer.length = param->length;
> +		apmf_if_args[1].buffer.pointer = param->pointer;
> +	} else {
> +		apmf_if_args[1].type = ACPI_TYPE_INTEGER;
> +		apmf_if_args[1].integer.value = 0;
> +	}
> +
> +	status = acpi_evaluate_object(apmf_if->handle, NULL, &apmf_if_arg_list, &buffer);
> +	if (ACPI_FAILURE(status)) {
> +		pr_err("PMF: APMF method call failed\n");
> +		if (status != AE_NOT_FOUND)
> +			kfree(buffer.pointer);
> +
> +		return NULL;
> +	}
> +
> +	return buffer.pointer;
> +}
> +
> +static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
> +{
> +	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
> +}
> +
> +static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
> +{
> +	struct apmf_verify_interface output;
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_VERIFY_INTERFACE, NULL);
> +	if (!info)
> +		return -EIO;
> +

You are missing the following checks here:

	if (info->type != ACPI_TYPE_BUFFER) {
		dev_err(pdev->dev, "object is not a buffer\n");
		err = -EINVAL;
		goto out;
	}

	if (info->buffer.length < 2) }
		dev_err(pdev->dev, "buffer too small\n");
		err = -EINVAL;
		goto out;
	}

> +	size = *(u16 *)info->buffer.pointer;

And here you are missing:

	if (info->buffer.length < size) {
		dev_err(pdev->dev, "buffer smaller then headersize %zu < %zu\n",
			info->buffer.length, size);
		err = -EINVAL;
		goto out;
	}

> +
> +	if (size < sizeof(output)) {
> +		dev_err(pdev->dev, "buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(output), size);

You already know that size >= sizeof(output) because of the above if
so this boils down to:

	size = sizeof(output);



> +	memset(&output, 0, sizeof(output));

Since the memcpy size == sizeof(output), this memset is not necessary,
please drop it.

> +	memcpy(&output, info->buffer.pointer, size);
> +	apmf_if_parse_functions(&ampf_if->func, output.supported_functions);
> +	supported_func = output.supported_functions;
> +	dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
> +		output.supported_functions, output.notification_mask);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
> +int is_apmf_func_supported(unsigned long index)
> +{
> +	/* If bit-n is set, that indicates function n+1 is supported */
> +	return ((supported_func & (1 << (index - 1))) != 0);
> +}
> +
> +static int apmf_get_system_params(struct apmf_if *ampf_if)
> +{
> +	struct apmf_system_params params;
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_GET_SYS_PARAMS, NULL);
> +	if (!info) {
> +		err = -EIO;
> +		goto out;
> +	}
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(params)) {
> +		pr_err("PMF: buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(params), size);

All the same remarks wrt size handling as above. It would be good
to write a common helper for this, which takes a void *pointer to the
struct to fill as well as the struct size as parameters and then do
the apmf_if_call() + all the size checks + memcpy in the helper.


> +	memset(&params, 0, sizeof(params));
> +	memcpy(&params, info->buffer.pointer, size);
> +
> +	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
> +		 params.valid_mask,
> +		 params.flags,
> +		 params.command_code);
> +	params.flags = params.flags & params.valid_mask;
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
> +static acpi_handle apmf_if_probe_handle(void)
> +{
> +	acpi_handle handle = NULL;
> +	char acpi_method_name[255] = { 0 };

Please also use ACPI_FULL_PATHNAME here, like you do below
in the acpi_get_name() call.

> +	struct acpi_buffer buffer = { sizeof(acpi_method_name), acpi_method_name };

Please always use named struct initializers.

> +	acpi_status status;
> +
> +	status = acpi_get_handle(NULL, (acpi_string) APMF_METHOD, &handle);
> +	if (ACPI_SUCCESS(status))
> +		goto out;
> +
> +	pr_err("PMF: APMF handle not found\n");
> +	return NULL;
> +
> +out:
> +	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);

You already know the name since you just looked up the handle by its
ACPI path, so this is not useful and can be dropped.

> +	pr_debug("PMF: APMF handle %s found\n", acpi_method_name);
> +	return handle;
> +}
> +
> +int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
> +{
> +	acpi_handle apmf_if_handle;
> +	struct apmf_if *apmf_if;
> +	int ret;
> +
> +	apmf_if_handle = apmf_if_probe_handle();
> +	if (!apmf_if_handle)
> +		goto out;
> +
> +	apmf_if = kzalloc(sizeof(*apmf_if), GFP_KERNEL);
> +	if (!apmf_if)
> +		goto out;
> +
> +	apmf_if->handle = apmf_if_handle;
> +
> +	ret = apmf_if_verify_interface(pmf_dev, apmf_if);
> +	if (ret) {
> +		dev_err(pmf_dev->dev, "APMF verify interface failed :%d\n", ret);
> +		kfree(apmf_if);
> +		goto out;
> +	}
> +	pmf_dev->apmf_if = apmf_if;
> +
> +	if (apmf_if->func.system_params) {

Maybe move this if check to inside apmf_get_system_params() ?

> +		ret = apmf_get_system_params(apmf_if);
> +		if (ret) {
> +			dev_err(pmf_dev->dev, "APMF apmf_get_system_params failed :%d\n", ret);
> +			kfree(apmf_if);
> +			goto out;
> +		}
> +	}
> +
> +out:
> +	return ret;
> +}
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index aef97965c181..c5002b7bb904 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -204,6 +204,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
>  	if (!dev->regbase)
>  		return -ENOMEM;
>  
> +	apmf_acpi_init(dev);
>  	platform_set_drvdata(pdev, dev);
>  
>  	mutex_init(&dev->lock);
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index ab773aa5a6e1..40f038eb6197 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -10,6 +10,12 @@
>  #ifndef PMF_H
>  #define PMF_H
>  
> +#include <linux/acpi.h>
> +
> +/* APMF Functions */
> +#define APMF_FUNC_VERIFY_INTERFACE			0
> +#define APMF_FUNC_GET_SYS_PARAMS			1
> +
>  /* Message Definitions */
>  #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
>  #define SET_SPPT			0x05 /* SPPT: Slow Package Power Tracking */
> @@ -29,6 +35,30 @@
>  #define GET_STT_LIMIT_APU	0x20
>  #define GET_STT_LIMIT_HS2	0x21
>  
> +/* AMD PMF BIOS interfaces */
> +struct apmf_if_functions {
> +	bool system_params;
> +};
> +
> +struct apmf_if {
> +	acpi_handle handle;
> +	struct apmf_if_functions func;
> +};
> +
> +struct apmf_verify_interface {
> +	u16 size;
> +	u16 version;
> +	u32 notification_mask;
> +	u32 supported_functions;
> +} __packed;
> +
> +struct apmf_system_params {
> +	u16 size;
> +	u32 valid_mask;
> +	u32 flags;
> +	u8 command_code;
> +} __packed;
> +
>  struct amd_pmf_dev {
>  	void __iomem *regbase;
>  	void __iomem *smu_virt_addr;
> @@ -38,10 +68,12 @@ struct amd_pmf_dev {
>  	u32 hi;
>  	u32 low;
>  	struct device *dev;
> +	struct apmf_if *apmf_if;
>  	struct mutex lock; /* protects the PMF interface */
>  };
>  
>  /* Core Layer */
> +int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>  
>  #endif /* PMF_H */


Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer
  2022-07-12 14:58 ` [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer Shyam Sundar S K
@ 2022-07-27 13:57   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 13:57 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> PMF core layer is meant to abstract the common functionalities
> across PMF features. This layer also does the plumbing work
> like setting up the mailbox channel for the communication
> between the PMF driver and the PMFW (Power Management Firmware)
> running on the SMU.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans

> ---
>  drivers/platform/x86/amd/Kconfig      |   2 +
>  drivers/platform/x86/amd/Makefile     |   1 +
>  drivers/platform/x86/amd/pmf/Kconfig  |  16 ++
>  drivers/platform/x86/amd/pmf/Makefile |   8 +
>  drivers/platform/x86/amd/pmf/core.c   | 235 ++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h    |  47 ++++++
>  6 files changed, 309 insertions(+)
>  create mode 100644 drivers/platform/x86/amd/pmf/Kconfig
>  create mode 100644 drivers/platform/x86/amd/pmf/Makefile
>  create mode 100644 drivers/platform/x86/amd/pmf/core.c
>  create mode 100644 drivers/platform/x86/amd/pmf/pmf.h
> 
> diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig
> index c0d0a3c5170c..a825af8126c8 100644
> --- a/drivers/platform/x86/amd/Kconfig
> +++ b/drivers/platform/x86/amd/Kconfig
> @@ -3,6 +3,8 @@
>  # AMD x86 Platform Specific Drivers
>  #
>  
> +source "drivers/platform/x86/amd/pmf/Kconfig"
> +
>  config AMD_PMC
>  	tristate "AMD SoC PMC driver"
>  	depends on ACPI && PCI && RTC_CLASS
> diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile
> index a03fbb08e808..2c229198e24c 100644
> --- a/drivers/platform/x86/amd/Makefile
> +++ b/drivers/platform/x86/amd/Makefile
> @@ -8,3 +8,4 @@ amd-pmc-y			:= pmc.o
>  obj-$(CONFIG_AMD_PMC)		+= amd-pmc.o
>  amd_hsmp-y			:= hsmp.o
>  obj-$(CONFIG_AMD_HSMP)		+= amd_hsmp.o
> +obj-$(CONFIG_AMD_PMF)		+= pmf/
> diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig
> new file mode 100644
> index 000000000000..2a5f72419515
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/Kconfig
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# AMD PMF Driver
> +#
> +
> +config AMD_PMF
> +	tristate "AMD Platform Management Framework"
> +	depends on ACPI
> +	help
> +	  This driver provides support for the AMD Platform Management Framework.
> +	  The goal is to enhance end user experience by making AMD PCs smarter,
> +	  quiter, power efficient by adapting to user behavior and environment.
> +
> +	  To compile this driver as a module, choose M here: the module will
> +	  be called amd_pmf.
> +
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> new file mode 100644
> index 000000000000..459005f659e5
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for linux/drivers/platform/x86/amd/pmf
> +# AMD Platform Management Framework
> +#
> +
> +obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> +amd-pmf-objs := core.o
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> new file mode 100644
> index 000000000000..aef97965c181
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -0,0 +1,235 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include "pmf.h"
> +
> +/* PMF-SMU communication registers */
> +#define AMD_PMF_REGISTER_MESSAGE	0xA18
> +#define AMD_PMF_REGISTER_RESPONSE	0xA78
> +#define AMD_PMF_REGISTER_ARGUMENT	0xA58
> +
> +/* Base address of SMU for mapping physical address to virtual address */
> +#define AMD_PMF_SMU_INDEX_ADDRESS	0xB8
> +#define AMD_PMF_SMU_INDEX_DATA		0xBC
> +#define AMD_PMF_MAPPING_SIZE		0x01000
> +#define AMD_PMF_BASE_ADDR_OFFSET	0x10000
> +#define AMD_PMF_BASE_ADDR_LO		0x13B102E8
> +#define AMD_PMF_BASE_ADDR_HI		0x13B102EC
> +#define AMD_PMF_BASE_ADDR_LO_MASK	GENMASK(15, 0)
> +#define AMD_PMF_BASE_ADDR_HI_MASK	GENMASK(31, 20)
> +
> +/* SMU Response Codes */
> +#define AMD_PMF_RESULT_OK                    0x01
> +#define AMD_PMF_RESULT_CMD_REJECT_BUSY       0xFC
> +#define AMD_PMF_RESULT_CMD_REJECT_PREREQ     0xFD
> +#define AMD_PMF_RESULT_CMD_UNKNOWN           0xFE
> +#define AMD_PMF_RESULT_FAILED                0xFF
> +
> +/* List of supported CPU ids */
> +#define AMD_CPU_ID_PS			0x14e8
> +
> +#define PMF_MSG_DELAY_MIN_US		50
> +#define RESPONSE_REGISTER_LOOP_MAX	20000
> +
> +#define DELAY_MIN_US	2000
> +#define DELAY_MAX_US	3000
> +
> +static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
> +{
> +	return ioread32(dev->regbase + reg_offset);
> +}
> +
> +static inline void amd_pmf_reg_write(struct amd_pmf_dev *dev, int reg_offset, u32 val)
> +{
> +	iowrite32(val, dev->regbase + reg_offset);
> +}
> +
> +static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
> +{
> +	u32 value;
> +
> +	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE);
> +	dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
> +
> +	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
> +	dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
> +
> +	value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE);
> +	dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
> +}
> +
> +int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
> +{
> +	int rc;
> +	u32 val;
> +
> +	mutex_lock(&dev->lock);
> +
> +	/* Wait until we get a valid response */
> +	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
> +				val, val != 0, PMF_MSG_DELAY_MIN_US,
> +				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
> +	if (rc) {
> +		dev_err(dev->dev, "failed to talk to SMU\n");
> +		goto out_unlock;
> +	}
> +
> +	/* Write zero to response register */
> +	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0);
> +
> +	/* Write argument into argument register */
> +	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg);
> +
> +	/* Write message ID to message ID register */
> +	amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message);
> +
> +	/* Wait until we get a valid response */
> +	rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
> +				val, val != 0, PMF_MSG_DELAY_MIN_US,
> +				PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
> +	if (rc) {
> +		dev_err(dev->dev, "SMU response timed out\n");
> +		goto out_unlock;
> +	}
> +
> +	switch (val) {
> +	case AMD_PMF_RESULT_OK:
> +		if (get) {
> +			/* PMFW may take longer time to return back the data */
> +			usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
> +			*data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
> +		}
> +		break;
> +	case AMD_PMF_RESULT_CMD_REJECT_BUSY:
> +		dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
> +		rc = -EBUSY;
> +		goto out_unlock;
> +	case AMD_PMF_RESULT_CMD_UNKNOWN:
> +		dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
> +		rc = -EINVAL;
> +		goto out_unlock;
> +	case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
> +	case AMD_PMF_RESULT_FAILED:
> +	default:
> +		dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
> +		rc = -EIO;
> +		goto out_unlock;
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&dev->lock);
> +	amd_pmf_dump_registers(dev);
> +	return rc;
> +}
> +
> +static const struct pci_device_id pmf_pci_ids[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
> +	{ }
> +};
> +
> +static const struct acpi_device_id amd_pmf_acpi_ids[] = {
> +	{"AMDI0102", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
> +
> +static int amd_pmf_probe(struct platform_device *pdev)
> +{
> +	struct amd_pmf_dev *dev;
> +	struct pci_dev *rdev;
> +	u32 base_addr_lo;
> +	u32 base_addr_hi;
> +	u64 base_addr;
> +	u32 val;
> +	int err;
> +
> +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->dev = &pdev->dev;
> +
> +	rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
> +	if (!rdev || !pci_match_id(pmf_pci_ids, rdev)) {
> +		pci_dev_put(rdev);
> +		return -ENODEV;
> +	}
> +
> +	dev->cpu_id = rdev->device;
> +	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO);
> +	if (err) {
> +		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
> +		pci_dev_put(rdev);
> +		return pcibios_err_to_errno(err);
> +	}
> +
> +	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
> +	if (err) {
> +		pci_dev_put(rdev);
> +		return pcibios_err_to_errno(err);
> +	}
> +
> +	base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
> +
> +	err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI);
> +	if (err) {
> +		dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
> +		pci_dev_put(rdev);
> +		return pcibios_err_to_errno(err);
> +	}
> +
> +	err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
> +	if (err) {
> +		pci_dev_put(rdev);
> +		return pcibios_err_to_errno(err);
> +	}
> +
> +	base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK;
> +	pci_dev_put(rdev);
> +	base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
> +
> +	dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMF_BASE_ADDR_OFFSET,
> +				    AMD_PMF_MAPPING_SIZE);
> +	if (!dev->regbase)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, dev);
> +
> +	mutex_init(&dev->lock);
> +	dev_info(dev->dev, "registered PMF device successfully\n");
> +
> +	return 0;
> +}
> +
> +static int amd_pmf_remove(struct platform_device *pdev)
> +{
> +	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
> +
> +	mutex_destroy(&dev->lock);
> +	kfree(dev->buf);
> +	return 0;
> +}
> +
> +static struct platform_driver amd_pmf_driver = {
> +	.driver = {
> +		.name = "amd-pmf",
> +		.acpi_match_table = amd_pmf_acpi_ids,
> +	},
> +	.probe = amd_pmf_probe,
> +	.remove = amd_pmf_remove,
> +};
> +module_platform_driver(amd_pmf_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> new file mode 100644
> index 000000000000..ab773aa5a6e1
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +#ifndef PMF_H
> +#define PMF_H
> +
> +/* Message Definitions */
> +#define SET_SPL				0x03 /* SPL: Sustained Power Limit */
> +#define SET_SPPT			0x05 /* SPPT: Slow Package Power Tracking */
> +#define SET_FPPT			0x07 /* FPPT: Fast Package Power Tracking */
> +#define GET_SPL				0x0B
> +#define GET_SPPT			0x0D
> +#define GET_FPPT			0x0F
> +#define SET_DRAM_ADDR_HIGH	0x14
> +#define SET_DRAM_ADDR_LOW	0x15
> +#define SET_TRANSFER_TABLE	0x16
> +#define SET_STT_MIN_LIMIT	0x18 /* STT: Skin Temperature Tracking */
> +#define SET_STT_LIMIT_APU	0x19
> +#define SET_STT_LIMIT_HS2	0x1A
> +#define SET_SPPT_APU_ONLY	0x1D
> +#define GET_SPPT_APU_ONLY	0x1E
> +#define GET_STT_MIN_LIMIT	0x1F
> +#define GET_STT_LIMIT_APU	0x20
> +#define GET_STT_LIMIT_HS2	0x21
> +
> +struct amd_pmf_dev {
> +	void __iomem *regbase;
> +	void __iomem *smu_virt_addr;
> +	void *buf;
> +	u32 base_addr;
> +	u32 cpu_id;
> +	u32 hi;
> +	u32 low;
> +	struct device *dev;
> +	struct mutex lock; /* protects the PMF interface */
> +};
> +
> +/* Core Layer */
> +int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
> +
> +#endif /* PMF_H */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature
  2022-07-12 14:58 ` [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature Shyam Sundar S K
@ 2022-07-27 19:29   ` Hans de Goede
  2022-07-27 20:26     ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 19:29 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> SPS (a.k.a. Static Power Slider) gives a feel of Windows performance
> power slider for the Linux users, where the user selects a certain
> mode (like "balanced", "low-power" or "performance") and the thermals
> associated with each selected mode gets applied from the silicon
> side via the mailboxes defined through PMFW.
> 
> PMF driver hooks to platform_profile by reading the PMF ACPI fn9 to
> see if the support is being advertised by ACPI interface.
> 
> If supported, the PMF driver reacts to platform_profile selection choices
> made by the user and adjust the system thermal behavior.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/Makefile |   2 +-
>  drivers/platform/x86/amd/pmf/acpi.c   |  29 +++++
>  drivers/platform/x86/amd/pmf/core.c   |  26 ++++
>  drivers/platform/x86/amd/pmf/pmf.h    |  65 ++++++++++
>  drivers/platform/x86/amd/pmf/sps.c    | 166 ++++++++++++++++++++++++++
>  5 files changed, 287 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/x86/amd/pmf/sps.c
> 
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index 2617eba773ce..557521a80427 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -5,4 +5,4 @@
>  #
>  
>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> -amd-pmf-objs := core.o acpi.o
> +amd-pmf-objs := core.o acpi.o sps.o
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index addcaae5675c..fd5ab7a116f0 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -54,6 +54,7 @@ static union acpi_object *apmf_if_call(struct apmf_if *apmf_if, int func, struct
>  static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
> +	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>  }
>  
>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
> @@ -130,6 +131,34 @@ static int apmf_get_system_params(struct apmf_if *ampf_if)
>  	return err;
>  }
>  
> +int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
> +				    struct apmf_static_slider_granular_output *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_STATIC_SLIDER_GRANULAR, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_err("PMF: buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);

Same remarks wrt size handling as in patch 3/15. As mentioned you really need
a helper for this. Something with a prototype like this:

int apmf_if_call_store_buffer(struct apmf_if *ampf_if, int func, void *dest, size_t dest_size);

And then this entire function becomes just:

int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
				    struct apmf_static_slider_granular_output *data)
	return apmf_if_call_store_buffer(ampf_if, APMF_FUNC_STATIC_SLIDER_GRANULAR, data, sizeof(*data));
}


> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index c5002b7bb904..b6006e8ee1a1 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -12,6 +12,7 @@
>  #include <linux/module.h>
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
>  #include "pmf.h"
>  
>  /* PMF-SMU communication registers */
> @@ -45,6 +46,14 @@
>  #define DELAY_MIN_US	2000
>  #define DELAY_MAX_US	3000
>  
> +int amd_pmf_get_power_source(void)
> +{
> +	if (power_supply_is_system_supplied() > 0)
> +		return POWER_SOURCE_AC;
> +	else
> +		return POWER_SOURCE_DC;
> +}
> +>  static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
>  {
>  	return ioread32(dev->regbase + reg_offset);
> @@ -138,6 +147,21 @@ static const struct pci_device_id pmf_pci_ids[] = {
>  	{ }
>  };
>  
> +static void amd_pmf_init_features(struct amd_pmf_dev *dev)
> +{
> +	/* Enable Static Slider */
> +	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR)) {

Just put a:

	if (!is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
		return;

check at the top of amd_pmf_init_sps(dev); and then you can drop this
entire function.

> +		amd_pmf_init_sps(dev);
> +		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
> +	}
> +}
> +
> +static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
> +{
> +	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
> +		amd_pmf_deinit_sps(dev);

Same here.

> +}
> +
>  static const struct acpi_device_id amd_pmf_acpi_ids[] = {
>  	{"AMDI0102", 0},
>  	{ }
> @@ -206,6 +230,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
>  
>  	apmf_acpi_init(dev);
>  	platform_set_drvdata(pdev, dev);
> +	amd_pmf_init_features(dev);
>  
>  	mutex_init(&dev->lock);
>  	dev_info(dev->dev, "registered PMF device successfully\n");
> @@ -218,6 +243,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>  	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
>  
>  	mutex_destroy(&dev->lock);
> +	amd_pmf_deinit_features(dev);
>  	kfree(dev->buf);
>  	return 0;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 40f038eb6197..a405987ae653 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -7,14 +7,17 @@
>   *
>   * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>   */
> +
>  #ifndef PMF_H
>  #define PMF_H
>  
>  #include <linux/acpi.h>
> +#include <linux/platform_profile.h>
>  
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>  
>  /* Message Definitions */
>  #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
> @@ -35,9 +38,12 @@
>  #define GET_STT_LIMIT_APU	0x20
>  #define GET_STT_LIMIT_HS2	0x21
>  
> +#define ARG_NONE 0
> +
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
> +	bool static_slider_granular;
>  };
>  
>  struct apmf_if {
> @@ -59,6 +65,30 @@ struct apmf_system_params {
>  	u8 command_code;
>  } __packed;
>  
> +enum amd_stt_skin_temp {
> +	STT_TEMP_APU,
> +	STT_TEMP_HS2,
> +	STT_TEMP_COUNT,
> +};
> +
> +enum amd_slider_op {
> +	SLIDER_OP_GET,
> +	SLIDER_OP_SET,
> +};
> +
> +enum power_source {
> +	POWER_SOURCE_AC,
> +	POWER_SOURCE_DC,
> +	POWER_SOURCE_MAX,
> +};
> +
> +enum power_modes {
> +	POWER_MODE_PERFORMANCE,
> +	POWER_MODE_BALANCED_POWER,
> +	POWER_MODE_POWER_SAVER,
> +	POWER_MODE_MAX,
> +};
> +
>  struct amd_pmf_dev {
>  	void __iomem *regbase;
>  	void __iomem *smu_virt_addr;
> @@ -69,11 +99,46 @@ struct amd_pmf_dev {
>  	u32 low;
>  	struct device *dev;
>  	struct apmf_if *apmf_if;
> +	enum platform_profile_option current_profile;
> +	struct platform_profile_handler pprof;
>  	struct mutex lock; /* protects the PMF interface */
>  };
>  
> +struct apmf_sps_prop_granular {
> +	u32 fppt;
> +	u32 sppt;
> +	u32 sppt_apu_only;
> +	u32 spl;
> +	u32 stt_min;
> +	u8 stt_skin_temp[STT_TEMP_COUNT];
> +	u32 fan_id;
> +} __packed;
> +
> +/* Static Slider */
> +struct apmf_static_slider_granular_output {
> +	u16 size;
> +	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX * POWER_MODE_MAX];
> +};

missing __packed here I presume, since this is the data returned
by an ACPI call ?

> +
> +struct amd_pmf_static_slider_granular {
> +	u16 size;
> +	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
> +};
> +
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
> +int is_apmf_func_supported(unsigned long index);
>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
> +int amd_pmf_get_power_source(void);
> +
> +/* SPS Layer */
> +u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
> +void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
> +			   struct amd_pmf_static_slider_granular *table);
> +int amd_pmf_init_sps(struct amd_pmf_dev *dev);
> +void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
> +int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
> +				    struct apmf_static_slider_granular_output *output);
> +void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>  
>  #endif /* PMF_H */
> diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
> new file mode 100644
> index 000000000000..25289cf5608c
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/sps.c
> @@ -0,0 +1,166 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Platform Management Framework (PMF) Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include "pmf.h"
> +
> +static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
> +			       enum platform_profile_option *profile);
> +static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
> +			       enum platform_profile_option profile);
> +static struct amd_pmf_static_slider_granular config_store;
> +
> +void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_static_slider_granular_output output;
> +	int i, j, idx = 0;
> +
> +	memset(&config_store, 0, sizeof(config_store));
> +
> +	if (dev->apmf_if->func.static_slider_granular) {
> +		apmf_get_static_slider_granular(dev->apmf_if, &output);
> +
> +		for (i = 0; i < POWER_SOURCE_MAX; i++) {
> +			for (j = 0; j < POWER_MODE_MAX; j++) {
> +				config_store.prop[i][j].spl = output.prop[idx].spl;
> +				config_store.prop[i][j].sppt = output.prop[idx].sppt;
> +				config_store.prop[i][j].sppt_apu_only =
> +							output.prop[idx].sppt_apu_only;
> +				config_store.prop[i][j].fppt = output.prop[idx].fppt;
> +				config_store.prop[i][j].stt_min = output.prop[idx].stt_min;
> +				config_store.prop[i][j].stt_skin_temp[STT_TEMP_APU] =
> +						output.prop[idx].stt_skin_temp[STT_TEMP_APU];
> +				config_store.prop[i][j].stt_skin_temp[STT_TEMP_HS2] =
> +						output.prop[idx].stt_skin_temp[STT_TEMP_HS2];
> +				config_store.prop[i][j].fan_id = output.prop[idx].fan_id;
> +				idx++;
> +			}
> +		}
> +	}
> +}
> +
> +void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
> +			   struct amd_pmf_static_slider_granular *table)
> +{
> +	int src = amd_pmf_get_power_source();
> +
> +	if (op == SLIDER_OP_SET) {
> +		amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
> +		amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
> +				 config_store.prop[src][idx].sppt_apu_only, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
> +				 config_store.prop[src][idx].stt_min, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
> +	} else if (op == SLIDER_OP_GET) {
> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
> +				 &table->prop[src][idx].sppt_apu_only);
> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
> +				 &table->prop[src][idx].stt_min);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
> +				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
> +				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]);
> +	}
> +}
> +
> +static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
> +			       enum platform_profile_option *profile)
> +{
> +	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
> +
> +	*profile = pmf->current_profile;
> +	return 0;
> +}
> +
> +u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
> +{
> +	u8 mode;
> +
> +	switch (pmf->current_profile) {
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		mode = 0;

Please use POWER_MODE_PERFORMANCE instead of 1 here.

> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		mode = 1;

POWER_MODE_BALANCED_POWER instead of 1.

> +		break;
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		mode = 2;

POWER_MODE_POWER_SAVER instead of 2.

> +		break;
> +	default:
> +		dev_err(pmf->dev, "Unknown Platform Profile.\n");
> +		break;
> +	}
> +
> +	return mode;
> +}
> +
> +int amd_pmf_profile_set(struct platform_profile_handler *pprof,
> +			enum platform_profile_option profile)
> +{
> +	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
> +	u8 mode;
> +
> +	pmf->current_profile = profile;
> +	mode = amd_pmf_get_pprof_modes(pmf);
> +	amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
> +	return 0;
> +}
> +
> +static void amd_pmf_profile_refresh(struct amd_pmf_dev *dev)
> +{
> +	enum platform_profile_option profile;
> +
> +	/* Set intially to Balanced Mode */
> +	profile = PLATFORM_PROFILE_BALANCED;
> +
> +	if (profile != dev->current_profile) {
> +		dev->current_profile = profile;
> +		platform_profile_notify();
> +	}
> +}
> +
> +int amd_pmf_init_sps(struct amd_pmf_dev *dev)
> +{
> +	int err = 0;
> +
> +	dev->pprof.profile_get = amd_pmf_profile_get;
> +	dev->pprof.profile_set = amd_pmf_profile_set;
> +
> +	/* Setup supported modes */
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
> +
> +	/* Create platform_profile structure and register */
> +	err = platform_profile_register(&dev->pprof);
> +	if (err) {
> +		dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
> +			err);
> +		return -EEXIST;
> +	}
> +	/* Ensure initial values are correct */
> +	amd_pmf_profile_refresh(dev);

Since dev->current_profile == 0 and thus  dev->current_profile != PLATFORM_PROFILE_BALANCED
this ends up doing 2 things:

1.	dev->current_profile = PLATFORM_PROFILE_BALANCED;
2.	platform_profile_notify()

Where 2 is not useful since the /sys/firmware/acpi/power_profile
attribute does not exist before the platform_profile_register() call
we have just done, so nothing is listening on it.

So this entire function call can be replaced with just:

	dev->current_profile = PLATFORM_PROFILE_BALANCED;

and the badly named refresh (it is not a refresh since it does
not fetch values from the hw) function can be dropped.

> +
> +	amd_pmf_load_defaults_sps(dev);
> +
> +	return err;
> +}
> +
> +void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
> +{
> +	platform_profile_remove();
> +}

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information
  2022-07-12 14:58 ` [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information Shyam Sundar S K
@ 2022-07-27 19:50   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 19:50 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> Add debugfs support to the PMF driver so that using this interface the
> live counters from the PMFW can be queried to see if the power parameters
> are getting set properly when a certain power mode change happens.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/core.c | 46 +++++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h  |  3 ++
>  2 files changed, 49 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index b6006e8ee1a1..3d2af6a8e5e4 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -8,6 +8,7 @@
>   * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>   */
>  
> +#include <linux/debugfs.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
>  #include <linux/pci.h>
> @@ -46,6 +47,49 @@
>  #define DELAY_MIN_US	2000
>  #define DELAY_MAX_US	3000
>  
> +#ifdef CONFIG_DEBUG_FS

linux/debugfs.h defines stubs for the used debugfs functions when
debugfs is not enabled, which will cause all the referred code
to also get optimized away. So the #ifdef here is not necessary
please drop it.

> +static int current_power_limits_show(struct seq_file *seq, void *unused)
> +{
> +	struct amd_pmf_dev *dev = seq->private;
> +	struct amd_pmf_static_slider_granular table;
> +	int mode, src = 0;
> +
> +	mode = amd_pmf_get_pprof_modes(dev);
> +	src = amd_pmf_get_power_source();
> +	amd_pmf_update_slider(dev, SLIDER_OP_GET, mode, &table);
> +	seq_printf(seq, "spl:%u fppt:%u sppt:%u sppt_apu_only:%u stt_min:%u stt[APU]:%u stt[HS2]: %u\n",
> +		   table.prop[src][mode].spl,
> +		   table.prop[src][mode].fppt,
> +		   table.prop[src][mode].sppt,
> +		   table.prop[src][mode].sppt_apu_only,
> +		   table.prop[src][mode].stt_min,
> +		   table.prop[src][mode].stt_skin_temp[STT_TEMP_APU],
> +		   table.prop[src][mode].stt_skin_temp[STT_TEMP_HS2]);
> +	return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(current_power_limits);
> +
> +static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
> +{
> +	debugfs_remove_recursive(dev->dbgfs_dir);
> +}
> +
> +static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
> +{
> +	dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
> +	debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
> +			    &current_power_limits_fops);
> +}
> +#else
> +static inline void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
> +{
> +}
> +
> +static inline void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
> +{
> +}

and also drop the entire #else block where you define your own stubs.

> +#endif /* CONFIG_DEBUG_FS */
> +
>  int amd_pmf_get_power_source(void)
>  {
>  	if (power_supply_is_system_supplied() > 0)
> @@ -231,6 +275,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
>  	apmf_acpi_init(dev);
>  	platform_set_drvdata(pdev, dev);
>  	amd_pmf_init_features(dev);
> +	amd_pmf_dbgfs_register(dev);
>  
>  	mutex_init(&dev->lock);
>  	dev_info(dev->dev, "registered PMF device successfully\n");
> @@ -244,6 +289,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>  
>  	mutex_destroy(&dev->lock);
>  	amd_pmf_deinit_features(dev);
> +	amd_pmf_dbgfs_unregister(dev);
>  	kfree(dev->buf);
>  	return 0;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index a405987ae653..77021ef4bfde 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -102,6 +102,9 @@ struct amd_pmf_dev {
>  	enum platform_profile_option current_profile;
>  	struct platform_profile_handler pprof;
>  	struct mutex lock; /* protects the PMF interface */
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> +	struct dentry *dbgfs_dir;
> +#endif /* CONFIG_DEBUG_FS */

And don't forget to drop the #if here. Also note your #if-s are not consistent
you are using:

#ifdef CONFIG_DEBUG_FS

vs:

#if IS_ENABLED(CONFIG_DEBUG_FS)

But since both should be dropped anyways this is not a problem :)

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support
  2022-07-12 14:58 ` [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support Shyam Sundar S K
@ 2022-07-27 19:53   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 19:53 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
> not receive the signal after a period of time, it can infer that AMDPMF
> has hung or failed to load.
> 
> In this situation, BIOS can fallback to legacy operations. OEM can modify
> the time interval of the signal or completely disable signals through
> ACPI Method.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
>  drivers/platform/x86/amd/pmf/acpi.c | 42 +++++++++++++++++++++++++++--
>  drivers/platform/x86/amd/pmf/core.c |  1 +
>  drivers/platform/x86/amd/pmf/pmf.h  | 10 +++++++
>  3 files changed, 51 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index fd5ab7a116f0..60ffc9ba4adc 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -97,6 +97,7 @@ int is_apmf_func_supported(unsigned long index)
>  
>  static int apmf_get_system_params(struct apmf_if *ampf_if)
>  {
> +	struct apmf_notification_cfg *n = &ampf_if->notify_cfg;
>  	struct apmf_system_params params;
>  	union acpi_object *info;
>  	size_t size;
> @@ -120,11 +121,13 @@ static int apmf_get_system_params(struct apmf_if *ampf_if)
>  	memset(&params, 0, sizeof(params));
>  	memcpy(&params, info->buffer.pointer, size);
>  
> -	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
> +	pr_debug("PMF: system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
>  		 params.valid_mask,
>  		 params.flags,
> -		 params.command_code);
> +		 params.command_code,
> +		 params.heartbeat_int);
>  	params.flags = params.flags & params.valid_mask;
> +	n->hb_interval = params.heartbeat_int;
>  
>  out:
>  	kfree(info);
> @@ -159,6 +162,26 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>  	return err;
>  }
>  
> +static void apmf_sbios_heartbeat_notify(struct work_struct *work)
> +{
> +	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
> +	struct apmf_notification_cfg *n = &dev->apmf_if->notify_cfg;
> +	union acpi_object *info;
> +	int err = 0;
> +
> +	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
> +	info = apmf_if_call(dev->apmf_if, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
> +	if (!info) {
> +		err = -EIO;
> +		goto out;
> +	}
> +
> +	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(n->hb_interval * 1000));
> +
> +out:
> +	kfree(info);
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> @@ -179,8 +202,15 @@ static acpi_handle apmf_if_probe_handle(void)
>  	return handle;
>  }
>  
> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
> +{
> +	if (pmf_dev->apmf_if->func.sbios_heartbeat)
> +		cancel_delayed_work_sync(&pmf_dev->heart_beat);
> +}
> +
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  {
> +	struct apmf_notification_cfg *n;
>  	acpi_handle apmf_if_handle;
>  	struct apmf_if *apmf_if;
>  	int ret;
> @@ -202,6 +232,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		goto out;
>  	}
>  	pmf_dev->apmf_if = apmf_if;
> +	n = &apmf_if->notify_cfg;
>  
>  	if (apmf_if->func.system_params) {
>  		ret = apmf_get_system_params(apmf_if);
> @@ -212,6 +243,13 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		}
>  	}
>  
> +	if (n->hb_interval) {
> +		apmf_if->func.sbios_heartbeat = true;
> +		/* send heartbeats only if the interval is not zero */
> +		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
> +		schedule_delayed_work(&pmf_dev->heart_beat, 0);
> +	}
> +
>  out:
>  	return ret;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 3d2af6a8e5e4..ff26928e6a49 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -289,6 +289,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>  
>  	mutex_destroy(&dev->lock);
>  	amd_pmf_deinit_features(dev);
> +	apmf_acpi_deinit(dev);
>  	amd_pmf_dbgfs_unregister(dev);
>  	kfree(dev->buf);
>  	return 0;
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 77021ef4bfde..504e1ae79706 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -17,6 +17,7 @@
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>  
>  /* Message Definitions */
> @@ -43,12 +44,18 @@
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
> +	bool sbios_heartbeat;
>  	bool static_slider_granular;
>  };
>  
> +struct apmf_notification_cfg {
> +	int hb_interval; /* SBIOS heartbeat interval */
> +};
> +
>  struct apmf_if {
>  	acpi_handle handle;
>  	struct apmf_if_functions func;
> +	struct apmf_notification_cfg notify_cfg;
>  };
>  
>  struct apmf_verify_interface {
> @@ -63,6 +70,7 @@ struct apmf_system_params {
>  	u32 valid_mask;
>  	u32 flags;
>  	u8 command_code;
> +	u32 heartbeat_int;
>  } __packed;
>  
>  enum amd_stt_skin_temp {
> @@ -101,6 +109,7 @@ struct amd_pmf_dev {
>  	struct apmf_if *apmf_if;
>  	enum platform_profile_option current_profile;
>  	struct platform_profile_handler pprof;
> +	struct delayed_work heart_beat;
>  	struct mutex lock; /* protects the PMF interface */
>  #if IS_ENABLED(CONFIG_DEBUG_FS)
>  	struct dentry *dbgfs_dir;
> @@ -130,6 +139,7 @@ struct amd_pmf_static_slider_granular {
>  
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
>  int is_apmf_func_supported(unsigned long index);
>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>  int amd_pmf_get_power_source(void);


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support
  2022-07-12 14:58 ` [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support Shyam Sundar S K
@ 2022-07-27 20:11   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:11 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> PMF has a generic interface defined via PMF ACPI fn9 for influencing fan
> policy during mode switch.
> 
> PMF ACPI fn9 will normally be invoked when AMDPMF needs to change the fan
> table index for the EC. When AMDPMF is loaded this function will be invoked
> to change fan speed. OEM can also choose to report the actual fan table
> index and fan RPM to PMF through OEM structure.
> 
> This information will be communicated by PMF driver to customer BIOS.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
>  drivers/platform/x86/amd/pmf/acpi.c | 26 ++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h  | 17 +++++++++++++++++
>  2 files changed, 43 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index 60ffc9ba4adc..e4822c6f4259 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
> +	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>  }
>  
>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
> @@ -182,6 +183,31 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
>  	kfree(info);
>  }
>  
> +int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
> +{
> +	union acpi_object *info;
> +	struct apmf_fan_idx args;
> +	struct acpi_buffer params;
> +	int err = 0;
> +
> +	args.size = sizeof(args);
> +	args.fan_ctl_mode = manual;
> +	args.fan_ctl_idx = idx;
> +
> +	params.length = sizeof(args);
> +	params.pointer = (void *)&args;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_SET_FAN_IDX, &params);
> +	if (!info) {
> +		err = -EIO;
> +		goto out;
> +	}
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 504e1ae79706..1e9e2e498d15 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -18,6 +18,7 @@
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
> +#define APMF_FUNC_SET_FAN_IDX				7
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>  
>  /* Message Definitions */
> @@ -39,12 +40,16 @@
>  #define GET_STT_LIMIT_APU	0x20
>  #define GET_STT_LIMIT_HS2	0x21
>  
> +/* Fan Index for Auto Mode */
> +#define FAN_INDEX_AUTO 0xFFFFFFFF
> +
>  #define ARG_NONE 0
>  
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
>  	bool sbios_heartbeat;
> +	bool fan_table_idx;
>  	bool static_slider_granular;
>  };
>  
> @@ -73,6 +78,12 @@ struct apmf_system_params {
>  	u32 heartbeat_int;
>  } __packed;
>  
> +struct apmf_fan_idx {
> +	u16 size;
> +	u8 fan_ctl_mode;
> +	u32 fan_ctl_idx;
> +} __packed;
> +
>  enum amd_stt_skin_temp {
>  	STT_TEMP_APU,
>  	STT_TEMP_HS2,
> @@ -137,6 +148,11 @@ struct amd_pmf_static_slider_granular {
>  	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
>  };
>  
> +struct fan_table_control {
> +	bool manual;
> +	unsigned long fan_id;
> +};
> +
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
> @@ -154,4 +170,5 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>  				    struct apmf_static_slider_granular_output *output);
>  void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>  
> +int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>  #endif /* PMF_H */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature
  2022-07-27 19:29   ` Hans de Goede
@ 2022-07-27 20:26     ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:26 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/27/22 21:29, Hans de Goede wrote:
> Hi,
> 
> On 7/12/22 16:58, Shyam Sundar S K wrote:
>> SPS (a.k.a. Static Power Slider) gives a feel of Windows performance
>> power slider for the Linux users, where the user selects a certain
>> mode (like "balanced", "low-power" or "performance") and the thermals
>> associated with each selected mode gets applied from the silicon
>> side via the mailboxes defined through PMFW.
>>
>> PMF driver hooks to platform_profile by reading the PMF ACPI fn9 to
>> see if the support is being advertised by ACPI interface.
>>
>> If supported, the PMF driver reacts to platform_profile selection choices
>> made by the user and adjust the system thermal behavior.
>>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>  drivers/platform/x86/amd/pmf/Makefile |   2 +-
>>  drivers/platform/x86/amd/pmf/acpi.c   |  29 +++++
>>  drivers/platform/x86/amd/pmf/core.c   |  26 ++++
>>  drivers/platform/x86/amd/pmf/pmf.h    |  65 ++++++++++
>>  drivers/platform/x86/amd/pmf/sps.c    | 166 ++++++++++++++++++++++++++
>>  5 files changed, 287 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/platform/x86/amd/pmf/sps.c
>>
>> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
>> index 2617eba773ce..557521a80427 100644
>> --- a/drivers/platform/x86/amd/pmf/Makefile
>> +++ b/drivers/platform/x86/amd/pmf/Makefile
>> @@ -5,4 +5,4 @@
>>  #
>>  
>>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
>> -amd-pmf-objs := core.o acpi.o
>> +amd-pmf-objs := core.o acpi.o sps.o
>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>> index addcaae5675c..fd5ab7a116f0 100644
>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>> @@ -54,6 +54,7 @@ static union acpi_object *apmf_if_call(struct apmf_if *apmf_if, int func, struct
>>  static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>  {
>>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>> +	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>  }
>>  
>>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
>> @@ -130,6 +131,34 @@ static int apmf_get_system_params(struct apmf_if *ampf_if)
>>  	return err;
>>  }
>>  
>> +int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>> +				    struct apmf_static_slider_granular_output *data)
>> +{
>> +	union acpi_object *info;
>> +	size_t size;
>> +	int err = 0;
>> +
>> +	info = apmf_if_call(ampf_if, APMF_FUNC_STATIC_SLIDER_GRANULAR, NULL);
>> +	if (!info)
>> +		return -EIO;
>> +
>> +	size = *(u16 *)info->buffer.pointer;
>> +
>> +	if (size < sizeof(*data)) {
>> +		pr_err("PMF: buffer too small %zu\n", size);
>> +		err = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	size = min(sizeof(*data), size);
>> +	memset(data, 0, sizeof(*data));
>> +	memcpy(data, info->buffer.pointer, size);
> 
> Same remarks wrt size handling as in patch 3/15. As mentioned you really need
> a helper for this. Something with a prototype like this:
> 
> int apmf_if_call_store_buffer(struct apmf_if *ampf_if, int func, void *dest, size_t dest_size);
> 
> And then this entire function becomes just:
> 
> int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
> 				    struct apmf_static_slider_granular_output *data)
> 	return apmf_if_call_store_buffer(ampf_if, APMF_FUNC_STATIC_SLIDER_GRANULAR, data, sizeof(*data));
> }
> 
> 
>> +
>> +out:
>> +	kfree(info);
>> +	return err;
>> +}
>> +
>>  static acpi_handle apmf_if_probe_handle(void)
>>  {
>>  	acpi_handle handle = NULL;
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index c5002b7bb904..b6006e8ee1a1 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/module.h>
>>  #include <linux/pci.h>
>>  #include <linux/platform_device.h>
>> +#include <linux/power_supply.h>
>>  #include "pmf.h"
>>  
>>  /* PMF-SMU communication registers */
>> @@ -45,6 +46,14 @@
>>  #define DELAY_MIN_US	2000
>>  #define DELAY_MAX_US	3000
>>  
>> +int amd_pmf_get_power_source(void)
>> +{
>> +	if (power_supply_is_system_supplied() > 0)
>> +		return POWER_SOURCE_AC;
>> +	else
>> +		return POWER_SOURCE_DC;
>> +}
>> +>  static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
>>  {
>>  	return ioread32(dev->regbase + reg_offset);
>> @@ -138,6 +147,21 @@ static const struct pci_device_id pmf_pci_ids[] = {
>>  	{ }
>>  };
>>  
>> +static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>> +{
>> +	/* Enable Static Slider */
>> +	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
> 
> Just put a:
> 
> 	if (!is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
> 		return;
> 
> check at the top of amd_pmf_init_sps(dev); and then you can drop this
> entire function.

I see that in later patches you add more feature checks here. So it is
ok to keep this code as is. Please ignore my original
remark about changing this.

> 
>> +		amd_pmf_init_sps(dev);
>> +		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>> +	}
>> +}
>> +
>> +static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>> +{
>> +	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>> +		amd_pmf_deinit_sps(dev);
> 
> Same here.

And this can be kept as is too.  Please ignore my original remark about this too.

Regards,

Hans



> 
>> +}
>> +
>>  static const struct acpi_device_id amd_pmf_acpi_ids[] = {
>>  	{"AMDI0102", 0},
>>  	{ }
>> @@ -206,6 +230,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
>>  
>>  	apmf_acpi_init(dev);
>>  	platform_set_drvdata(pdev, dev);
>> +	amd_pmf_init_features(dev);
>>  
>>  	mutex_init(&dev->lock);
>>  	dev_info(dev->dev, "registered PMF device successfully\n");
>> @@ -218,6 +243,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>>  	struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
>>  
>>  	mutex_destroy(&dev->lock);
>> +	amd_pmf_deinit_features(dev);
>>  	kfree(dev->buf);
>>  	return 0;
>>  }
>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>> index 40f038eb6197..a405987ae653 100644
>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>> @@ -7,14 +7,17 @@
>>   *
>>   * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>   */
>> +
>>  #ifndef PMF_H
>>  #define PMF_H
>>  
>>  #include <linux/acpi.h>
>> +#include <linux/platform_profile.h>
>>  
>>  /* APMF Functions */
>>  #define APMF_FUNC_VERIFY_INTERFACE			0
>>  #define APMF_FUNC_GET_SYS_PARAMS			1
>> +#define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>>  
>>  /* Message Definitions */
>>  #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
>> @@ -35,9 +38,12 @@
>>  #define GET_STT_LIMIT_APU	0x20
>>  #define GET_STT_LIMIT_HS2	0x21
>>  
>> +#define ARG_NONE 0
>> +
>>  /* AMD PMF BIOS interfaces */
>>  struct apmf_if_functions {
>>  	bool system_params;
>> +	bool static_slider_granular;
>>  };
>>  
>>  struct apmf_if {
>> @@ -59,6 +65,30 @@ struct apmf_system_params {
>>  	u8 command_code;
>>  } __packed;
>>  
>> +enum amd_stt_skin_temp {
>> +	STT_TEMP_APU,
>> +	STT_TEMP_HS2,
>> +	STT_TEMP_COUNT,
>> +};
>> +
>> +enum amd_slider_op {
>> +	SLIDER_OP_GET,
>> +	SLIDER_OP_SET,
>> +};
>> +
>> +enum power_source {
>> +	POWER_SOURCE_AC,
>> +	POWER_SOURCE_DC,
>> +	POWER_SOURCE_MAX,
>> +};
>> +
>> +enum power_modes {
>> +	POWER_MODE_PERFORMANCE,
>> +	POWER_MODE_BALANCED_POWER,
>> +	POWER_MODE_POWER_SAVER,
>> +	POWER_MODE_MAX,
>> +};
>> +
>>  struct amd_pmf_dev {
>>  	void __iomem *regbase;
>>  	void __iomem *smu_virt_addr;
>> @@ -69,11 +99,46 @@ struct amd_pmf_dev {
>>  	u32 low;
>>  	struct device *dev;
>>  	struct apmf_if *apmf_if;
>> +	enum platform_profile_option current_profile;
>> +	struct platform_profile_handler pprof;
>>  	struct mutex lock; /* protects the PMF interface */
>>  };
>>  
>> +struct apmf_sps_prop_granular {
>> +	u32 fppt;
>> +	u32 sppt;
>> +	u32 sppt_apu_only;
>> +	u32 spl;
>> +	u32 stt_min;
>> +	u8 stt_skin_temp[STT_TEMP_COUNT];
>> +	u32 fan_id;
>> +} __packed;
>> +
>> +/* Static Slider */
>> +struct apmf_static_slider_granular_output {
>> +	u16 size;
>> +	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX * POWER_MODE_MAX];
>> +};
> 
> missing __packed here I presume, since this is the data returned
> by an ACPI call ?
> 
>> +
>> +struct amd_pmf_static_slider_granular {
>> +	u16 size;
>> +	struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
>> +};
>> +
>>  /* Core Layer */
>>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>> +int is_apmf_func_supported(unsigned long index);
>>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>> +int amd_pmf_get_power_source(void);
>> +
>> +/* SPS Layer */
>> +u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
>> +void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
>> +			   struct amd_pmf_static_slider_granular *table);
>> +int amd_pmf_init_sps(struct amd_pmf_dev *dev);
>> +void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
>> +int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>> +				    struct apmf_static_slider_granular_output *output);
>> +void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>>  
>>  #endif /* PMF_H */
>> diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
>> new file mode 100644
>> index 000000000000..25289cf5608c
>> --- /dev/null
>> +++ b/drivers/platform/x86/amd/pmf/sps.c
>> @@ -0,0 +1,166 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * AMD Platform Management Framework (PMF) Driver
>> + *
>> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
>> + * All Rights Reserved.
>> + *
>> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> + */
>> +
>> +#include "pmf.h"
>> +
>> +static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
>> +			       enum platform_profile_option *profile);
>> +static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
>> +			       enum platform_profile_option profile);
>> +static struct amd_pmf_static_slider_granular config_store;
>> +
>> +void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
>> +{
>> +	struct apmf_static_slider_granular_output output;
>> +	int i, j, idx = 0;
>> +
>> +	memset(&config_store, 0, sizeof(config_store));
>> +
>> +	if (dev->apmf_if->func.static_slider_granular) {
>> +		apmf_get_static_slider_granular(dev->apmf_if, &output);
>> +
>> +		for (i = 0; i < POWER_SOURCE_MAX; i++) {
>> +			for (j = 0; j < POWER_MODE_MAX; j++) {
>> +				config_store.prop[i][j].spl = output.prop[idx].spl;
>> +				config_store.prop[i][j].sppt = output.prop[idx].sppt;
>> +				config_store.prop[i][j].sppt_apu_only =
>> +							output.prop[idx].sppt_apu_only;
>> +				config_store.prop[i][j].fppt = output.prop[idx].fppt;
>> +				config_store.prop[i][j].stt_min = output.prop[idx].stt_min;
>> +				config_store.prop[i][j].stt_skin_temp[STT_TEMP_APU] =
>> +						output.prop[idx].stt_skin_temp[STT_TEMP_APU];
>> +				config_store.prop[i][j].stt_skin_temp[STT_TEMP_HS2] =
>> +						output.prop[idx].stt_skin_temp[STT_TEMP_HS2];
>> +				config_store.prop[i][j].fan_id = output.prop[idx].fan_id;
>> +				idx++;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
>> +			   struct amd_pmf_static_slider_granular *table)
>> +{
>> +	int src = amd_pmf_get_power_source();
>> +
>> +	if (op == SLIDER_OP_SET) {
>> +		amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
>> +		amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
>> +		amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
>> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
>> +				 config_store.prop[src][idx].sppt_apu_only, NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
>> +				 config_store.prop[src][idx].stt_min, NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
>> +	} else if (op == SLIDER_OP_GET) {
>> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
>> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
>> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt);
>> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
>> +				 &table->prop[src][idx].sppt_apu_only);
>> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
>> +				 &table->prop[src][idx].stt_min);
>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
>> +				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]);
>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
>> +				 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]);
>> +	}
>> +}
>> +
>> +static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
>> +			       enum platform_profile_option *profile)
>> +{
>> +	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
>> +
>> +	*profile = pmf->current_profile;
>> +	return 0;
>> +}
>> +
>> +u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
>> +{
>> +	u8 mode;
>> +
>> +	switch (pmf->current_profile) {
>> +	case PLATFORM_PROFILE_PERFORMANCE:
>> +		mode = 0;
> 
> Please use POWER_MODE_PERFORMANCE instead of 1 here.
> 
>> +		break;
>> +	case PLATFORM_PROFILE_BALANCED:
>> +		mode = 1;
> 
> POWER_MODE_BALANCED_POWER instead of 1.
> 
>> +		break;
>> +	case PLATFORM_PROFILE_LOW_POWER:
>> +		mode = 2;
> 
> POWER_MODE_POWER_SAVER instead of 2.
> 
>> +		break;
>> +	default:
>> +		dev_err(pmf->dev, "Unknown Platform Profile.\n");
>> +		break;
>> +	}
>> +
>> +	return mode;
>> +}
>> +
>> +int amd_pmf_profile_set(struct platform_profile_handler *pprof,
>> +			enum platform_profile_option profile)
>> +{
>> +	struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
>> +	u8 mode;
>> +
>> +	pmf->current_profile = profile;
>> +	mode = amd_pmf_get_pprof_modes(pmf);
>> +	amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
>> +	return 0;
>> +}
>> +
>> +static void amd_pmf_profile_refresh(struct amd_pmf_dev *dev)
>> +{
>> +	enum platform_profile_option profile;
>> +
>> +	/* Set intially to Balanced Mode */
>> +	profile = PLATFORM_PROFILE_BALANCED;
>> +
>> +	if (profile != dev->current_profile) {
>> +		dev->current_profile = profile;
>> +		platform_profile_notify();
>> +	}
>> +}
>> +
>> +int amd_pmf_init_sps(struct amd_pmf_dev *dev)
>> +{
>> +	int err = 0;
>> +
>> +	dev->pprof.profile_get = amd_pmf_profile_get;
>> +	dev->pprof.profile_set = amd_pmf_profile_set;
>> +
>> +	/* Setup supported modes */
>> +	set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
>> +	set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
>> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
>> +
>> +	/* Create platform_profile structure and register */
>> +	err = platform_profile_register(&dev->pprof);
>> +	if (err) {
>> +		dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
>> +			err);
>> +		return -EEXIST;
>> +	}
>> +	/* Ensure initial values are correct */
>> +	amd_pmf_profile_refresh(dev);
> 
> Since dev->current_profile == 0 and thus  dev->current_profile != PLATFORM_PROFILE_BALANCED
> this ends up doing 2 things:
> 
> 1.	dev->current_profile = PLATFORM_PROFILE_BALANCED;
> 2.	platform_profile_notify()
> 
> Where 2 is not useful since the /sys/firmware/acpi/power_profile
> attribute does not exist before the platform_profile_register() call
> we have just done, so nothing is listening on it.
> 
> So this entire function call can be replaced with just:
> 
> 	dev->current_profile = PLATFORM_PROFILE_BALANCED;
> 
> and the badly named refresh (it is not a refresh since it does
> not fetch values from the hw) function can be dropped.
> 
>> +
>> +	amd_pmf_load_defaults_sps(dev);
>> +
>> +	return err;
>> +}
>> +
>> +void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
>> +{
>> +	platform_profile_remove();
>> +}
> 
> Regards,
> 
> Hans
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW
  2022-07-12 14:58 ` [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW Shyam Sundar S K
@ 2022-07-27 20:36   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:36 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> PMF driver polls for metrics information from PMFW to understand the system
> behavior, power consumption etc.
> 
> This metrics table information will be used the PMF features to tweak the
> thermal heuristics. The poll duration can also be changed by the user
> by changing the poll duration time.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/core.c | 57 +++++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h  | 39 ++++++++++++++++++++
>  2 files changed, 96 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index ff26928e6a49..c6fd52c46818 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -47,6 +47,11 @@
>  #define DELAY_MIN_US	2000
>  #define DELAY_MAX_US	3000
>  
> +/* override Metrics Table sample size time (in ms) */
> +static int metrics_table_loop_ms = 1000;
> +module_param(metrics_table_loop_ms, int, 0644);
> +MODULE_PARM_DESC(metrics_table_loop_ms, " Metrics Table sample size time (default = 1000ms) ");
> +
>  #ifdef CONFIG_DEBUG_FS
>  static int current_power_limits_show(struct seq_file *seq, void *unused)
>  {
> @@ -98,6 +103,30 @@ int amd_pmf_get_power_source(void)
>  		return POWER_SOURCE_DC;
>  }
>  
> +static void amd_pmf_get_metrics(struct work_struct *work)
> +{
> +	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
> +	enum platform_profile_option current_profile;
> +	ktime_t time_elapsed_ms;
> +	int socket_power;
> +
> +	/* Get the current profile information */
> +	platform_profile_get(&current_profile);

This can simply be written as:

	current_profile = READ_ONCE(dev->current_profile);

Avoiding the need to add the platform_profile_get() helper and allowing
the dropping of patch 1/15.

> +
> +	/* Transfer table contents */
> +	memset(&dev->m_table, 0, sizeof(dev->m_table));

memset dev->buf I presume, not dev->m_table ?

> +	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
> +	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
> +
> +	time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
> +	/* Calculate the avg SoC power consumption */
> +	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
> +
> +	dev->start_time = ktime_to_ms(ktime_get());
> +	dev_dbg(dev->dev, "Metrics table sample size time:%d\n", metrics_table_loop_ms);
> +	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
> +}
> +
>  static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
>  {
>  	return ioread32(dev->regbase + reg_offset);
> @@ -191,6 +220,34 @@ static const struct pci_device_id pmf_pci_ids[] = {
>  	{ }
>  };
>  
> +int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
> +{
> +	struct smu_pmf_metrics *m_table;

Using a local variable just to use sizeof on it is weird,
please drop this.

> +	u64 phys_addr;
> +
> +	INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
> +
> +	/*
> +	 * Start collecting the metrics data after a small delay
> +	 * or else, we might end up getting stale values from PMFW.
> +	 */
> +	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));

This work relies on dev->buf != NULL and on the DRAM address being
set, so this schedule_delayed_work() must be moved to lower in this function.

> +
> +	/* Get Metrics Table Address */
> +	dev->buf = kzalloc(sizeof(*m_table), GFP_KERNEL);

Use sizeof(dev->m_table) here, this is also what is used in
amd_pmf_get_metrics().

> +	if (!dev->buf)

Moving it lower also fixes it still running when existing with an error here.

> +		return -ENOMEM;
> +
> +	phys_addr = virt_to_phys(dev->buf);
> +	dev->hi = phys_addr >> 32;
> +	dev->low = phys_addr & GENMASK(31, 0);
> +
> +	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, dev->hi, NULL);
> +	amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, dev->low, NULL);

Are dev->hi and dev>low only ever used here? It seems so, in that case
please use local function variables for them instead of storing them
in the amd_pmf_dev struct.

> +

This is where the schedule_work() should be done.

> +	return 0;
> +}
> +
>  static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>  {
>  	/* Enable Static Slider */
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 1e9e2e498d15..8f318ff59c2e 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -84,6 +84,41 @@ struct apmf_fan_idx {
>  	u32 fan_ctl_idx;
>  } __packed;
>  
> +struct smu_pmf_metrics {
> +	u16 gfxclk_freq; /* in MHz */
> +	u16 socclk_freq; /* in MHz */
> +	u16 vclk_freq; /* in MHz */
> +	u16 dclk_freq; /* in MHz */
> +	u16 memclk_freq; /* in MHz */
> +	u16 spare;
> +	u16 gfx_activity; /* in Centi */
> +	u16 uvd_activity; /* in Centi */
> +	u16 voltage[2]; /* in mV */
> +	u16 currents[2]; /* in mA */
> +	u16 power[2];/* in mW */
> +	u16 core_freq[8]; /* in MHz */
> +	u16 core_power[8]; /* in mW */
> +	u16 core_temp[8]; /* in centi-Celsius */
> +	u16 l3_freq; /* in MHz */
> +	u16 l3_temp; /* in centi-Celsius */
> +	u16 gfx_temp; /* in centi-Celsius */
> +	u16 soc_temp; /* in centi-Celsius */
> +	u16 throttler_status;
> +	u16 current_socketpower; /* in mW */
> +	u16 stapm_orig_limit; /* in W */
> +	u16 stapm_cur_limit; /* in W */
> +	u32 apu_power; /* in mW */
> +	u32 dgpu_power; /* in mW */
> +	u16 vdd_tdc_val; /* in mA */
> +	u16 soc_tdc_val; /* in mA */
> +	u16 vdd_edc_val; /* in mA */
> +	u16 soc_edcv_al; /* in mA */
> +	u16 infra_cpu_maxfreq; /* in MHz */
> +	u16 infra_gfx_maxfreq; /* in MHz */
> +	u16 skin_temp; /* in centi-Celsius */
> +	u16 device_state;
> +};
> +
>  enum amd_stt_skin_temp {
>  	STT_TEMP_APU,
>  	STT_TEMP_HS2,
> @@ -120,8 +155,11 @@ struct amd_pmf_dev {
>  	struct apmf_if *apmf_if;
>  	enum platform_profile_option current_profile;
>  	struct platform_profile_handler pprof;
> +	struct smu_pmf_metrics m_table;
> +	struct delayed_work work_buffer;
>  	struct delayed_work heart_beat;
>  	struct mutex lock; /* protects the PMF interface */
> +	ktime_t start_time;
>  #if IS_ENABLED(CONFIG_DEBUG_FS)
>  	struct dentry *dbgfs_dir;
>  #endif /* CONFIG_DEBUG_FS */
> @@ -158,6 +196,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
>  int is_apmf_func_supported(unsigned long index);
>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
> +int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev);
>  int amd_pmf_get_power_source(void);
>  
>  /* SPS Layer */


Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains
  2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
  2022-07-12 15:03   ` Limonciello, Mario
@ 2022-07-27 20:38   ` Hans de Goede
  1 sibling, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:38 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mario Limonciello

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> Allow other drivers to react to determine current active profile
> and react to platform profile changes.
> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

This new helper is not necessary, as mentioned in my review of 8/15,
its single caller, the code there can just use:

	current_profile = READ_ONCE(dev->current_profile);

Please drop this patch for the next version of the series.

Regards,

Hans


> ---
>  drivers/acpi/platform_profile.c  | 26 ++++++++++++++++++++++++++
>  include/linux/platform_profile.h |  1 +
>  2 files changed, 27 insertions(+)
> 
> diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> index d418462ab791..7e12a1f30f06 100644
> --- a/drivers/acpi/platform_profile.c
> +++ b/drivers/acpi/platform_profile.c
> @@ -49,6 +49,32 @@ static ssize_t platform_profile_choices_show(struct device *dev,
>  	return len;
>  }
>  
> +int platform_profile_get(enum platform_profile_option *profile)
> +{
> +	int err;
> +
> +	err = mutex_lock_interruptible(&profile_lock);
> +	if (err)
> +		return err;
> +
> +	if (!cur_profile) {
> +		mutex_unlock(&profile_lock);
> +		return -ENODEV;
> +	}
> +
> +	err = cur_profile->profile_get(cur_profile, profile);
> +	mutex_unlock(&profile_lock);
> +	if (err)
> +		return err;
> +
> +	/* Check that profile is valid index */
> +	if (WARN_ON((*profile < 0) || (*profile >= ARRAY_SIZE(profile_names))))
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(platform_profile_get);
> +
>  static ssize_t platform_profile_show(struct device *dev,
>  					struct device_attribute *attr,
>  					char *buf)
> diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
> index e5cbb6841f3a..2395be670dfd 100644
> --- a/include/linux/platform_profile.h
> +++ b/include/linux/platform_profile.h
> @@ -37,5 +37,6 @@ struct platform_profile_handler {
>  int platform_profile_register(struct platform_profile_handler *pprof);
>  int platform_profile_remove(void);
>  void platform_profile_notify(void);
> +int platform_profile_get(enum platform_profile_option *profile);
>  
>  #endif  /*_PLATFORM_PROFILE_H_*/


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF
  2022-07-12 14:58 ` [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF Shyam Sundar S K
@ 2022-07-27 20:51   ` Hans de Goede
  2022-07-27 21:00   ` Hans de Goede
  1 sibling, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:51 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> CnQF (a.k.a Cool and Quiet Framework) extends the static slider concept.
> PMF dynamically manages system power limits and fan policy based on system
> power trends which is representative of workload trend.
> 
> Static slider and CnQF controls are mutually exclusive for system power
> budget adjustments. CnQF supports configurable number of modes which can
> be unique for AC and DC. Every mode is representative of a system state
> characterized by unique steady state and boost behavior.
> 
> OEMs can configure the different modes/system states and how the
> transition to a mode happens. Whether to have CnQF manage system power
> budget dynamically in AC or DC or both is also configurable. Mode changes
> due to CnQF don’t result in slider position change.
> 
> The default OEM values are obtained after evaluating the PMF ACPI function
> idx 11 & 12 for AC and DC respectively. Whether to turn ON/OFF by default
> is guided by a "flag" passed by the OEM BIOS.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/Makefile |   3 +-
>  drivers/platform/x86/amd/pmf/acpi.c   |  56 +++++
>  drivers/platform/x86/amd/pmf/cnqf.c   | 327 ++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/core.c   |  14 ++
>  drivers/platform/x86/amd/pmf/pmf.h    | 115 +++++++++
>  5 files changed, 514 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/x86/amd/pmf/cnqf.c
> 
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index 557521a80427..d02a0bdc6429 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -5,4 +5,5 @@
>  #
>  
>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> -amd-pmf-objs := core.o acpi.o sps.o
> +amd-pmf-objs := core.o acpi.o sps.o \
> +		cnqf.o
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index e4822c6f4259..a3ff91c605b5 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -56,6 +56,8 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
> +	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
> +	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
>  }
>  
>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
> @@ -208,6 +210,60 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>  	return err;
>  }
>  
> +int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_AC, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_debug("buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}

Please use the suggested:

int apmf_if_call_store_buffer(struct apmf_if *ampf_if, int func, void *dest, size_t dest_size);

here, see my review of patch 4/15.


> +
> +int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_DC, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_debug("buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +

And for this you can use apmf_if_call_store_buffer() too.


>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> new file mode 100644
> index 000000000000..8c6756faab25
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include <linux/workqueue.h>
> +#include "pmf.h"
> +
> +static struct cnqf_config config_store;
> +
> +static int amd_pmf_handle_cnqf(struct amd_pmf_dev *dev, bool op, int src, int idx,
> +			       struct cnqf_config *table)
> +{
> +	struct power_table_control *pc;
> +
> +	if (op == SLIDER_OP_SET) {
> +		pc = &config_store.mode_set[src][idx].power_control;
> +
> +		amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL);
> +		amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
> +				 NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
> +				 NULL);
> +	} else if (op == SLIDER_OP_GET) {
> +		pc = &table->mode_set[src][idx].power_control;
> +
> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &pc->spl);
> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &pc->fppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &pc->sppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE, &pc->sppt_apu_only);
> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE, &pc->stt_min);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
> +				 &pc->stt_skin_temp[STT_TEMP_APU]);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
> +				 &pc->stt_skin_temp[STT_TEMP_HS2]);
> +	}
> +
> +	if (dev->apmf_if->func.fan_table_idx)
> +		apmf_update_fan_idx(dev->apmf_if,
> +				    config_store.mode_set[src][idx].fan_control.manual,
> +				    config_store.mode_set[src][idx].fan_control.fan_id);

This code sets the fan values, should this not be inside the
"if (op == SLIDER_OP_SET)" block ?

> +
> +	return 0;
> +}
> +
> +static void amd_pmf_update_power_threshold(void)
> +{
> +	struct cnqf_mode_settings *ts;
> +	struct cnqf_tran_params *tp;
> +	int i;
> +
> +	for (i = 0; i < POWER_SOURCE_MAX; i++) {
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_QUIET];
> +		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_TURBO];
> +		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_QUIET];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_TURBO];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +	}
> +}
> +
> +static const char *state_as_str(unsigned int state)
> +{
> +	switch (state) {
> +	case CNQF_MODE_QUIET:
> +		return "QUIET";
> +	case CNQF_MODE_BALANCE:
> +		return "BALANCED";
> +	case CNQF_MODE_TURBO:
> +		return "TURBO";
> +	case CNQF_MODE_PERFORMANCE:
> +		return "PERFORMANCE";
> +	default:
> +		return "Unknown CnQF mode";
> +	}
> +}
> +
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
> +{
> +	struct cnqf_tran_params *tp;
> +	int src, i, j, index = 0;
> +	u32 avg_power = 0;
> +
> +	src = amd_pmf_get_power_source();
> +
> +	if (!dev->cnqf_running) {
> +		pr_debug("setting CNQF thermals\n");
> +		amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +		dev->cnqf_running = true;
> +		return;
> +	}
> +
> +	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
> +		config_store.trans_param[src][i].timer += time_lapsed_ms;
> +		config_store.trans_param[src][i].total_power += socket_power;
> +		config_store.trans_param[src][i].count++;
> +
> +		tp = &config_store.trans_param[src][i];
> +		if (tp->timer >= tp->time_constant && tp->count) {
> +			avg_power = tp->total_power / tp->count;
> +
> +			/* Reset the indices */
> +			tp->timer = 0;
> +			tp->total_power = 0;
> +			tp->count = 0;
> +
> +			if ((tp->shifting_up && avg_power >= tp->power_threshold) ||
> +			    (!tp->shifting_up && avg_power <= tp->power_threshold)) {
> +				tp->priority = true;
> +			} else {
> +				tp->priority = false;
> +			}
> +		}
> +	}
> +
> +	dev_dbg(dev->dev, "[CNQF] Avg power: %u mW socket power: %u mW mode:%s\n",
> +		avg_power, socket_power, state_as_str(config_store.current_mode));
> +
> +	for (j = 0; j < CNQF_TRANSITION_MAX; j++) {
> +		/* apply the highest priority */
> +		index = config_store.trans_prio[j];
> +		if (config_store.trans_param[src][index].priority) {
> +			if (config_store.current_mode !=
> +			    config_store.trans_param[src][index].target_mode) {
> +				config_store.current_mode =
> +						config_store.trans_param[src][index].target_mode;
> +				dev_dbg(dev->dev, "Moving to Mode :%s\n",
> +					state_as_str(config_store.current_mode));
> +				amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src,
> +						    config_store.current_mode, NULL);
> +			}
> +			break;
> +		}
> +	}
> +}
> +
> +static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output out)
> +{
> +	struct cnqf_tran_params *tp;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_QUIET];
> +	tp->time_constant = out.t_balanced_to_quiet;
> +	tp->target_mode = CNQF_MODE_QUIET;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
> +	tp->time_constant = out.t_balanced_to_perf;
> +	tp->target_mode = CNQF_MODE_PERFORMANCE;
> +	tp->shifting_up = true;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
> +	tp->time_constant = out.t_quiet_to_balanced;
> +	tp->target_mode = CNQF_MODE_BALANCE;
> +	tp->shifting_up = true;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
> +	tp->time_constant = out.t_perf_to_balanced;
> +	tp->target_mode = CNQF_MODE_BALANCE;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
> +	tp->time_constant = out.t_turbo_to_perf;
> +	tp->target_mode = CNQF_MODE_PERFORMANCE;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_TURBO];
> +	tp->time_constant = out.t_perf_to_turbo;
> +	tp->target_mode = CNQF_MODE_TURBO;
> +	tp->shifting_up = true;
> +}
> +
> +static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output out)
> +{
> +	struct cnqf_mode_settings *ms;
> +
> +	/* Quiet Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_QUIET];
> +	ms->power_floor = out.ps[APMF_CNQF_QUIET].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_QUIET].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_QUIET].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_QUIET].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_QUIET].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_QUIET].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_QUIET].fan_id;
> +
> +	/* Balance Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_BALANCE];
> +	ms->power_floor = out.ps[APMF_CNQF_BALANCE].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_BALANCE].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_BALANCE].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_BALANCE].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_BALANCE].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_BALANCE].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_BALANCE].fan_id;
> +
> +	/* Performance Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_PERFORMANCE];
> +	ms->power_floor = out.ps[APMF_CNQF_PERFORMANCE].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_PERFORMANCE].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_PERFORMANCE].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_PERFORMANCE].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_PERFORMANCE].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_PERFORMANCE].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_PERFORMANCE].fan_id;
> +
> +	/* Turbo Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_TURBO];
> +	ms->power_floor = out.ps[APMF_CNQF_TURBO].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_TURBO].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_TURBO].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_TURBO].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_TURBO].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_TURBO].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_TURBO].fan_id;
> +}
> +
> +static int amd_pmf_check_flags(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_dyn_slider_output out;

The apmf_get_dyn_slider_def_* calls can fail and then the
return value is uninitialized stack, please 0 this out:

	struct apmf_dyn_slider_output out = {};

> +
> +	if (dev->apmf_if->func.dyn_slider_ac)
> +		apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
> +	else if (dev->apmf_if->func.dyn_slider_dc)
> +		apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
> +
> +	return out.flags;
> +}
> +
> +void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_dyn_slider_output out;

The apmf_get_dyn_slider_def_* calls can fail and then the
uninitialized stack will get used for "out", please 0 this out:

	struct apmf_dyn_slider_output out = {};

> +	int i, j, ret;
> +
> +	for (i = 0; i < POWER_SOURCE_MAX; i++) {
> +		if (i == POWER_SOURCE_AC && dev->apmf_if->func.dyn_slider_ac) {
> +			ret = apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
> +			if (ret)
> +				dev_err(dev->dev,
> +					"APMF apmf_get_dyn_slider_def_ac failed :%d\n", ret);
> +		} else if (i == POWER_SOURCE_DC && dev->apmf_if->func.dyn_slider_dc) {
> +			ret = apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
> +			if (ret)
> +				dev_err(dev->dev,
> +					"APMF apmf_get_dyn_slider_def_dc failed :%d\n", ret);
> +		}

What if either the call fails ; or dyn_slider_dc or dyn_slider_ac is not set, then
output will contain all zeros, do we want to continue with that ?

Maybe CqNF support should be disabled if this happens?

> +
> +		amd_pmf_update_mode_set(i, out);
> +		amd_pmf_update_trans_data(i, out);
> +
> +		for (j = 0; j < CNQF_MODE_MAX; j++) {
> +			if (config_store.mode_set[i][j].fan_control.fan_id == FAN_INDEX_AUTO)
> +				config_store.mode_set[i][j].fan_control.manual = false;
> +			else
> +				config_store.mode_set[i][j].fan_control.manual = true;
> +		}
> +	}
> +	amd_pmf_update_power_threshold();
> +
> +	for (i = 0; i < CNQF_TRANSITION_MAX; i++)
> +		config_store.trans_prio[i] = i;
> +}
> +
> +void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
> +{
> +	cancel_delayed_work_sync(&dev->work_buffer);
> +}
> +
> +void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
> +{
> +	int ret, src;
> +
> +	ret = amd_pmf_check_flags(dev);
> +	if (!ret) {
> +		dev_dbg(dev->dev, "CnQF bios default_enable flag not set\n");
> +		return;
> +	}
> +
> +	dev->cnqf_feat = true;
> +	amd_pmf_load_defaults_cnqf(dev);
> +	amd_pmf_init_metrics_table(dev);
> +
> +	/* update the thermal for CnQF */
> +	src = amd_pmf_get_power_source();
> +	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +}
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index c6fd52c46818..bc267d333b76 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	/* Calculate the avg SoC power consumption */
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
> +	if (dev->cnqf_feat) {
> +		/* Apply the CnQF transition */
> +		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
> +	}
> +
>  	dev->start_time = ktime_to_ms(ktime_get());
>  	dev_dbg(dev->dev, "Metrics table sample size time:%d\n", metrics_table_loop_ms);
>  	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
> @@ -255,12 +260,21 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>  		amd_pmf_init_sps(dev);
>  		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>  	}
> +	/* Enable Cool n Quiet Framework (CnQF) */
> +	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
> +	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
> +		amd_pmf_init_cnqf(dev);
> +		dev_dbg(dev->dev, "CnQF Init done\n");
> +	}
>  }
>  
>  static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>  {
>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>  		amd_pmf_deinit_sps(dev);
> +	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
> +	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
> +		amd_pmf_deinit_cnqf(dev);
>  }
>  
>  static const struct acpi_device_id amd_pmf_acpi_ids[] = {
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 8f318ff59c2e..452266809dfa 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -20,6 +20,8 @@
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_SET_FAN_IDX				7
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
> +#define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
> +#define APMF_FUNC_DYN_SLIDER_GRANULAR_DC	12
>  
>  /* Message Definitions */
>  #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
> @@ -51,6 +53,8 @@ struct apmf_if_functions {
>  	bool sbios_heartbeat;
>  	bool fan_table_idx;
>  	bool static_slider_granular;
> +	bool dyn_slider_ac;
> +	bool dyn_slider_dc;
>  };
>  
>  struct apmf_notification_cfg {
> @@ -163,6 +167,8 @@ struct amd_pmf_dev {
>  #if IS_ENABLED(CONFIG_DEBUG_FS)
>  	struct dentry *dbgfs_dir;
>  #endif /* CONFIG_DEBUG_FS */
> +	bool cnqf_feat;
> +	bool cnqf_running;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -191,6 +197,106 @@ struct fan_table_control {
>  	unsigned long fan_id;
>  };
>  
> +struct power_table_control {
> +	u32 spl;
> +	u32 sppt;
> +	u32 fppt;
> +	u32 sppt_apu_only;
> +	u32 stt_min;
> +	u32 stt_skin_temp[STT_TEMP_COUNT];
> +	u32 reserved[16];
> +};
> +
> +/* CnQF Layer */
> +enum cnqf_trans_priority {
> +	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
> +	CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE, /* quiet/balance to Performance Mode */
> +	CNQF_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
> +	CNQF_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
> +	CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance/Turbo to Balance Mode */
> +	CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE, /* Turbo mode to Performance Mode */
> +	CNQF_TRANSITION_MAX,
> +};
> +
> +enum cnqf_mode {
> +	CNQF_MODE_QUIET,
> +	CNQF_MODE_BALANCE,
> +	CNQF_MODE_PERFORMANCE,
> +	CNQF_MODE_TURBO,
> +	CNQF_MODE_MAX,
> +};
> +
> +enum apmf_cnqf_pos {
> +	APMF_CNQF_TURBO,
> +	APMF_CNQF_PERFORMANCE,
> +	APMF_CNQF_BALANCE,
> +	APMF_CNQF_QUIET,
> +	APMF_CNQF_MAX,
> +};
> +
> +struct cnqf_mode_settings {
> +	struct power_table_control power_control;
> +	struct fan_table_control fan_control;
> +	u32 power_floor;
> +	bool enable;
> +};
> +
> +struct cnqf_tran_params {
> +	u32 time_constant; /* minimum time required to switch to next mode */
> +	u32 power_delta; /* minimum power required to switch to next mode */
> +	u32 power_threshold;
> +	u32 timer; /* elapsed time. if timer > timethreshold, it will move to next mode */
> +	u32 total_power;
> +	u32 count;
> +	bool enable;
> +	bool priority;
> +	bool shifting_up;
> +	enum cnqf_mode target_mode;
> +};
> +
> +struct cnqf_power_delta {
> +	u32 to_turbo;
> +	u32 balance_to_perf;
> +	u32 quiet_to_balance;
> +	u32 to_quiet;
> +	u32 perf_to_balance;
> +	u32 turbo_to_perf;
> +};
> +
> +struct cnqf_config {
> +	struct cnqf_tran_params trans_param[POWER_SOURCE_MAX][CNQF_TRANSITION_MAX];
> +	struct cnqf_mode_settings mode_set[POWER_SOURCE_MAX][CNQF_MODE_MAX];
> +	struct power_table_control defaults;
> +	enum cnqf_mode current_mode;
> +	struct cnqf_power_delta power_delta[POWER_SOURCE_MAX];
> +	u32 power_src;
> +	u32 avg_power;
> +	enum cnqf_trans_priority trans_prio[CNQF_TRANSITION_MAX];
> +};
> +
> +struct apmf_cnqf_power_set {
> +	u32 pfloor;
> +	u32 fppt;
> +	u32 sppt;
> +	u32 sppt_apu_only;
> +	u32 spl;
> +	u32 stt_min_limit;
> +	u8 stt_skintemp[STT_TEMP_COUNT];
> +	u32 fan_id;
> +} __packed;
> +
> +struct apmf_dyn_slider_output {
> +	u16 size;
> +	u16 flags;
> +	u32 t_perf_to_turbo;
> +	u32 t_balanced_to_perf;
> +	u32 t_quiet_to_balanced;
> +	u32 t_balanced_to_quiet;
> +	u32 t_perf_to_balanced;
> +	u32 t_turbo_to_perf;
> +	struct apmf_cnqf_power_set ps[APMF_CNQF_MAX];
> +};

You are missing an __packed here.

> +
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
> @@ -210,4 +316,13 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>  void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>  
>  int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
> +
> +/* CnQF Layer */
> +int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> +int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> +void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +
>  #endif /* PMF_H */

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF
  2022-07-12 14:58 ` [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF Shyam Sundar S K
@ 2022-07-27 20:52   ` Hans de Goede
  2022-07-27 21:12     ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:52 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> Whether to turn CnQF on/off by default upon driver load would be decided
> by a BIOS flag. Add a sysfs node to provide a way to the user whether to
> use static slider or CnQF .
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
>  drivers/platform/x86/amd/pmf/cnqf.c | 52 +++++++++++++++++++++++++++++
>  1 file changed, 52 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> index 8c6756faab25..2b03ae1ad37f 100644
> --- a/drivers/platform/x86/amd/pmf/cnqf.c
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -302,9 +302,59 @@ void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
>  		config_store.trans_prio[i] = i;
>  }
>  
> +static ssize_t feat_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
> +	int result, src;
> +	bool enabled;
> +	u8 mode;
> +
> +	result = kstrtobool(buf, &enabled);
> +	if (result)
> +		return result;
> +
> +	src = amd_pmf_get_power_source();
> +	pdev->cnqf_feat = enabled;
> +	if (pdev->cnqf_feat) {
> +		amd_pmf_handle_cnqf(pdev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +	} else {
> +		pdev->cnqf_running = false;
> +		mode = amd_pmf_get_pprof_modes(pdev);
> +		amd_pmf_update_slider(pdev, SLIDER_OP_SET, mode, NULL);
> +	}
> +
> +	dev_dbg(pdev->dev, "Received CnQF %s\n", enabled ? "on" : "off");
> +	return count;
> +}
> +
> +static ssize_t feat_show(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", pdev->cnqf_feat ? "on" : "off");
> +}
> +
> +static DEVICE_ATTR_RW(feat);
> +
> +static struct attribute *cnqf_feature_attrs[] = {
> +	&dev_attr_feat.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group cnqf_feature_attribute_group = {
> +	.attrs = cnqf_feature_attrs,
> +	.name = "cnqf"
> +};
> +
>  void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
>  {
>  	cancel_delayed_work_sync(&dev->work_buffer);
> +	sysfs_remove_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
> +	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
>  }
>  
>  void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
> @@ -324,4 +374,6 @@ void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
>  	/* update the thermal for CnQF */
>  	src = amd_pmf_get_power_source();
>  	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +	ret = sysfs_create_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
> +	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
>  }


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF
  2022-07-12 14:58 ` [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF Shyam Sundar S K
@ 2022-07-27 20:52   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 20:52 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> AMD PMF driver provides the flexibility to turn "on" or "off"
> CnQF feature (introduced in the earlier patch).
> 
> Add corresponding ABI documentation for the new sysfs node.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
>  Documentation/ABI/testing/sysfs-amd-pmf | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-amd-pmf
> 
> diff --git a/Documentation/ABI/testing/sysfs-amd-pmf b/Documentation/ABI/testing/sysfs-amd-pmf
> new file mode 100644
> index 000000000000..5935dc549185
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-amd-pmf
> @@ -0,0 +1,11 @@
> +What:		/sys/devices/platform/AMDI0102\:00/cnqf/feat
> +Date:		July 2022
> +Contact:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> +Description:	Reading this file tells if the AMD Platform Management(PMF)
> +		Cool n Quiet Framework(CnQF) feature is enabled or not.
> +
> +		This feature is not enabled by default and gets only turned on
> +		if OEM BIOS passes a "flag" to PMF ACPI function (index 11 or 12)
> +		or in case the user writes "on".
> +
> +		To turn off CnQF user can write "off" to the sysfs node.


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF
  2022-07-12 14:58 ` [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF Shyam Sundar S K
  2022-07-27 20:51   ` Hans de Goede
@ 2022-07-27 21:00   ` Hans de Goede
  1 sibling, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:00 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Bastien Nocera

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> CnQF (a.k.a Cool and Quiet Framework) extends the static slider concept.
> PMF dynamically manages system power limits and fan policy based on system
> power trends which is representative of workload trend.
> 
> Static slider and CnQF controls are mutually exclusive for system power
> budget adjustments. CnQF supports configurable number of modes which can
> be unique for AC and DC. Every mode is representative of a system state
> characterized by unique steady state and boost behavior.
> 
> OEMs can configure the different modes/system states and how the
> transition to a mode happens. Whether to have CnQF manage system power
> budget dynamically in AC or DC or both is also configurable. Mode changes
> due to CnQF don’t result in slider position change.

Hmm, it seems that this in essence means that the profile selected
by the static slider is more or less being ignored, do I have that right?

That seems somewhat unfortunate, users may want to avoid e.g the highest
turbo clocks even though they are running say a big compile, because
the user knows they will not be capable of charging the laptop anytime
soon, so taking a bit longer to compile is better then draining the battery...

And the users preference for say "balanced" now basically gets ignored
by the BIOS/linux kernel PMF code combination ?

I see that the next patch also adds a sysfs nob to turn this on/off.

I'm wondering if it would not be better to see this as another
platform_profile setting. IOW I think it might be best to add a:
PLATFORM_PROFILE_AUTO and advertise that when CnQF is supported:

diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
index e5cbb6841f3a..cafa07f3b803 100644
--- a/include/linux/platform_profile.h
+++ b/include/linux/platform_profile.h
@@ -23,6 +23,7 @@ enum platform_profile_option {
 	PLATFORM_PROFILE_BALANCED,
 	PLATFORM_PROFILE_BALANCED_PERFORMANCE,
 	PLATFORM_PROFILE_PERFORMANCE,
+	PLATFORM_PROFILE_AUTO,
 	PLATFORM_PROFILE_LAST, /*must always be last */
 };
 

And then enable CnQF when AUTO gets selected and disable it when
another profile gets selected ?

How does this work under Windows? Do laptops with CnQF support in
essence ignore the Windows power setting slider like this code is doing ?

Regards,

Hans




> 
> The default OEM values are obtained after evaluating the PMF ACPI function
> idx 11 & 12 for AC and DC respectively. Whether to turn ON/OFF by default
> is guided by a "flag" passed by the OEM BIOS.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/Makefile |   3 +-
>  drivers/platform/x86/amd/pmf/acpi.c   |  56 +++++
>  drivers/platform/x86/amd/pmf/cnqf.c   | 327 ++++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/core.c   |  14 ++
>  drivers/platform/x86/amd/pmf/pmf.h    | 115 +++++++++
>  5 files changed, 514 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/x86/amd/pmf/cnqf.c
> 
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index 557521a80427..d02a0bdc6429 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -5,4 +5,5 @@
>  #
>  
>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> -amd-pmf-objs := core.o acpi.o sps.o
> +amd-pmf-objs := core.o acpi.o sps.o \
> +		cnqf.o
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index e4822c6f4259..a3ff91c605b5 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -56,6 +56,8 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
> +	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
> +	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
>  }
>  
>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev, struct apmf_if *ampf_if)
> @@ -208,6 +210,60 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>  	return err;
>  }
>  
> +int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_AC, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_debug("buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
> +int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_DYN_SLIDER_GRANULAR_DC, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_debug("buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> new file mode 100644
> index 000000000000..8c6756faab25
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include <linux/workqueue.h>
> +#include "pmf.h"
> +
> +static struct cnqf_config config_store;
> +
> +static int amd_pmf_handle_cnqf(struct amd_pmf_dev *dev, bool op, int src, int idx,
> +			       struct cnqf_config *table)
> +{
> +	struct power_table_control *pc;
> +
> +	if (op == SLIDER_OP_SET) {
> +		pc = &config_store.mode_set[src][idx].power_control;
> +
> +		amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL);
> +		amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
> +				 NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
> +				 NULL);
> +	} else if (op == SLIDER_OP_GET) {
> +		pc = &table->mode_set[src][idx].power_control;
> +
> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &pc->spl);
> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &pc->fppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &pc->sppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE, &pc->sppt_apu_only);
> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE, &pc->stt_min);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
> +				 &pc->stt_skin_temp[STT_TEMP_APU]);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
> +				 &pc->stt_skin_temp[STT_TEMP_HS2]);
> +	}
> +
> +	if (dev->apmf_if->func.fan_table_idx)
> +		apmf_update_fan_idx(dev->apmf_if,
> +				    config_store.mode_set[src][idx].fan_control.manual,
> +				    config_store.mode_set[src][idx].fan_control.fan_id);
> +
> +	return 0;
> +}
> +
> +static void amd_pmf_update_power_threshold(void)
> +{
> +	struct cnqf_mode_settings *ts;
> +	struct cnqf_tran_params *tp;
> +	int i;
> +
> +	for (i = 0; i < POWER_SOURCE_MAX; i++) {
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_QUIET];
> +		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_TO_TURBO];
> +		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_BALANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_PERFORMANCE];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_QUIET];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +
> +		tp = &config_store.trans_param[i][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
> +		ts = &config_store.mode_set[i][CNQF_MODE_TURBO];
> +		tp->power_threshold = ts->power_floor - tp->power_delta;
> +	}
> +}
> +
> +static const char *state_as_str(unsigned int state)
> +{
> +	switch (state) {
> +	case CNQF_MODE_QUIET:
> +		return "QUIET";
> +	case CNQF_MODE_BALANCE:
> +		return "BALANCED";
> +	case CNQF_MODE_TURBO:
> +		return "TURBO";
> +	case CNQF_MODE_PERFORMANCE:
> +		return "PERFORMANCE";
> +	default:
> +		return "Unknown CnQF mode";
> +	}
> +}
> +
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
> +{
> +	struct cnqf_tran_params *tp;
> +	int src, i, j, index = 0;
> +	u32 avg_power = 0;
> +
> +	src = amd_pmf_get_power_source();
> +
> +	if (!dev->cnqf_running) {
> +		pr_debug("setting CNQF thermals\n");
> +		amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +		dev->cnqf_running = true;
> +		return;
> +	}
> +
> +	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
> +		config_store.trans_param[src][i].timer += time_lapsed_ms;
> +		config_store.trans_param[src][i].total_power += socket_power;
> +		config_store.trans_param[src][i].count++;
> +
> +		tp = &config_store.trans_param[src][i];
> +		if (tp->timer >= tp->time_constant && tp->count) {
> +			avg_power = tp->total_power / tp->count;
> +
> +			/* Reset the indices */
> +			tp->timer = 0;
> +			tp->total_power = 0;
> +			tp->count = 0;
> +
> +			if ((tp->shifting_up && avg_power >= tp->power_threshold) ||
> +			    (!tp->shifting_up && avg_power <= tp->power_threshold)) {
> +				tp->priority = true;
> +			} else {
> +				tp->priority = false;
> +			}
> +		}
> +	}
> +
> +	dev_dbg(dev->dev, "[CNQF] Avg power: %u mW socket power: %u mW mode:%s\n",
> +		avg_power, socket_power, state_as_str(config_store.current_mode));
> +
> +	for (j = 0; j < CNQF_TRANSITION_MAX; j++) {
> +		/* apply the highest priority */
> +		index = config_store.trans_prio[j];
> +		if (config_store.trans_param[src][index].priority) {
> +			if (config_store.current_mode !=
> +			    config_store.trans_param[src][index].target_mode) {
> +				config_store.current_mode =
> +						config_store.trans_param[src][index].target_mode;
> +				dev_dbg(dev->dev, "Moving to Mode :%s\n",
> +					state_as_str(config_store.current_mode));
> +				amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src,
> +						    config_store.current_mode, NULL);
> +			}
> +			break;
> +		}
> +	}
> +}
> +
> +static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output out)
> +{
> +	struct cnqf_tran_params *tp;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_QUIET];
> +	tp->time_constant = out.t_balanced_to_quiet;
> +	tp->target_mode = CNQF_MODE_QUIET;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];
> +	tp->time_constant = out.t_balanced_to_perf;
> +	tp->target_mode = CNQF_MODE_PERFORMANCE;
> +	tp->shifting_up = true;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];
> +	tp->time_constant = out.t_quiet_to_balanced;
> +	tp->target_mode = CNQF_MODE_BALANCE;
> +	tp->shifting_up = true;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];
> +	tp->time_constant = out.t_perf_to_balanced;
> +	tp->target_mode = CNQF_MODE_BALANCE;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];
> +	tp->time_constant = out.t_turbo_to_perf;
> +	tp->target_mode = CNQF_MODE_PERFORMANCE;
> +	tp->shifting_up = false;
> +
> +	tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_TURBO];
> +	tp->time_constant = out.t_perf_to_turbo;
> +	tp->target_mode = CNQF_MODE_TURBO;
> +	tp->shifting_up = true;
> +}
> +
> +static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output out)
> +{
> +	struct cnqf_mode_settings *ms;
> +
> +	/* Quiet Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_QUIET];
> +	ms->power_floor = out.ps[APMF_CNQF_QUIET].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_QUIET].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_QUIET].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_QUIET].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_QUIET].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_QUIET].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_QUIET].fan_id;
> +
> +	/* Balance Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_BALANCE];
> +	ms->power_floor = out.ps[APMF_CNQF_BALANCE].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_BALANCE].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_BALANCE].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_BALANCE].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_BALANCE].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_BALANCE].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_BALANCE].fan_id;
> +
> +	/* Performance Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_PERFORMANCE];
> +	ms->power_floor = out.ps[APMF_CNQF_PERFORMANCE].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_PERFORMANCE].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_PERFORMANCE].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_PERFORMANCE].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_PERFORMANCE].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_PERFORMANCE].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_PERFORMANCE].fan_id;
> +
> +	/* Turbo Mode */
> +	ms = &config_store.mode_set[idx][CNQF_MODE_TURBO];
> +	ms->power_floor = out.ps[APMF_CNQF_TURBO].pfloor;
> +	ms->power_control.fppt = out.ps[APMF_CNQF_TURBO].fppt;
> +	ms->power_control.sppt = out.ps[APMF_CNQF_TURBO].sppt;
> +	ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_TURBO].sppt_apu_only;
> +	ms->power_control.spl = out.ps[APMF_CNQF_TURBO].spl;
> +	ms->power_control.stt_min = out.ps[APMF_CNQF_TURBO].stt_min_limit;
> +	ms->power_control.stt_skin_temp[STT_TEMP_APU] =
> +		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU];
> +	ms->power_control.stt_skin_temp[STT_TEMP_HS2] =
> +		out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2];
> +	ms->fan_control.fan_id = out.ps[APMF_CNQF_TURBO].fan_id;
> +}
> +
> +static int amd_pmf_check_flags(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_dyn_slider_output out;
> +
> +	if (dev->apmf_if->func.dyn_slider_ac)
> +		apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
> +	else if (dev->apmf_if->func.dyn_slider_dc)
> +		apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
> +
> +	return out.flags;
> +}
> +
> +void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_dyn_slider_output out;
> +	int i, j, ret;
> +
> +	for (i = 0; i < POWER_SOURCE_MAX; i++) {
> +		if (i == POWER_SOURCE_AC && dev->apmf_if->func.dyn_slider_ac) {
> +			ret = apmf_get_dyn_slider_def_ac(dev->apmf_if, &out);
> +			if (ret)
> +				dev_err(dev->dev,
> +					"APMF apmf_get_dyn_slider_def_ac failed :%d\n", ret);
> +		} else if (i == POWER_SOURCE_DC && dev->apmf_if->func.dyn_slider_dc) {
> +			ret = apmf_get_dyn_slider_def_dc(dev->apmf_if, &out);
> +			if (ret)
> +				dev_err(dev->dev,
> +					"APMF apmf_get_dyn_slider_def_dc failed :%d\n", ret);
> +		}
> +
> +		amd_pmf_update_mode_set(i, out);
> +		amd_pmf_update_trans_data(i, out);
> +
> +		for (j = 0; j < CNQF_MODE_MAX; j++) {
> +			if (config_store.mode_set[i][j].fan_control.fan_id == FAN_INDEX_AUTO)
> +				config_store.mode_set[i][j].fan_control.manual = false;
> +			else
> +				config_store.mode_set[i][j].fan_control.manual = true;
> +		}
> +	}
> +	amd_pmf_update_power_threshold();
> +
> +	for (i = 0; i < CNQF_TRANSITION_MAX; i++)
> +		config_store.trans_prio[i] = i;
> +}
> +
> +void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
> +{
> +	cancel_delayed_work_sync(&dev->work_buffer);
> +}
> +
> +void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
> +{
> +	int ret, src;
> +
> +	ret = amd_pmf_check_flags(dev);
> +	if (!ret) {
> +		dev_dbg(dev->dev, "CnQF bios default_enable flag not set\n");
> +		return;
> +	}
> +
> +	dev->cnqf_feat = true;
> +	amd_pmf_load_defaults_cnqf(dev);
> +	amd_pmf_init_metrics_table(dev);
> +
> +	/* update the thermal for CnQF */
> +	src = amd_pmf_get_power_source();
> +	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
> +}
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index c6fd52c46818..bc267d333b76 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	/* Calculate the avg SoC power consumption */
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
> +	if (dev->cnqf_feat) {
> +		/* Apply the CnQF transition */
> +		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
> +	}
> +
>  	dev->start_time = ktime_to_ms(ktime_get());
>  	dev_dbg(dev->dev, "Metrics table sample size time:%d\n", metrics_table_loop_ms);
>  	schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
> @@ -255,12 +260,21 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>  		amd_pmf_init_sps(dev);
>  		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>  	}
> +	/* Enable Cool n Quiet Framework (CnQF) */
> +	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
> +	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
> +		amd_pmf_init_cnqf(dev);
> +		dev_dbg(dev->dev, "CnQF Init done\n");
> +	}
>  }
>  
>  static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>  {
>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>  		amd_pmf_deinit_sps(dev);
> +	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
> +	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
> +		amd_pmf_deinit_cnqf(dev);
>  }
>  
>  static const struct acpi_device_id amd_pmf_acpi_ids[] = {
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 8f318ff59c2e..452266809dfa 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -20,6 +20,8 @@
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_SET_FAN_IDX				7
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
> +#define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
> +#define APMF_FUNC_DYN_SLIDER_GRANULAR_DC	12
>  
>  /* Message Definitions */
>  #define SET_SPL				0x03 /* SPL: Sustained Power Limit */
> @@ -51,6 +53,8 @@ struct apmf_if_functions {
>  	bool sbios_heartbeat;
>  	bool fan_table_idx;
>  	bool static_slider_granular;
> +	bool dyn_slider_ac;
> +	bool dyn_slider_dc;
>  };
>  
>  struct apmf_notification_cfg {
> @@ -163,6 +167,8 @@ struct amd_pmf_dev {
>  #if IS_ENABLED(CONFIG_DEBUG_FS)
>  	struct dentry *dbgfs_dir;
>  #endif /* CONFIG_DEBUG_FS */
> +	bool cnqf_feat;
> +	bool cnqf_running;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -191,6 +197,106 @@ struct fan_table_control {
>  	unsigned long fan_id;
>  };
>  
> +struct power_table_control {
> +	u32 spl;
> +	u32 sppt;
> +	u32 fppt;
> +	u32 sppt_apu_only;
> +	u32 stt_min;
> +	u32 stt_skin_temp[STT_TEMP_COUNT];
> +	u32 reserved[16];
> +};
> +
> +/* CnQF Layer */
> +enum cnqf_trans_priority {
> +	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
> +	CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE, /* quiet/balance to Performance Mode */
> +	CNQF_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
> +	CNQF_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
> +	CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance/Turbo to Balance Mode */
> +	CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE, /* Turbo mode to Performance Mode */
> +	CNQF_TRANSITION_MAX,
> +};
> +
> +enum cnqf_mode {
> +	CNQF_MODE_QUIET,
> +	CNQF_MODE_BALANCE,
> +	CNQF_MODE_PERFORMANCE,
> +	CNQF_MODE_TURBO,
> +	CNQF_MODE_MAX,
> +};
> +
> +enum apmf_cnqf_pos {
> +	APMF_CNQF_TURBO,
> +	APMF_CNQF_PERFORMANCE,
> +	APMF_CNQF_BALANCE,
> +	APMF_CNQF_QUIET,
> +	APMF_CNQF_MAX,
> +};
> +
> +struct cnqf_mode_settings {
> +	struct power_table_control power_control;
> +	struct fan_table_control fan_control;
> +	u32 power_floor;
> +	bool enable;
> +};
> +
> +struct cnqf_tran_params {
> +	u32 time_constant; /* minimum time required to switch to next mode */
> +	u32 power_delta; /* minimum power required to switch to next mode */
> +	u32 power_threshold;
> +	u32 timer; /* elapsed time. if timer > timethreshold, it will move to next mode */
> +	u32 total_power;
> +	u32 count;
> +	bool enable;
> +	bool priority;
> +	bool shifting_up;
> +	enum cnqf_mode target_mode;
> +};
> +
> +struct cnqf_power_delta {
> +	u32 to_turbo;
> +	u32 balance_to_perf;
> +	u32 quiet_to_balance;
> +	u32 to_quiet;
> +	u32 perf_to_balance;
> +	u32 turbo_to_perf;
> +};
> +
> +struct cnqf_config {
> +	struct cnqf_tran_params trans_param[POWER_SOURCE_MAX][CNQF_TRANSITION_MAX];
> +	struct cnqf_mode_settings mode_set[POWER_SOURCE_MAX][CNQF_MODE_MAX];
> +	struct power_table_control defaults;
> +	enum cnqf_mode current_mode;
> +	struct cnqf_power_delta power_delta[POWER_SOURCE_MAX];
> +	u32 power_src;
> +	u32 avg_power;
> +	enum cnqf_trans_priority trans_prio[CNQF_TRANSITION_MAX];
> +};
> +
> +struct apmf_cnqf_power_set {
> +	u32 pfloor;
> +	u32 fppt;
> +	u32 sppt;
> +	u32 sppt_apu_only;
> +	u32 spl;
> +	u32 stt_min_limit;
> +	u8 stt_skintemp[STT_TEMP_COUNT];
> +	u32 fan_id;
> +} __packed;
> +
> +struct apmf_dyn_slider_output {
> +	u16 size;
> +	u16 flags;
> +	u32 t_perf_to_turbo;
> +	u32 t_balanced_to_perf;
> +	u32 t_quiet_to_balanced;
> +	u32 t_balanced_to_quiet;
> +	u32 t_perf_to_balanced;
> +	u32 t_turbo_to_perf;
> +	struct apmf_cnqf_power_set ps[APMF_CNQF_MAX];
> +};
> +
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
> @@ -210,4 +316,13 @@ int apmf_get_static_slider_granular(struct apmf_if *ampf_if,
>  void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>  
>  int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
> +
> +/* CnQF Layer */
> +int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> +int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> +void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +
>  #endif /* PMF_H */


^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF
  2022-07-27 20:52   ` Hans de Goede
@ 2022-07-27 21:12     ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:12 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/27/22 22:52, Hans de Goede wrote:
> Hi,
> 
> On 7/12/22 16:58, Shyam Sundar S K wrote:
>> Whether to turn CnQF on/off by default upon driver load would be decided
>> by a BIOS flag. Add a sysfs node to provide a way to the user whether to
>> use static slider or CnQF .
>>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> 
> Thanks, patch looks good to me:
> 
> Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Thinking more about this, there are userspace API questions here, see
my reply to patch 9/15.

Also ...

>> ---
>>  drivers/platform/x86/amd/pmf/cnqf.c | 52 +++++++++++++++++++++++++++++
>>  1 file changed, 52 insertions(+)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
>> index 8c6756faab25..2b03ae1ad37f 100644
>> --- a/drivers/platform/x86/amd/pmf/cnqf.c
>> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
>> @@ -302,9 +302,59 @@ void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
>>  		config_store.trans_prio[i] = i;
>>  }
>>  
>> +static ssize_t feat_store(struct device *dev,
>> +				  struct device_attribute *attr,
>> +				  const char *buf, size_t count)
>> +{
>> +	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
>> +	int result, src;
>> +	bool enabled;
>> +	u8 mode;
>> +
>> +	result = kstrtobool(buf, &enabled);
>> +	if (result)
>> +		return result;
>> +
>> +	src = amd_pmf_get_power_source();
>> +	pdev->cnqf_feat = enabled;
>> +	if (pdev->cnqf_feat) {
>> +		amd_pmf_handle_cnqf(pdev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
>> +	} else {
>> +		pdev->cnqf_running = false;
>> +		mode = amd_pmf_get_pprof_modes(pdev);
>> +		amd_pmf_update_slider(pdev, SLIDER_OP_SET, mode, NULL);

This assumes that the static slider is always available on systems
with CnQF support, which is not checked anywhere.

Also work_buffer is still running here which will reset cnqf_running = true
after its first run, so this does not disable CnQF at all.

But maybe that is a bug in the amd_pmf_trans_cnqf() function ?

Maybe that function should not touch the cnqf_running flag; and it should
keep gathering statistics even when CqNF is disabled, but not actually
set the profile ?

Regards,

Hans



>> +	}
>> +
>> +	dev_dbg(pdev->dev, "Received CnQF %s\n", enabled ? "on" : "off");
>> +	return count;
>> +}
>> +
>> +static ssize_t feat_show(struct device *dev,
>> +				 struct device_attribute *attr,
>> +				 char *buf)
>> +{
>> +	struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
>> +
>> +	return sprintf(buf, "%s\n", pdev->cnqf_feat ? "on" : "off");
>> +}
>> +
>> +static DEVICE_ATTR_RW(feat);
>> +
>> +static struct attribute *cnqf_feature_attrs[] = {
>> +	&dev_attr_feat.attr,
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group cnqf_feature_attribute_group = {
>> +	.attrs = cnqf_feature_attrs,
>> +	.name = "cnqf"
>> +};
>> +
>>  void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)
>>  {
>>  	cancel_delayed_work_sync(&dev->work_buffer);
>> +	sysfs_remove_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
>> +	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
>>  }
>>  
>>  void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
>> @@ -324,4 +374,6 @@ void amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
>>  	/* update the thermal for CnQF */
>>  	src = amd_pmf_get_power_source();
>>  	amd_pmf_handle_cnqf(dev, SLIDER_OP_SET, src, config_store.current_mode, NULL);
>> +	ret = sysfs_create_group(&dev->dev->kobj, &cnqf_feature_attribute_group);
>> +	kobject_uevent(&dev->dev->kobj, KOBJ_CHANGE);
>>  }


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature
  2022-07-12 14:58 ` [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature Shyam Sundar S K
@ 2022-07-27 21:22   ` Hans de Goede
  2022-07-28 12:57     ` Shyam Sundar S K
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:22 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> The objective of this feature is to track the moving average of system
> power over the time period specified and switch to the subsequent mode.
> 
> This feature has 3 modes: quiet, balanced, performance.
> 
> In order to do this, PMF driver will get the moving average of APU power
> from PMFW and power threshold, time constants, system config parameters
> from OEM inputs.
> 
> System power as read by PMF driver from PMFW is the filtered value over
> the sampling window. Every sampling window, moving average of system power
> is computed. At the end of the monitoring window, the moving average is
> compared against the threshold for mode switch for decision making.
> 
> With AMD managing the system config limits, any mode switch within
> auto-mode will result in limits of fPPT/sPPT/STAPM or STT being scaled
> down.
> 
> When "auto mode" is enabled, the static slider control remains out of
> the PMF driver, so the platform_profile registration would not
> happen in PMF driver.

This is not what happens in the code, the platform_profile registration
is still happening AFAICT.

Like with CnQF for this other auto mode we also need to figure out the
relation with the regular static platform_profile settings.

I see this in the code:

@@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
 	/* Calculate the avg SoC power consumption */
 	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
 
+	if (current_profile == PLATFORM_PROFILE_BALANCED) {
+		/* Apply the Auto Mode transition */
+		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+	}
+

So it seems that unlike CnQF this other auto-mode only does mode transitions
when the static-slider is set to balanced ?


> 
> The transition to auto-mode only happens when the APMF fn5 is enabled
> in BIOS, platform_profile set to "balanced" and a AMT
> (Auto Mode transition) is received.

Right, this part of the commit msg matches the code, but it conflicts with
the "When "auto mode" is enabled, the static slider control remains out of
the PMF driver:" part of the commit msg above.

> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/Makefile    |   2 +-
>  drivers/platform/x86/amd/pmf/acpi.c      |  28 ++
>  drivers/platform/x86/amd/pmf/auto-mode.c | 317 +++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/core.c      |  25 +-
>  drivers/platform/x86/amd/pmf/pmf.h       | 104 ++++++++
>  5 files changed, 473 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c
> 
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index d02a0bdc6429..2a9568bf9064 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -6,4 +6,4 @@
>  
>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
>  amd-pmf-objs := core.o acpi.o sps.o \
> -		cnqf.o
> +		cnqf.o auto-mode.o
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index a3ff91c605b5..e9f33e61659f 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
> +	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>  	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
> @@ -210,6 +211,33 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>  	return err;
>  }
>  
> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_AUTO_MODE, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(*data)) {
> +		pr_debug("buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*data), size);
> +	memset(data, 0, sizeof(*data));
> +	memcpy(data, info->buffer.pointer, size);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +

Please use apmf_if_call_store_buffer() for this (see review of 4/15)

Regards,

Hans


>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
>  {
>  	union acpi_object *info;
> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
> new file mode 100644
> index 000000000000..954fde25e71e
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
> @@ -0,0 +1,317 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * AMD Platform Management Framework Driver
> + *
> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/list.h>
> +#include <linux/workqueue.h>
> +#include "pmf.h"
> +
> +#define AVG_SAMPLE_SIZE 3
> +
> +struct power_history {
> +	int socket_power;
> +	struct list_head list;
> +	int avg;
> +	int total;
> +};
> +
> +struct list_pdata {
> +	int total;
> +	int avg;
> +};
> +
> +static struct power_history power_list;
> +static struct list_pdata pdata;
> +
> +static struct auto_mode_mode_config config_store;
> +static const char *state_as_str(unsigned int state);
> +
> +static void amd_pmf_handle_automode(struct amd_pmf_dev *dev, bool op, int idx,
> +				    struct auto_mode_mode_config *table)
> +{
> +	if (op == SLIDER_OP_SET) {
> +		struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control;
> +
> +		amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL);
> +		amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
> +	} else if (op == SLIDER_OP_GET) {
> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.spl);
> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.fppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.sppt);
> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.sppt_apu_only);
> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.stt_min);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_APU]);
> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_HS2]);
> +	}
> +
> +	if (dev->apmf_if->func.fan_table_idx)
> +		apmf_update_fan_idx(dev->apmf_if, config_store.mode_set[idx].fan_control.manual,
> +				    config_store.mode_set[idx].fan_control.fan_id);
> +}
> +
> +static int amd_pmf_get_moving_avg(int socket_power)
> +{
> +	struct power_history *tmp;
> +	struct list_head *pos, *q;
> +	static int count;
> +
> +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
> +	tmp->socket_power = socket_power;
> +	list_add_tail(&tmp->list, &power_list.list);
> +
> +	list_for_each_safe(pos, q, &power_list.list) {
> +		if (count >= AVG_SAMPLE_SIZE) {
> +			tmp = list_first_entry(pos, struct power_history, list);
> +			list_del_init(pos);
> +			goto next;
> +		}
> +	}
> +
> +next:
> +	pdata.total = 0;
> +	pdata.avg = 0;
> +
> +	list_for_each(pos, &power_list.list) {
> +		tmp = list_entry(pos, struct power_history, list);
> +		pdata.total += tmp->socket_power;
> +		pdata.avg = pdata.total / AVG_SAMPLE_SIZE;
> +	}
> +
> +	count++;
> +	if (count >= AVG_SAMPLE_SIZE)
> +		return pdata.avg;
> +
> +	return 0;
> +}
> +
> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
> +{
> +	int avg_power = 0;
> +	bool update = false;
> +	int i, j;
> +
> +	/* Get the average moving average computed by auto mode algorithm */
> +	avg_power = amd_pmf_get_moving_avg(socket_power);
> +
> +	for (i = 0; i < AUTO_TRANSITION_MAX; i++) {
> +		if ((config_store.transition[i].shifting_up && avg_power >=
> +		     config_store.transition[i].power_threshold) ||
> +		    (!config_store.transition[i].shifting_up && avg_power <=
> +		     config_store.transition[i].power_threshold)) {
> +			if (config_store.transition[i].timer <
> +			    config_store.transition[i].time_constant)
> +				config_store.transition[i].timer += time_elapsed_ms;
> +		} else {
> +			config_store.transition[i].timer = 0;
> +		}
> +
> +		if (config_store.transition[i].timer >=
> +		    config_store.transition[i].time_constant &&
> +		    !config_store.transition[i].applied) {
> +			config_store.transition[i].applied = true;
> +			update = true;
> +		} else if (config_store.transition[i].timer <=
> +			   config_store.transition[i].time_constant &&
> +			   config_store.transition[i].applied) {
> +			config_store.transition[i].applied = false;
> +			update = true;
> +		}
> +	}
> +
> +	dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power,
> +		state_as_str(config_store.current_mode));
> +
> +	if (update) {
> +		for (j = 0; j < AUTO_TRANSITION_MAX; j++) {
> +			/* Apply the mode with highest priority indentified */
> +			if (config_store.transition[j].applied) {
> +				if (config_store.current_mode !=
> +				    config_store.transition[j].target_mode) {
> +					config_store.current_mode =
> +							config_store.transition[j].target_mode;
> +					dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n",
> +						state_as_str(config_store.current_mode));
> +					amd_pmf_handle_automode(dev, SLIDER_OP_SET,
> +								config_store.current_mode, NULL);
> +				}
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +static void amd_pmf_get_power_threshold(void)
> +{
> +	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
> +				config_store.mode_set[AUTO_BALANCE].power_floor -
> +				config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta;
> +
> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold =
> +				config_store.mode_set[AUTO_BALANCE].power_floor -
> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta;
> +
> +	config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold =
> +			config_store.mode_set[AUTO_QUIET].power_floor -
> +			config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta;
> +
> +	config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold =
> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor -
> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta;
> +}
> +
> +static const char *state_as_str(unsigned int state)
> +{
> +	switch (state) {
> +	case AUTO_QUIET:
> +		return "QUIET";
> +	case AUTO_BALANCE:
> +		return "BALANCED";
> +	case AUTO_PERFORMANCE_ON_LAP:
> +		return "ON_LAP";
> +	case AUTO_PERFORMANCE:
> +		return "PERFORMANCE";
> +	default:
> +		return "Unknown Auto Mode State";
> +	}
> +}
> +
> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev)
> +{
> +	struct apmf_auto_mode output;
> +	struct power_table_control *pwr_ctrl;
> +	int i;
> +
> +	if (dev->apmf_if->func.auto_mode_def) {
> +		apmf_get_auto_mode_def(dev->apmf_if, &output);
> +		/* time constant */
> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant =
> +									output.balanced_to_quiet;
> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant =
> +									output.balanced_to_perf;
> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant =
> +									output.quiet_to_balanced;
> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant =
> +									output.perf_to_balanced;
> +
> +		/* power floor */
> +		config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet;
> +		config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced;
> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf;
> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf;
> +
> +		/* Power delta for mode change */
> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta =
> +									output.pd_balanced_to_quiet;
> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta =
> +									output.pd_balanced_to_perf;
> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta =
> +									output.pd_quiet_to_balanced;
> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta =
> +									output.pd_perf_to_balanced;
> +
> +		/* Power threshold */
> +		amd_pmf_get_power_threshold();
> +
> +		/* skin temperature limits */
> +		pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control;
> +		pwr_ctrl->spl = output.spl_quiet;
> +		pwr_ctrl->sppt = output.sppt_quiet;
> +		pwr_ctrl->fppt = output.fppt_quiet;
> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet;
> +		pwr_ctrl->stt_min = output.stt_min_limit_quiet;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet;
> +
> +		pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control;
> +		pwr_ctrl->spl = output.spl_balanced;
> +		pwr_ctrl->sppt = output.sppt_balanced;
> +		pwr_ctrl->fppt = output.fppt_balanced;
> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced;
> +		pwr_ctrl->stt_min = output.stt_min_limit_balanced;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced;
> +
> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control;
> +		pwr_ctrl->spl = output.spl_perf;
> +		pwr_ctrl->sppt = output.sppt_perf;
> +		pwr_ctrl->fppt = output.fppt_perf;
> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf;
> +		pwr_ctrl->stt_min = output.stt_min_limit_perf;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf;
> +
> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control;
> +		pwr_ctrl->spl = output.spl_perf_on_lap;
> +		pwr_ctrl->sppt = output.sppt_perf_on_lap;
> +		pwr_ctrl->fppt = output.fppt_perf_on_lap;
> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap;
> +		pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap;
> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap;
> +
> +		/* Fan ID */
> +		config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet;
> +		config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced;
> +		config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf;
> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id =
> +										output.fan_id_perf;
> +
> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET;
> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
> +									AUTO_PERFORMANCE;
> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode =
> +										AUTO_BALANCE;
> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode =
> +										AUTO_BALANCE;
> +
> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false;
> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true;
> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true;
> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up =
> +											false;
> +
> +		for (i = 0 ; i < AUTO_MODE_MAX ; i++) {
> +			if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO)
> +				config_store.mode_set[i].fan_control.manual = false;
> +			else
> +				config_store.mode_set[i].fan_control.manual = true;
> +		}
> +	}
> +}
> +
> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev)
> +{
> +	cancel_delayed_work_sync(&dev->work_buffer);
> +}
> +
> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev)
> +{
> +	INIT_LIST_HEAD(&power_list.list);
> +
> +	amd_pmf_init_metrics_table(dev);
> +	amd_pmf_load_defaults_auto_mode(dev);
> +
> +	/* update the thermal for Automode */
> +	amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +}
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index bc267d333b76..674ddf599135 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	/* Calculate the avg SoC power consumption */
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
> +		/* Apply the Auto Mode transition */
> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +	}
> +
>  	if (dev->cnqf_feat) {
>  		/* Apply the CnQF transition */
>  		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
> @@ -260,9 +265,18 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>  		amd_pmf_init_sps(dev);
>  		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>  	}
> -	/* Enable Cool n Quiet Framework (CnQF) */
> -	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
> +
> +	/* Enable Auto Mode */
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
> +		amd_pmf_init_auto_mode(dev);
> +		dev_dbg(dev->dev, "Auto Mode Init done\n");
> +		/*
> +		 * Auto mode and CnQF cannot co-exist. If auto mode is supported it takes
> +		 * higher priority over CnQF.
> +		 */
> +	} else if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
> +		/* Enable Cool n Quiet Framework (CnQF) */
>  		amd_pmf_init_cnqf(dev);
>  		dev_dbg(dev->dev, "CnQF Init done\n");
>  	}
> @@ -272,6 +286,13 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>  {
>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>  		amd_pmf_deinit_sps(dev);
> +
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
> +		amd_pmf_deinit_auto_mode(dev);
> +		/* If auto mode is supported, there is no need to proceed */
> +		return;
> +	}
> +
>  	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
>  		amd_pmf_deinit_cnqf(dev);
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 452266809dfa..0532f49e9613 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -18,6 +18,7 @@
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
> +#define APMF_FUNC_AUTO_MODE					5
>  #define APMF_FUNC_SET_FAN_IDX				7
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>  #define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
> @@ -51,6 +52,7 @@
>  struct apmf_if_functions {
>  	bool system_params;
>  	bool sbios_heartbeat;
> +	bool auto_mode_def;
>  	bool fan_table_idx;
>  	bool static_slider_granular;
>  	bool dyn_slider_ac;
> @@ -197,6 +199,33 @@ struct fan_table_control {
>  	unsigned long fan_id;
>  };
>  
> +/* Auto Mode Layer */
> +enum auto_mode_transition_priority {
> +	AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */
> +	AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
> +	AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
> +	AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */
> +	AUTO_TRANSITION_MAX,
> +};
> +
> +enum auto_mode_mode {
> +	AUTO_QUIET,
> +	AUTO_BALANCE,
> +	AUTO_PERFORMANCE_ON_LAP,
> +	AUTO_PERFORMANCE,
> +	AUTO_MODE_MAX,
> +};
> +
> +struct auto_mode_trans_params {
> +	u32	time_constant; /* minimum time required to switch to next mode */
> +	u32 power_delta; /* delta power to shift mode */
> +	u32 power_threshold;
> +	u32	timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */
> +	u32 applied;
> +	enum auto_mode_mode target_mode;
> +	u32 shifting_up;
> +};
> +
>  struct power_table_control {
>  	u32 spl;
>  	u32 sppt;
> @@ -207,6 +236,74 @@ struct power_table_control {
>  	u32 reserved[16];
>  };
>  
> +struct auto_mode_mode_settings {
> +	struct power_table_control power_control;
> +	struct fan_table_control fan_control;
> +	u32 power_floor;
> +};
> +
> +struct auto_mode_mode_config {
> +	struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX];
> +	struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX];
> +	enum auto_mode_mode current_mode;
> +	bool on_lap;
> +	bool better_perf;
> +	u32 amt_enabled; /* Auto Mode Transition */
> +	u32 avg_power;
> +};
> +
> +struct apmf_auto_mode {
> +	u16 size;
> +	/* time constant */
> +	u32 balanced_to_perf;
> +	u32 perf_to_balanced;
> +	u32 quiet_to_balanced;
> +	u32 balanced_to_quiet;
> +	/* power floor */
> +	u32 pfloor_perf;
> +	u32 pfloor_balanced;
> +	u32 pfloor_quiet;
> +	/* Power delta for mode change */
> +	u32 pd_balanced_to_perf;
> +	u32 pd_perf_to_balanced;
> +	u32 pd_quiet_to_balanced;
> +	u32 pd_balanced_to_quiet;
> +	/* skin temperature limits */
> +	u8 stt_apu_perf_on_lap; /* CQL ON */
> +	u8 stt_hs2_perf_on_lap; /* CQL ON */
> +	u8 stt_apu_perf;
> +	u8 stt_hs2_perf;
> +	u8 stt_apu_balanced;
> +	u8 stt_hs2_balanced;
> +	u8 stt_apu_quiet;
> +	u8 stt_hs2_quiet;
> +	u32 stt_min_limit_perf_on_lap; /* CQL ON */
> +	u32 stt_min_limit_perf;
> +	u32 stt_min_limit_balanced;
> +	u32 stt_min_limit_quiet;
> +	/* SPL based */
> +	u32 fppt_perf_on_lap; /* CQL ON */
> +	u32 sppt_perf_on_lap; /* CQL ON */
> +	u32 spl_perf_on_lap; /* CQL ON */
> +	u32 sppt_apu_only_perf_on_lap; /* CQL ON */
> +	u32 fppt_perf;
> +	u32 sppt_perf;
> +	u32 spl_perf;
> +	u32 sppt_apu_only_perf;
> +	u32 fppt_balanced;
> +	u32 sppt_balanced;
> +	u32 spl_balanced;
> +	u32 sppt_apu_only_balanced;
> +	u32 fppt_quiet;
> +	u32 sppt_quiet;
> +	u32 spl_quiet;
> +	u32 sppt_apu_only_quiet;
> +	/* Fan ID */
> +	u32 fan_id_perf;
> +	u32 fan_id_balanced;
> +	u32 fan_id_quiet;
> +} __packed;
> +
>  /* CnQF Layer */
>  enum cnqf_trans_priority {
>  	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
> @@ -317,6 +414,13 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>  
>  int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>  
> +/* Auto Mode Layer */
> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +
>  /* CnQF Layer */
>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>  int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
@ 2022-07-27 21:33   ` Hans de Goede
  2022-07-27 21:44   ` Hans de Goede
  2022-07-27 21:46   ` Hans de Goede
  2 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:33 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> The transition to auto-mode happens when the PMF driver receives
> AMT (Auto Mode transition) event. transition logic will reside in the
> PMF driver but the events would come from other supported drivers[1].
> 
> The thermal parameters would vary between when a performance "on-lap" mode
> is detected and versus when not. The CQL event would get triggered from
> other drivers, so that PMF driver would adjust the system thermal config
> based on the ACPI inputs.
> 
> OEMs can control whether or not to enable AMT or CQL via other supported
> drivers[1] but the actual transition logic resides in the AMD PMF driver.
> When an AMT event is received the automatic mode transition RAPL algorithm
> will run. When a CQL event is received an performance "on-lap" mode will
> be enabled and thermal parameters will be adjusted accordingly.
> 
> [1]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-hans&id=755b249250df1b612d982f3b702c831b26ecdf73
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>  drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>  drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>  drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>  drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>  5 files changed, 156 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index e9f33e61659f..4bde86aeafa0 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -12,6 +12,8 @@
>  #include "pmf.h"
>  
>  #define APMF_METHOD "\\_SB_.PMF_.APMF"
> +#define APMF_CQL_NOTIFICATION	2
> +#define APMF_AMT_NOTIFICATION	3
>  
>  static unsigned long supported_func;
>  
> @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
> +	func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>  	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  	return err;
>  }
>  
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(union acpi_object)) {
> +		pr_err("PMF: buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*req), size);
> +	memset(req, 0, sizeof(*req));
> +	memcpy(req, info->buffer.pointer, size);
> +
> +	pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
> +		 req->pending_req, req->cql_event, req->amt_event);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +

Please use apmf_if_call_store_buffer() for this.

>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>  	return handle;
>  }
>  
> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
> +{
> +	struct amd_pmf_dev *pmf_dev = data;
> +	struct apmf_if *apmf_if = pmf_dev->apmf_if;
> +	int ret;
> +
> +	if (apmf_if->func.sbios_requests) {
> +		struct apmf_sbios_req req;
> +
> +		ret = apmf_get_sbios_requests(apmf_if, &req);
> +		if (ret) {
> +			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
> +			return;
> +		}
> +		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
> +			pr_debug("PMF: AMT is supported and notifications %s\n",
> +				 req.amt_event ? "Enabled" : "Disabled");
> +			if (req.amt_event)
> +				pmf_dev->is_amt_event = true;
> +			else
> +				pmf_dev->is_amt_event = !!req.amt_event;

Replace the entire if-else with:

			pmf_dev->is_amt_event = !!req.amt_event;

please.


> +		}
> +
> +		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
> +			pr_debug("PMF: CQL is supported and notifications %s\n",
> +				 req.cql_event ? "Enabled" : "Disabled");
> +			if (req.cql_event)
> +				pmf_dev->is_cql_event = true;
> +			else
> +				pmf_dev->is_cql_event = !!req.cql_event;

Replace the entire if-else with:

			pmf_dev->is_cql_event = !!req.cql_event;

please.


> +
> +			/* update the target mode information */
> +			amd_pmf_update_2_cql(pmf_dev);
> +		}
> +	}
> +}
> +
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
> +
>  	if (pmf_dev->apmf_if->func.sbios_heartbeat)
>  		cancel_delayed_work_sync(&pmf_dev->heart_beat);
> +
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
> +		acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
> +					   apmf_event_handler);
>  }
>  
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>  	struct apmf_notification_cfg *n;
>  	acpi_handle apmf_if_handle;
>  	struct apmf_if *apmf_if;
> -	int ret;
> +	int ret, status;
>  
>  	apmf_if_handle = apmf_if_probe_handle();
>  	if (!apmf_if_handle)
> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>  	}
>  
> +	/* Install the APMF Notify handler */
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
> +		status = acpi_install_notify_handler(ahandle,
> +						     ACPI_ALL_NOTIFY,
> +						     apmf_event_handler, pmf_dev);
> +		if (ACPI_FAILURE(status)) {
> +			dev_err(pmf_dev->dev, "failed to install notify handler\n");
> +			return -ENODEV;
> +		}
> +	}
> +
>  out:
>  	return ret;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
> index 954fde25e71e..a85ef4b95cdb 100644
> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	bool update = false;
>  	int i, j;
>  
> +	if (!dev->amt_running && dev->is_amt_event) {
> +		dev_dbg(dev->dev, "setting AMT thermals\n");
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +		dev->amt_running = true;
> +		return;
> +	}
> +
>  	/* Get the average moving average computed by auto mode algorithm */
>  	avg_power = amd_pmf_get_moving_avg(socket_power);
>  
> @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	}
>  }
>  
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
> +{
> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
> +			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
> +	if ((config_store.current_mode == AUTO_PERFORMANCE ||
> +	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
> +	    config_store.current_mode !=
> +	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
> +		config_store.current_mode =
> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +	}
> +	dev_dbg(dev->dev, "updated CQL thermals\n");
> +}
> +
>  static void amd_pmf_get_power_threshold(void)
>  {
>  	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> index 2b03ae1ad37f..eba8f0d79a62 100644
> --- a/drivers/platform/x86/amd/pmf/cnqf.c
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>  	}
>  }
>  
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>  {
>  	struct cnqf_tran_params *tp;
>  	int src, i, j, index = 0;
> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>  	}
>  
>  	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
> -		config_store.trans_param[src][i].timer += time_lapsed_ms;
> +		config_store.trans_param[src][i].timer += time_elapsed_ms;

This should be in squashed into the patch introducing the type. Each patch should
compile without errors individually (on top of previous patches obviously).

>  		config_store.trans_param[src][i].total_power += socket_power;
>  		config_store.trans_param[src][i].count++;
>  
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 674ddf599135..2a3dacfb2005 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	enum platform_profile_option current_profile;
>  	ktime_t time_elapsed_ms;
>  	int socket_power;
> +	u8 mode;
>  
>  	/* Get the current profile information */
>  	platform_profile_get(&current_profile);
>  
> +	if (!dev->is_amt_event)
> +		dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
> +			dev->is_amt_event ? "Enabled" : "Disabled");
> +
>  	/* Transfer table contents */
>  	memset(&dev->m_table, 0, sizeof(dev->m_table));
>  	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
>  	if (current_profile == PLATFORM_PROFILE_BALANCED) {
> -		/* Apply the Auto Mode transition */
> -		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		if (dev->is_amt_event) {
> +			/* Apply the Auto Mode transition */
> +			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		} else if (!dev->is_amt_event && dev->amt_running) {
> +			pr_debug("resetting AMT thermals\n");
> +			mode = amd_pmf_get_pprof_modes(dev);
> +			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
> +			dev->amt_running = false;
> +		}
> +	} else {
> +		dev->amt_running = false;
>  	}
>  
>  	if (dev->cnqf_feat) {
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 0532f49e9613..9ae9812353cd 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -17,6 +17,7 @@
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_SBIOS_REQUESTS			2
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_AUTO_MODE					5
>  #define APMF_FUNC_SET_FAN_IDX				7
> @@ -51,6 +52,7 @@
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
> +	bool sbios_requests;
>  	bool sbios_heartbeat;
>  	bool auto_mode_def;
>  	bool fan_table_idx;
> @@ -84,6 +86,24 @@ struct apmf_system_params {
>  	u32 heartbeat_int;
>  } __packed;
>  
> +struct apmf_sbios_req {
> +	u16 size;
> +	u32 pending_req;
> +	u8 rsd;
> +	u8 cql_event;
> +	u8 amt_event;
> +	u32 fppt;
> +	u32 sppt;
> +	u32 fppt_apu_only;
> +	u32 spl;
> +	u32 stt_min_limit;
> +	u8 skin_temp_apu;
> +	u8 skin_temp_hs2;
> +	u8 dps_enable;
> +	u32 custom_policy_1;
> +	u32 custom_policy_2;
> +} __packed;
> +
>  struct apmf_fan_idx {
>  	u16 size;
>  	u8 fan_ctl_mode;
> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>  #endif /* CONFIG_DEBUG_FS */
>  	bool cnqf_feat;
>  	bool cnqf_running;
> +	bool is_amt_event;
> +	bool is_cql_event;
> +	bool amt_running;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>  /* Auto Mode Layer */
>  void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>  int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>  void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  /* CnQF Layer */
>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  #endif /* PMF_H */

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms
  2022-07-12 14:58 ` [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms Shyam Sundar S K
@ 2022-07-27 21:40   ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:40 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> Some of the older platforms with _HID "AMDI0100" PMF driver can be force
> loaded by interested users but only for experimental purposes.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans

> ---
>  drivers/platform/x86/amd/pmf/core.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 2a3dacfb2005..ef50d67f6013 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -39,6 +39,7 @@
>  #define AMD_PMF_RESULT_FAILED                0xFF
>  
>  /* List of supported CPU ids */
> +#define AMD_CPU_ID_RMB			0x14b5
>  #define AMD_CPU_ID_PS			0x14e8
>  
>  #define PMF_MSG_DELAY_MIN_US		50
> @@ -52,6 +53,11 @@ static int metrics_table_loop_ms = 1000;
>  module_param(metrics_table_loop_ms, int, 0644);
>  MODULE_PARM_DESC(metrics_table_loop_ms, " Metrics Table sample size time (default = 1000ms) ");
>  
> +/* Force load on supported older platforms */
> +static bool force_load;
> +module_param(force_load, bool, 0444);
> +MODULE_PARM_DESC(force_load, " Force load this driver on supported older platforms (experimental) ");
> +
>  #ifdef CONFIG_DEBUG_FS
>  static int current_power_limits_show(struct seq_file *seq, void *unused)
>  {
> @@ -240,6 +246,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
>  }
>  
>  static const struct pci_device_id pmf_pci_ids[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) },
>  	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
>  	{ }
>  };
> @@ -313,6 +320,7 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>  }
>  
>  static const struct acpi_device_id amd_pmf_acpi_ids[] = {
> +	{"AMDI0100", 0x100},
>  	{"AMDI0102", 0},
>  	{ }
>  };
> @@ -320,6 +328,7 @@ MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
>  
>  static int amd_pmf_probe(struct platform_device *pdev)
>  {
> +	const struct acpi_device_id *id;
>  	struct amd_pmf_dev *dev;
>  	struct pci_dev *rdev;
>  	u32 base_addr_lo;
> @@ -328,6 +337,13 @@ static int amd_pmf_probe(struct platform_device *pdev)
>  	u32 val;
>  	int err;
>  
> +	id = acpi_match_device(amd_pmf_acpi_ids, &pdev->dev);
> +	if (!id)
> +		return -ENODEV;
> +
> +	if (id->driver_data == 0x100 && !force_load)
> +		return -ENODEV;
> +
>  	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
>  	if (!dev)
>  		return -ENOMEM;


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry
  2022-07-12 14:58 ` [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry Shyam Sundar S K
@ 2022-07-27 21:41   ` Hans de Goede
  2022-07-28 17:44     ` Shyam Sundar S K
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:41 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> Update the MAINTAINERS file with AMD PMF driver details.
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
>  MAINTAINERS | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e01478062c56..d3f6cabcaab2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -998,6 +998,13 @@ L:	platform-driver-x86@vger.kernel.org
>  S:	Maintained
>  F:	drivers/platform/x86/amd/pmc.c
>  
> +AMD PMF DRIVER
> +M:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> +L:	platform-driver-x86@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-amd-pmf
> +F:	drivers/platform/x86/amd/pmf/
> +
>  AMD HSMP DRIVER
>  M:	Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
>  R:	Carlos Bilbao <carlos.bilbao@amd.com>


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
  2022-07-27 21:33   ` Hans de Goede
@ 2022-07-27 21:44   ` Hans de Goede
  2022-07-27 21:46   ` Hans de Goede
  2 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:44 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Limonciello, Mario

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> The transition to auto-mode happens when the PMF driver receives
> AMT (Auto Mode transition) event. transition logic will reside in the
> PMF driver but the events would come from other supported drivers[1].
> 
> The thermal parameters would vary between when a performance "on-lap" mode
> is detected and versus when not. The CQL event would get triggered from
> other drivers, so that PMF driver would adjust the system thermal config
> based on the ACPI inputs.
> 
> OEMs can control whether or not to enable AMT or CQL via other supported
> drivers[1] but the actual transition logic resides in the AMD PMF driver.
> When an AMT event is received the automatic mode transition RAPL algorithm
> will run. When a CQL event is received an performance "on-lap" mode will
> be enabled and thermal parameters will be adjusted accordingly.
> 
> [1]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-hans&id=755b249250df1b612d982f3b702c831b26ecdf73
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>  drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>  drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>  drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>  drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>  5 files changed, 156 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index e9f33e61659f..4bde86aeafa0 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -12,6 +12,8 @@
>  #include "pmf.h"
>  
>  #define APMF_METHOD "\\_SB_.PMF_.APMF"
> +#define APMF_CQL_NOTIFICATION	2
> +#define APMF_AMT_NOTIFICATION	3
>  
>  static unsigned long supported_func;
>  
> @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
> +	func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>  	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  	return err;
>  }
>  
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(union acpi_object)) {
> +		pr_err("PMF: buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*req), size);
> +	memset(req, 0, sizeof(*req));
> +	memcpy(req, info->buffer.pointer, size);
> +
> +	pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
> +		 req->pending_req, req->cql_event, req->amt_event);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>  	return handle;
>  }
>  
> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
> +{
> +	struct amd_pmf_dev *pmf_dev = data;
> +	struct apmf_if *apmf_if = pmf_dev->apmf_if;
> +	int ret;
> +
> +	if (apmf_if->func.sbios_requests) {
> +		struct apmf_sbios_req req;
> +
> +		ret = apmf_get_sbios_requests(apmf_if, &req);
> +		if (ret) {
> +			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
> +			return;
> +		}
> +		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
> +			pr_debug("PMF: AMT is supported and notifications %s\n",
> +				 req.amt_event ? "Enabled" : "Disabled");
> +			if (req.amt_event)
> +				pmf_dev->is_amt_event = true;
> +			else
> +				pmf_dev->is_amt_event = !!req.amt_event;
> +		}
> +
> +		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
> +			pr_debug("PMF: CQL is supported and notifications %s\n",
> +				 req.cql_event ? "Enabled" : "Disabled");
> +			if (req.cql_event)
> +				pmf_dev->is_cql_event = true;
> +			else
> +				pmf_dev->is_cql_event = !!req.cql_event;
> +
> +			/* update the target mode information */
> +			amd_pmf_update_2_cql(pmf_dev);
> +		}
> +	}
> +}
> +
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
> +
>  	if (pmf_dev->apmf_if->func.sbios_heartbeat)
>  		cancel_delayed_work_sync(&pmf_dev->heart_beat);
> +
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
> +		acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
> +					   apmf_event_handler);
>  }
>  
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>  	struct apmf_notification_cfg *n;
>  	acpi_handle apmf_if_handle;
>  	struct apmf_if *apmf_if;
> -	int ret;
> +	int ret, status;
>  
>  	apmf_if_handle = apmf_if_probe_handle();
>  	if (!apmf_if_handle)
> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>  	}
>  
> +	/* Install the APMF Notify handler */
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
> +		status = acpi_install_notify_handler(ahandle,
> +						     ACPI_ALL_NOTIFY,
> +						     apmf_event_handler, pmf_dev);
> +		if (ACPI_FAILURE(status)) {
> +			dev_err(pmf_dev->dev, "failed to install notify handler\n");
> +			return -ENODEV;
> +		}
> +	}
> +
>  out:
>  	return ret;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
> index 954fde25e71e..a85ef4b95cdb 100644
> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	bool update = false;
>  	int i, j;
>  
> +	if (!dev->amt_running && dev->is_amt_event) {
> +		dev_dbg(dev->dev, "setting AMT thermals\n");
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +		dev->amt_running = true;
> +		return;
> +	}
> +
>  	/* Get the average moving average computed by auto mode algorithm */
>  	avg_power = amd_pmf_get_moving_avg(socket_power);
>  
> @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	}
>  }
>  
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
> +{
> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
> +			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
> +	if ((config_store.current_mode == AUTO_PERFORMANCE ||
> +	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
> +	    config_store.current_mode !=
> +	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
> +		config_store.current_mode =
> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +	}
> +	dev_dbg(dev->dev, "updated CQL thermals\n");
> +}
> +
>  static void amd_pmf_get_power_threshold(void)
>  {
>  	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> index 2b03ae1ad37f..eba8f0d79a62 100644
> --- a/drivers/platform/x86/amd/pmf/cnqf.c
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>  	}
>  }
>  
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>  {
>  	struct cnqf_tran_params *tp;
>  	int src, i, j, index = 0;
> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>  	}
>  
>  	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
> -		config_store.trans_param[src][i].timer += time_lapsed_ms;
> +		config_store.trans_param[src][i].timer += time_elapsed_ms;
>  		config_store.trans_param[src][i].total_power += socket_power;
>  		config_store.trans_param[src][i].count++;
>  
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 674ddf599135..2a3dacfb2005 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	enum platform_profile_option current_profile;
>  	ktime_t time_elapsed_ms;
>  	int socket_power;
> +	u8 mode;
>  
>  	/* Get the current profile information */
>  	platform_profile_get(&current_profile);
>  
> +	if (!dev->is_amt_event)
> +		dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
> +			dev->is_amt_event ? "Enabled" : "Disabled");
> +
>  	/* Transfer table contents */
>  	memset(&dev->m_table, 0, sizeof(dev->m_table));
>  	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
>  	if (current_profile == PLATFORM_PROFILE_BALANCED) {

Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?

Can you please explain the expected interactions between thinkpad_acpi and
this code here a bit more ?

Since we now only call amd_pmf_trans_automode() based on the AMT flag and
that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
to be cleared when the profile is not balanced and can we thus not just drop
the current_profile == PLATFORM_PROFILE_BALANCED check all together?

It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
then enable AMT, else disable it, logic belongs inside thinkpad_acpi
and not here?

Regards,

Hans

> -		/* Apply the Auto Mode transition */
> -		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		if (dev->is_amt_event) {
> +			/* Apply the Auto Mode transition */
> +			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		} else if (!dev->is_amt_event && dev->amt_running) {
> +			pr_debug("resetting AMT thermals\n");
> +			mode = amd_pmf_get_pprof_modes(dev);
> +			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
> +			dev->amt_running = false;
> +		}
> +	} else {
> +		dev->amt_running = false;
>  	}
>  
>  	if (dev->cnqf_feat) {
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 0532f49e9613..9ae9812353cd 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -17,6 +17,7 @@
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_SBIOS_REQUESTS			2
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_AUTO_MODE					5
>  #define APMF_FUNC_SET_FAN_IDX				7
> @@ -51,6 +52,7 @@
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
> +	bool sbios_requests;
>  	bool sbios_heartbeat;
>  	bool auto_mode_def;
>  	bool fan_table_idx;
> @@ -84,6 +86,24 @@ struct apmf_system_params {
>  	u32 heartbeat_int;
>  } __packed;
>  
> +struct apmf_sbios_req {
> +	u16 size;
> +	u32 pending_req;
> +	u8 rsd;
> +	u8 cql_event;
> +	u8 amt_event;
> +	u32 fppt;
> +	u32 sppt;
> +	u32 fppt_apu_only;
> +	u32 spl;
> +	u32 stt_min_limit;
> +	u8 skin_temp_apu;
> +	u8 skin_temp_hs2;
> +	u8 dps_enable;
> +	u32 custom_policy_1;
> +	u32 custom_policy_2;
> +} __packed;
> +
>  struct apmf_fan_idx {
>  	u16 size;
>  	u8 fan_ctl_mode;
> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>  #endif /* CONFIG_DEBUG_FS */
>  	bool cnqf_feat;
>  	bool cnqf_running;
> +	bool is_amt_event;
> +	bool is_cql_event;
> +	bool amt_running;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>  /* Auto Mode Layer */
>  void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>  int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>  void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  /* CnQF Layer */
>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  #endif /* PMF_H */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
  2022-07-27 21:33   ` Hans de Goede
  2022-07-27 21:44   ` Hans de Goede
@ 2022-07-27 21:46   ` Hans de Goede
  2022-07-27 23:52     ` Limonciello, Mario
  2 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-27 21:46 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Limonciello, Mario, Mark Pearson

<resend with Cc list fixed>

Hi,

On 7/12/22 16:58, Shyam Sundar S K wrote:
> The transition to auto-mode happens when the PMF driver receives
> AMT (Auto Mode transition) event. transition logic will reside in the
> PMF driver but the events would come from other supported drivers[1].
> 
> The thermal parameters would vary between when a performance "on-lap" mode
> is detected and versus when not. The CQL event would get triggered from
> other drivers, so that PMF driver would adjust the system thermal config
> based on the ACPI inputs.
> 
> OEMs can control whether or not to enable AMT or CQL via other supported
> drivers[1] but the actual transition logic resides in the AMD PMF driver.
> When an AMT event is received the automatic mode transition RAPL algorithm
> will run. When a CQL event is received an performance "on-lap" mode will
> be enabled and thermal parameters will be adjusted accordingly.
> 
> [1]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-hans&id=755b249250df1b612d982f3b702c831b26ecdf73
> 
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>  drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>  drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>  drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>  drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>  5 files changed, 156 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index e9f33e61659f..4bde86aeafa0 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -12,6 +12,8 @@
>  #include "pmf.h"
>  
>  #define APMF_METHOD "\\_SB_.PMF_.APMF"
> +#define APMF_CQL_NOTIFICATION	2
> +#define APMF_AMT_NOTIFICATION	3
>  
>  static unsigned long supported_func;
>  
> @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>  {
>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
> +	func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>  	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  	return err;
>  }
>  
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
> +{
> +	union acpi_object *info;
> +	size_t size;
> +	int err = 0;
> +
> +	info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
> +	if (!info)
> +		return -EIO;
> +
> +	size = *(u16 *)info->buffer.pointer;
> +
> +	if (size < sizeof(union acpi_object)) {
> +		pr_err("PMF: buffer too small %zu\n", size);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	size = min(sizeof(*req), size);
> +	memset(req, 0, sizeof(*req));
> +	memcpy(req, info->buffer.pointer, size);
> +
> +	pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
> +		 req->pending_req, req->cql_event, req->amt_event);
> +
> +out:
> +	kfree(info);
> +	return err;
> +}
> +
>  static acpi_handle apmf_if_probe_handle(void)
>  {
>  	acpi_handle handle = NULL;
> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>  	return handle;
>  }
>  
> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
> +{
> +	struct amd_pmf_dev *pmf_dev = data;
> +	struct apmf_if *apmf_if = pmf_dev->apmf_if;
> +	int ret;
> +
> +	if (apmf_if->func.sbios_requests) {
> +		struct apmf_sbios_req req;
> +
> +		ret = apmf_get_sbios_requests(apmf_if, &req);
> +		if (ret) {
> +			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
> +			return;
> +		}
> +		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
> +			pr_debug("PMF: AMT is supported and notifications %s\n",
> +				 req.amt_event ? "Enabled" : "Disabled");
> +			if (req.amt_event)
> +				pmf_dev->is_amt_event = true;
> +			else
> +				pmf_dev->is_amt_event = !!req.amt_event;
> +		}
> +
> +		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
> +			pr_debug("PMF: CQL is supported and notifications %s\n",
> +				 req.cql_event ? "Enabled" : "Disabled");
> +			if (req.cql_event)
> +				pmf_dev->is_cql_event = true;
> +			else
> +				pmf_dev->is_cql_event = !!req.cql_event;
> +
> +			/* update the target mode information */
> +			amd_pmf_update_2_cql(pmf_dev);
> +		}
> +	}
> +}
> +
>  void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
> +
>  	if (pmf_dev->apmf_if->func.sbios_heartbeat)
>  		cancel_delayed_work_sync(&pmf_dev->heart_beat);
> +
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
> +		acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
> +					   apmf_event_handler);
>  }
>  
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  {
> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>  	struct apmf_notification_cfg *n;
>  	acpi_handle apmf_if_handle;
>  	struct apmf_if *apmf_if;
> -	int ret;
> +	int ret, status;
>  
>  	apmf_if_handle = apmf_if_probe_handle();
>  	if (!apmf_if_handle)
> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>  	}
>  
> +	/* Install the APMF Notify handler */
> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
> +		status = acpi_install_notify_handler(ahandle,
> +						     ACPI_ALL_NOTIFY,
> +						     apmf_event_handler, pmf_dev);
> +		if (ACPI_FAILURE(status)) {
> +			dev_err(pmf_dev->dev, "failed to install notify handler\n");
> +			return -ENODEV;
> +		}
> +	}
> +
>  out:
>  	return ret;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
> index 954fde25e71e..a85ef4b95cdb 100644
> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	bool update = false;
>  	int i, j;
>  
> +	if (!dev->amt_running && dev->is_amt_event) {
> +		dev_dbg(dev->dev, "setting AMT thermals\n");
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +		dev->amt_running = true;
> +		return;
> +	}
> +
>  	/* Get the average moving average computed by auto mode algorithm */
>  	avg_power = amd_pmf_get_moving_avg(socket_power);
>  
> @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>  	}
>  }
>  
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
> +{
> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
> +			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
> +	if ((config_store.current_mode == AUTO_PERFORMANCE ||
> +	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
> +	    config_store.current_mode !=
> +	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
> +		config_store.current_mode =
> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
> +	}
> +	dev_dbg(dev->dev, "updated CQL thermals\n");
> +}
> +
>  static void amd_pmf_get_power_threshold(void)
>  {
>  	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
> index 2b03ae1ad37f..eba8f0d79a62 100644
> --- a/drivers/platform/x86/amd/pmf/cnqf.c
> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>  	}
>  }
>  
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>  {
>  	struct cnqf_tran_params *tp;
>  	int src, i, j, index = 0;
> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>  	}
>  
>  	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
> -		config_store.trans_param[src][i].timer += time_lapsed_ms;
> +		config_store.trans_param[src][i].timer += time_elapsed_ms;
>  		config_store.trans_param[src][i].total_power += socket_power;
>  		config_store.trans_param[src][i].count++;
>  
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 674ddf599135..2a3dacfb2005 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	enum platform_profile_option current_profile;
>  	ktime_t time_elapsed_ms;
>  	int socket_power;
> +	u8 mode;
>  
>  	/* Get the current profile information */
>  	platform_profile_get(&current_profile);
>  
> +	if (!dev->is_amt_event)
> +		dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
> +			dev->is_amt_event ? "Enabled" : "Disabled");
> +
>  	/* Transfer table contents */
>  	memset(&dev->m_table, 0, sizeof(dev->m_table));
>  	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
>  	if (current_profile == PLATFORM_PROFILE_BALANCED) {

Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?

Can you please explain the expected interactions between thinkpad_acpi and
this code here a bit more ?

Since we now only call amd_pmf_trans_automode() based on the AMT flag and
that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
to be cleared when the profile is not balanced and can we thus not just drop
the current_profile == PLATFORM_PROFILE_BALANCED check all together?

It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
then enable AMT, else disable it, logic belongs inside thinkpad_acpi
and not here?

Regards,

Hans

> -		/* Apply the Auto Mode transition */
> -		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		if (dev->is_amt_event) {
> +			/* Apply the Auto Mode transition */
> +			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		} else if (!dev->is_amt_event && dev->amt_running) {
> +			pr_debug("resetting AMT thermals\n");
> +			mode = amd_pmf_get_pprof_modes(dev);
> +			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
> +			dev->amt_running = false;
> +		}
> +	} else {
> +		dev->amt_running = false;
>  	}
>  
>  	if (dev->cnqf_feat) {
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 0532f49e9613..9ae9812353cd 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -17,6 +17,7 @@
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_SBIOS_REQUESTS			2
>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_AUTO_MODE					5
>  #define APMF_FUNC_SET_FAN_IDX				7
> @@ -51,6 +52,7 @@
>  /* AMD PMF BIOS interfaces */
>  struct apmf_if_functions {
>  	bool system_params;
> +	bool sbios_requests;
>  	bool sbios_heartbeat;
>  	bool auto_mode_def;
>  	bool fan_table_idx;
> @@ -84,6 +86,24 @@ struct apmf_system_params {
>  	u32 heartbeat_int;
>  } __packed;
>  
> +struct apmf_sbios_req {
> +	u16 size;
> +	u32 pending_req;
> +	u8 rsd;
> +	u8 cql_event;
> +	u8 amt_event;
> +	u32 fppt;
> +	u32 sppt;
> +	u32 fppt_apu_only;
> +	u32 spl;
> +	u32 stt_min_limit;
> +	u8 skin_temp_apu;
> +	u8 skin_temp_hs2;
> +	u8 dps_enable;
> +	u32 custom_policy_1;
> +	u32 custom_policy_2;
> +} __packed;
> +
>  struct apmf_fan_idx {
>  	u16 size;
>  	u8 fan_ctl_mode;
> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>  #endif /* CONFIG_DEBUG_FS */
>  	bool cnqf_feat;
>  	bool cnqf_running;
> +	bool is_amt_event;
> +	bool is_cql_event;
> +	bool amt_running;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>  /* Auto Mode Layer */
>  void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>  int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>  void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  /* CnQF Layer */
>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>  void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>  void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>  
>  #endif /* PMF_H */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-27 21:46   ` Hans de Goede
@ 2022-07-27 23:52     ` Limonciello, Mario
  2022-07-28 13:03       ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-27 23:52 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

On 7/27/2022 16:46, Hans de Goede wrote:
> <resend with Cc list fixed>
> 
> Hi,
> 
> On 7/12/22 16:58, Shyam Sundar S K wrote:
>> The transition to auto-mode happens when the PMF driver receives
>> AMT (Auto Mode transition) event. transition logic will reside in the
>> PMF driver but the events would come from other supported drivers[1].
>>
>> The thermal parameters would vary between when a performance "on-lap" mode
>> is detected and versus when not. The CQL event would get triggered from
>> other drivers, so that PMF driver would adjust the system thermal config
>> based on the ACPI inputs.
>>
>> OEMs can control whether or not to enable AMT or CQL via other supported
>> drivers[1] but the actual transition logic resides in the AMD PMF driver.
>> When an AMT event is received the automatic mode transition RAPL algorithm
>> will run. When a CQL event is received an performance "on-lap" mode will
>> be enabled and thermal parameters will be adjusted accordingly.
>>
>> [1]
>> Link: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Fcommit%2F%3Fh%3Dreview-hans%26id%3D755b249250df1b612d982f3b702c831b26ecdf73&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cd72d8627f7ad4088aff308da7019afbf%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637945552890705546%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=4gLIOjMo%2FYO%2BkKdqtk4pcLHlsBrLiUb41cTKvuYrcHQ%3D&amp;reserved=0
>>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>   drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>>   drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>>   drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>>   drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>>   drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>>   5 files changed, 156 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>> index e9f33e61659f..4bde86aeafa0 100644
>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>> @@ -12,6 +12,8 @@
>>   #include "pmf.h"
>>   
>>   #define APMF_METHOD "\\_SB_.PMF_.APMF"
>> +#define APMF_CQL_NOTIFICATION	2
>> +#define APMF_AMT_NOTIFICATION	3
>>   
>>   static unsigned long supported_func;
>>   
>> @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>   {
>>   	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>   	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>> +	func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>>   	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>   	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>   	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>   	return err;
>>   }
>>   
>> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
>> +{
>> +	union acpi_object *info;
>> +	size_t size;
>> +	int err = 0;
>> +
>> +	info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
>> +	if (!info)
>> +		return -EIO;
>> +
>> +	size = *(u16 *)info->buffer.pointer;
>> +
>> +	if (size < sizeof(union acpi_object)) {
>> +		pr_err("PMF: buffer too small %zu\n", size);
>> +		err = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	size = min(sizeof(*req), size);
>> +	memset(req, 0, sizeof(*req));
>> +	memcpy(req, info->buffer.pointer, size);
>> +
>> +	pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
>> +		 req->pending_req, req->cql_event, req->amt_event);
>> +
>> +out:
>> +	kfree(info);
>> +	return err;
>> +}
>> +
>>   static acpi_handle apmf_if_probe_handle(void)
>>   {
>>   	acpi_handle handle = NULL;
>> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>>   	return handle;
>>   }
>>   
>> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>> +{
>> +	struct amd_pmf_dev *pmf_dev = data;
>> +	struct apmf_if *apmf_if = pmf_dev->apmf_if;
>> +	int ret;
>> +
>> +	if (apmf_if->func.sbios_requests) {
>> +		struct apmf_sbios_req req;
>> +
>> +		ret = apmf_get_sbios_requests(apmf_if, &req);
>> +		if (ret) {
>> +			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>> +			return;
>> +		}
>> +		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>> +			pr_debug("PMF: AMT is supported and notifications %s\n",
>> +				 req.amt_event ? "Enabled" : "Disabled");
>> +			if (req.amt_event)
>> +				pmf_dev->is_amt_event = true;
>> +			else
>> +				pmf_dev->is_amt_event = !!req.amt_event;
>> +		}
>> +
>> +		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>> +			pr_debug("PMF: CQL is supported and notifications %s\n",
>> +				 req.cql_event ? "Enabled" : "Disabled");
>> +			if (req.cql_event)
>> +				pmf_dev->is_cql_event = true;
>> +			else
>> +				pmf_dev->is_cql_event = !!req.cql_event;
>> +
>> +			/* update the target mode information */
>> +			amd_pmf_update_2_cql(pmf_dev);
>> +		}
>> +	}
>> +}
>> +
>>   void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>>   {
>> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>> +
>>   	if (pmf_dev->apmf_if->func.sbios_heartbeat)
>>   		cancel_delayed_work_sync(&pmf_dev->heart_beat);
>> +
>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
>> +		acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
>> +					   apmf_event_handler);
>>   }
>>   
>>   int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>   {
>> +	acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>   	struct apmf_notification_cfg *n;
>>   	acpi_handle apmf_if_handle;
>>   	struct apmf_if *apmf_if;
>> -	int ret;
>> +	int ret, status;
>>   
>>   	apmf_if_handle = apmf_if_probe_handle();
>>   	if (!apmf_if_handle)
>> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>   		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>>   	}
>>   
>> +	/* Install the APMF Notify handler */
>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>> +		status = acpi_install_notify_handler(ahandle,
>> +						     ACPI_ALL_NOTIFY,
>> +						     apmf_event_handler, pmf_dev);
>> +		if (ACPI_FAILURE(status)) {
>> +			dev_err(pmf_dev->dev, "failed to install notify handler\n");
>> +			return -ENODEV;
>> +		}
>> +	}
>> +
>>   out:
>>   	return ret;
>>   }
>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>> index 954fde25e71e..a85ef4b95cdb 100644
>> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>   	bool update = false;
>>   	int i, j;
>>   
>> +	if (!dev->amt_running && dev->is_amt_event) {
>> +		dev_dbg(dev->dev, "setting AMT thermals\n");
>> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>> +		dev->amt_running = true;
>> +		return;
>> +	}
>> +
>>   	/* Get the average moving average computed by auto mode algorithm */
>>   	avg_power = amd_pmf_get_moving_avg(socket_power);
>>   
>> @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>   	}
>>   }
>>   
>> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
>> +{
>> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>> +			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
>> +	if ((config_store.current_mode == AUTO_PERFORMANCE ||
>> +	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
>> +	    config_store.current_mode !=
>> +	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
>> +		config_store.current_mode =
>> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
>> +		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>> +	}
>> +	dev_dbg(dev->dev, "updated CQL thermals\n");
>> +}
>> +
>>   static void amd_pmf_get_power_threshold(void)
>>   {
>>   	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
>> index 2b03ae1ad37f..eba8f0d79a62 100644
>> --- a/drivers/platform/x86/amd/pmf/cnqf.c
>> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
>> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>>   	}
>>   }
>>   
>> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>>   {
>>   	struct cnqf_tran_params *tp;
>>   	int src, i, j, index = 0;
>> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>>   	}
>>   
>>   	for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
>> -		config_store.trans_param[src][i].timer += time_lapsed_ms;
>> +		config_store.trans_param[src][i].timer += time_elapsed_ms;
>>   		config_store.trans_param[src][i].total_power += socket_power;
>>   		config_store.trans_param[src][i].count++;
>>   
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index 674ddf599135..2a3dacfb2005 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>   	enum platform_profile_option current_profile;
>>   	ktime_t time_elapsed_ms;
>>   	int socket_power;
>> +	u8 mode;
>>   
>>   	/* Get the current profile information */
>>   	platform_profile_get(&current_profile);
>>   
>> +	if (!dev->is_amt_event)
>> +		dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
>> +			dev->is_amt_event ? "Enabled" : "Disabled");
>> +
>>   	/* Transfer table contents */
>>   	memset(&dev->m_table, 0, sizeof(dev->m_table));
>>   	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
>> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>   	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>   
>>   	if (current_profile == PLATFORM_PROFILE_BALANCED) {
> 
> Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
> on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
> then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?
> 
> Can you please explain the expected interactions between thinkpad_acpi and
> this code here a bit more ?
> 
> Since we now only call amd_pmf_trans_automode() based on the AMT flag and
> that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
> to be cleared when the profile is not balanced and can we thus not just drop
> the current_profile == PLATFORM_PROFILE_BALANCED check all together?
> 
> It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
> then enable AMT, else disable it, logic belongs inside thinkpad_acpi
> and not here?
> 

It actually already lives in thinkpad_acpi.

https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/tree/drivers/platform/x86/thinkpad_acpi.c?h=review-hans#n10489

By the control point from thinkpad_acpi BIOS events will be emitted 
controlling whether AMT is running in a given mode.

So yes; there is no need for this here anymore.

> Regards,
> 
> Hans
> 
>> -		/* Apply the Auto Mode transition */
>> -		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>> +		if (dev->is_amt_event) {
>> +			/* Apply the Auto Mode transition */
>> +			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>> +		} else if (!dev->is_amt_event && dev->amt_running) {
>> +			pr_debug("resetting AMT thermals\n");
>> +			mode = amd_pmf_get_pprof_modes(dev);
>> +			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
>> +			dev->amt_running = false;
>> +		}
>> +	} else {
>> +		dev->amt_running = false;
>>   	}
>>   
>>   	if (dev->cnqf_feat) {
>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>> index 0532f49e9613..9ae9812353cd 100644
>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>> @@ -17,6 +17,7 @@
>>   /* APMF Functions */
>>   #define APMF_FUNC_VERIFY_INTERFACE			0
>>   #define APMF_FUNC_GET_SYS_PARAMS			1
>> +#define APMF_FUNC_SBIOS_REQUESTS			2
>>   #define APMF_FUNC_SBIOS_HEARTBEAT			4
>>   #define APMF_FUNC_AUTO_MODE					5
>>   #define APMF_FUNC_SET_FAN_IDX				7
>> @@ -51,6 +52,7 @@
>>   /* AMD PMF BIOS interfaces */
>>   struct apmf_if_functions {
>>   	bool system_params;
>> +	bool sbios_requests;
>>   	bool sbios_heartbeat;
>>   	bool auto_mode_def;
>>   	bool fan_table_idx;
>> @@ -84,6 +86,24 @@ struct apmf_system_params {
>>   	u32 heartbeat_int;
>>   } __packed;
>>   
>> +struct apmf_sbios_req {
>> +	u16 size;
>> +	u32 pending_req;
>> +	u8 rsd;
>> +	u8 cql_event;
>> +	u8 amt_event;
>> +	u32 fppt;
>> +	u32 sppt;
>> +	u32 fppt_apu_only;
>> +	u32 spl;
>> +	u32 stt_min_limit;
>> +	u8 skin_temp_apu;
>> +	u8 skin_temp_hs2;
>> +	u8 dps_enable;
>> +	u32 custom_policy_1;
>> +	u32 custom_policy_2;
>> +} __packed;
>> +
>>   struct apmf_fan_idx {
>>   	u16 size;
>>   	u8 fan_ctl_mode;
>> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>>   #endif /* CONFIG_DEBUG_FS */
>>   	bool cnqf_feat;
>>   	bool cnqf_running;
>> +	bool is_amt_event;
>> +	bool is_cql_event;
>> +	bool amt_running;
>>   };
>>   
>>   struct apmf_sps_prop_granular {
>> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>   /* Auto Mode Layer */
>>   void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>>   int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>>   void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>>   void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>   
>>   /* CnQF Layer */
>>   int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>   void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>>   void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>>   void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
>> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>   
>>   #endif /* PMF_H */
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature
  2022-07-27 21:22   ` Hans de Goede
@ 2022-07-28 12:57     ` Shyam Sundar S K
  2022-07-28 13:15       ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-28 12:57 UTC (permalink / raw)
  To: Hans de Goede, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi Hans,

On 7/28/2022 2:52 AM, Hans de Goede wrote:
> Hi,
> 
> On 7/12/22 16:58, Shyam Sundar S K wrote:
>> The objective of this feature is to track the moving average of system
>> power over the time period specified and switch to the subsequent mode.
>>
>> This feature has 3 modes: quiet, balanced, performance.
>>
>> In order to do this, PMF driver will get the moving average of APU power
>> from PMFW and power threshold, time constants, system config parameters
>> from OEM inputs.
>>
>> System power as read by PMF driver from PMFW is the filtered value over
>> the sampling window. Every sampling window, moving average of system power
>> is computed. At the end of the monitoring window, the moving average is
>> compared against the threshold for mode switch for decision making.
>>
>> With AMD managing the system config limits, any mode switch within
>> auto-mode will result in limits of fPPT/sPPT/STAPM or STT being scaled
>> down.
>>
>> When "auto mode" is enabled, the static slider control remains out of
>> the PMF driver, so the platform_profile registration would not
>> happen in PMF driver.
> 
> This is not what happens in the code, the platform_profile registration
> is still happening AFAICT.

platform_profile_register gets called in amd_pmf_init_sps() and it gets
triggered only when APMF_FUNC_STATIC_SLIDER_GRANULAR (fn9) is enabled in
the BIOS.

OEMs have been already told in the BIOS implementation guide that, if
the "Auto Mode" is enabled, the "Static platform_profile" registration
shall happen at the OEM driver side. In this case, it will be
thinkpad_acpi which shall do the "platform_profile_register".

> 
> Like with CnQF for this other auto mode we also need to figure out the
> relation with the regular static platform_profile settings.
> 
> I see this in the code:
> 
> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>  	/* Calculate the avg SoC power consumption */
>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>  
> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
> +		/* Apply the Auto Mode transition */
> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +	}
> +
> 
> So it seems that unlike CnQF this other auto-mode only does mode transitions
> when the static-slider is set to balanced ?

CnQF and Auto-mode are complimentary to eachother. The key difference
between them are:

1. CnQF will have 4 internal modes (low-power, balanced, performance,
turbo) and Auto-mode will not have the turbo mode.

2. CnQF will be enabled only when the APCI call to the BIOS function
returns a spl flag which gets set from the BIOS side and driver sees it.
That is when the driver activates the CnQF. Irrespective of the
platform_profile choice selection, if the CnQF is enabled the values
passed in the BIOS tables for static slider are ignored.

3. Auto-mode will get activated only when an AMT ON event (which will be
sent by thinkpad_acpi driver in this case) is received and the
platform_profile is set to balanced. In other modes it keeps turned off.

4. The sampling window, the threshold calculation mechanism and the
power floor measurements are different w.r.t to CnQF and auto-mode.

5. OEMs are free to choose what kind of optimizations they are looking
for by enabling one or more of such features within PMF.

Hope it clarifies :-)

Thanks,
Shyam

> 
> 
>>
>> The transition to auto-mode only happens when the APMF fn5 is enabled
>> in BIOS, platform_profile set to "balanced" and a AMT
>> (Auto Mode transition) is received.
> 
> Right, this part of the commit msg matches the code, but it conflicts with
> the "When "auto mode" is enabled, the static slider control remains out of
> the PMF driver:" part of the commit msg above.
> 
>>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>  drivers/platform/x86/amd/pmf/Makefile    |   2 +-
>>  drivers/platform/x86/amd/pmf/acpi.c      |  28 ++
>>  drivers/platform/x86/amd/pmf/auto-mode.c | 317 +++++++++++++++++++++++
>>  drivers/platform/x86/amd/pmf/core.c      |  25 +-
>>  drivers/platform/x86/amd/pmf/pmf.h       | 104 ++++++++
>>  5 files changed, 473 insertions(+), 3 deletions(-)
>>  create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c
>>
>> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
>> index d02a0bdc6429..2a9568bf9064 100644
>> --- a/drivers/platform/x86/amd/pmf/Makefile
>> +++ b/drivers/platform/x86/amd/pmf/Makefile
>> @@ -6,4 +6,4 @@
>>  
>>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
>>  amd-pmf-objs := core.o acpi.o sps.o \
>> -		cnqf.o
>> +		cnqf.o auto-mode.o
>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>> index a3ff91c605b5..e9f33e61659f 100644
>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>> @@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>  {
>>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>> +	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>  	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
>> @@ -210,6 +211,33 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>>  	return err;
>>  }
>>  
>> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data)
>> +{
>> +	union acpi_object *info;
>> +	size_t size;
>> +	int err = 0;
>> +
>> +	info = apmf_if_call(ampf_if, APMF_FUNC_AUTO_MODE, NULL);
>> +	if (!info)
>> +		return -EIO;
>> +
>> +	size = *(u16 *)info->buffer.pointer;
>> +
>> +	if (size < sizeof(*data)) {
>> +		pr_debug("buffer too small %zu\n", size);
>> +		err = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	size = min(sizeof(*data), size);
>> +	memset(data, 0, sizeof(*data));
>> +	memcpy(data, info->buffer.pointer, size);
>> +
>> +out:
>> +	kfree(info);
>> +	return err;
>> +}
>> +
> 
> Please use apmf_if_call_store_buffer() for this (see review of 4/15)
> 
> Regards,
> 
> Hans
> 
> 
>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
>>  {
>>  	union acpi_object *info;
>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>> new file mode 100644
>> index 000000000000..954fde25e71e
>> --- /dev/null
>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>> @@ -0,0 +1,317 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * AMD Platform Management Framework Driver
>> + *
>> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
>> + * All Rights Reserved.
>> + *
>> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/list.h>
>> +#include <linux/workqueue.h>
>> +#include "pmf.h"
>> +
>> +#define AVG_SAMPLE_SIZE 3
>> +
>> +struct power_history {
>> +	int socket_power;
>> +	struct list_head list;
>> +	int avg;
>> +	int total;
>> +};
>> +
>> +struct list_pdata {
>> +	int total;
>> +	int avg;
>> +};
>> +
>> +static struct power_history power_list;
>> +static struct list_pdata pdata;
>> +
>> +static struct auto_mode_mode_config config_store;
>> +static const char *state_as_str(unsigned int state);
>> +
>> +static void amd_pmf_handle_automode(struct amd_pmf_dev *dev, bool op, int idx,
>> +				    struct auto_mode_mode_config *table)
>> +{
>> +	if (op == SLIDER_OP_SET) {
>> +		struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control;
>> +
>> +		amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL);
>> +		amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL);
>> +		amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL);
>> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
>> +	} else if (op == SLIDER_OP_GET) {
>> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.spl);
>> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.fppt);
>> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.sppt);
>> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.sppt_apu_only);
>> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.stt_min);
>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_APU]);
>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_HS2]);
>> +	}
>> +
>> +	if (dev->apmf_if->func.fan_table_idx)
>> +		apmf_update_fan_idx(dev->apmf_if, config_store.mode_set[idx].fan_control.manual,
>> +				    config_store.mode_set[idx].fan_control.fan_id);
>> +}
>> +
>> +static int amd_pmf_get_moving_avg(int socket_power)
>> +{
>> +	struct power_history *tmp;
>> +	struct list_head *pos, *q;
>> +	static int count;
>> +
>> +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
>> +	tmp->socket_power = socket_power;
>> +	list_add_tail(&tmp->list, &power_list.list);
>> +
>> +	list_for_each_safe(pos, q, &power_list.list) {
>> +		if (count >= AVG_SAMPLE_SIZE) {
>> +			tmp = list_first_entry(pos, struct power_history, list);
>> +			list_del_init(pos);
>> +			goto next;
>> +		}
>> +	}
>> +
>> +next:
>> +	pdata.total = 0;
>> +	pdata.avg = 0;
>> +
>> +	list_for_each(pos, &power_list.list) {
>> +		tmp = list_entry(pos, struct power_history, list);
>> +		pdata.total += tmp->socket_power;
>> +		pdata.avg = pdata.total / AVG_SAMPLE_SIZE;
>> +	}
>> +
>> +	count++;
>> +	if (count >= AVG_SAMPLE_SIZE)
>> +		return pdata.avg;
>> +
>> +	return 0;
>> +}
>> +
>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>> +{
>> +	int avg_power = 0;
>> +	bool update = false;
>> +	int i, j;
>> +
>> +	/* Get the average moving average computed by auto mode algorithm */
>> +	avg_power = amd_pmf_get_moving_avg(socket_power);
>> +
>> +	for (i = 0; i < AUTO_TRANSITION_MAX; i++) {
>> +		if ((config_store.transition[i].shifting_up && avg_power >=
>> +		     config_store.transition[i].power_threshold) ||
>> +		    (!config_store.transition[i].shifting_up && avg_power <=
>> +		     config_store.transition[i].power_threshold)) {
>> +			if (config_store.transition[i].timer <
>> +			    config_store.transition[i].time_constant)
>> +				config_store.transition[i].timer += time_elapsed_ms;
>> +		} else {
>> +			config_store.transition[i].timer = 0;
>> +		}
>> +
>> +		if (config_store.transition[i].timer >=
>> +		    config_store.transition[i].time_constant &&
>> +		    !config_store.transition[i].applied) {
>> +			config_store.transition[i].applied = true;
>> +			update = true;
>> +		} else if (config_store.transition[i].timer <=
>> +			   config_store.transition[i].time_constant &&
>> +			   config_store.transition[i].applied) {
>> +			config_store.transition[i].applied = false;
>> +			update = true;
>> +		}
>> +	}
>> +
>> +	dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power,
>> +		state_as_str(config_store.current_mode));
>> +
>> +	if (update) {
>> +		for (j = 0; j < AUTO_TRANSITION_MAX; j++) {
>> +			/* Apply the mode with highest priority indentified */
>> +			if (config_store.transition[j].applied) {
>> +				if (config_store.current_mode !=
>> +				    config_store.transition[j].target_mode) {
>> +					config_store.current_mode =
>> +							config_store.transition[j].target_mode;
>> +					dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n",
>> +						state_as_str(config_store.current_mode));
>> +					amd_pmf_handle_automode(dev, SLIDER_OP_SET,
>> +								config_store.current_mode, NULL);
>> +				}
>> +				break;
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +static void amd_pmf_get_power_threshold(void)
>> +{
>> +	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>> +				config_store.mode_set[AUTO_BALANCE].power_floor -
>> +				config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta;
>> +
>> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold =
>> +				config_store.mode_set[AUTO_BALANCE].power_floor -
>> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta;
>> +
>> +	config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold =
>> +			config_store.mode_set[AUTO_QUIET].power_floor -
>> +			config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta;
>> +
>> +	config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold =
>> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor -
>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta;
>> +}
>> +
>> +static const char *state_as_str(unsigned int state)
>> +{
>> +	switch (state) {
>> +	case AUTO_QUIET:
>> +		return "QUIET";
>> +	case AUTO_BALANCE:
>> +		return "BALANCED";
>> +	case AUTO_PERFORMANCE_ON_LAP:
>> +		return "ON_LAP";
>> +	case AUTO_PERFORMANCE:
>> +		return "PERFORMANCE";
>> +	default:
>> +		return "Unknown Auto Mode State";
>> +	}
>> +}
>> +
>> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev)
>> +{
>> +	struct apmf_auto_mode output;
>> +	struct power_table_control *pwr_ctrl;
>> +	int i;
>> +
>> +	if (dev->apmf_if->func.auto_mode_def) {
>> +		apmf_get_auto_mode_def(dev->apmf_if, &output);
>> +		/* time constant */
>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant =
>> +									output.balanced_to_quiet;
>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant =
>> +									output.balanced_to_perf;
>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant =
>> +									output.quiet_to_balanced;
>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant =
>> +									output.perf_to_balanced;
>> +
>> +		/* power floor */
>> +		config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet;
>> +		config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced;
>> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf;
>> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf;
>> +
>> +		/* Power delta for mode change */
>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta =
>> +									output.pd_balanced_to_quiet;
>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta =
>> +									output.pd_balanced_to_perf;
>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta =
>> +									output.pd_quiet_to_balanced;
>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta =
>> +									output.pd_perf_to_balanced;
>> +
>> +		/* Power threshold */
>> +		amd_pmf_get_power_threshold();
>> +
>> +		/* skin temperature limits */
>> +		pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control;
>> +		pwr_ctrl->spl = output.spl_quiet;
>> +		pwr_ctrl->sppt = output.sppt_quiet;
>> +		pwr_ctrl->fppt = output.fppt_quiet;
>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet;
>> +		pwr_ctrl->stt_min = output.stt_min_limit_quiet;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet;
>> +
>> +		pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control;
>> +		pwr_ctrl->spl = output.spl_balanced;
>> +		pwr_ctrl->sppt = output.sppt_balanced;
>> +		pwr_ctrl->fppt = output.fppt_balanced;
>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced;
>> +		pwr_ctrl->stt_min = output.stt_min_limit_balanced;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced;
>> +
>> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control;
>> +		pwr_ctrl->spl = output.spl_perf;
>> +		pwr_ctrl->sppt = output.sppt_perf;
>> +		pwr_ctrl->fppt = output.fppt_perf;
>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf;
>> +		pwr_ctrl->stt_min = output.stt_min_limit_perf;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf;
>> +
>> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control;
>> +		pwr_ctrl->spl = output.spl_perf_on_lap;
>> +		pwr_ctrl->sppt = output.sppt_perf_on_lap;
>> +		pwr_ctrl->fppt = output.fppt_perf_on_lap;
>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap;
>> +		pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap;
>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap;
>> +
>> +		/* Fan ID */
>> +		config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet;
>> +		config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced;
>> +		config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf;
>> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id =
>> +										output.fan_id_perf;
>> +
>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET;
>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>> +									AUTO_PERFORMANCE;
>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode =
>> +										AUTO_BALANCE;
>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode =
>> +										AUTO_BALANCE;
>> +
>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false;
>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true;
>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true;
>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up =
>> +											false;
>> +
>> +		for (i = 0 ; i < AUTO_MODE_MAX ; i++) {
>> +			if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO)
>> +				config_store.mode_set[i].fan_control.manual = false;
>> +			else
>> +				config_store.mode_set[i].fan_control.manual = true;
>> +		}
>> +	}
>> +}
>> +
>> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev)
>> +{
>> +	cancel_delayed_work_sync(&dev->work_buffer);
>> +}
>> +
>> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev)
>> +{
>> +	INIT_LIST_HEAD(&power_list.list);
>> +
>> +	amd_pmf_init_metrics_table(dev);
>> +	amd_pmf_load_defaults_auto_mode(dev);
>> +
>> +	/* update the thermal for Automode */
>> +	amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>> +}
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index bc267d333b76..674ddf599135 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>  	/* Calculate the avg SoC power consumption */
>>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>  
>> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
>> +		/* Apply the Auto Mode transition */
>> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>> +	}
>> +
>>  	if (dev->cnqf_feat) {
>>  		/* Apply the CnQF transition */
>>  		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
>> @@ -260,9 +265,18 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>>  		amd_pmf_init_sps(dev);
>>  		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>>  	}
>> -	/* Enable Cool n Quiet Framework (CnQF) */
>> -	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>> +
>> +	/* Enable Auto Mode */
>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>> +		amd_pmf_init_auto_mode(dev);
>> +		dev_dbg(dev->dev, "Auto Mode Init done\n");
>> +		/*
>> +		 * Auto mode and CnQF cannot co-exist. If auto mode is supported it takes
>> +		 * higher priority over CnQF.
>> +		 */
>> +	} else if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
>> +		/* Enable Cool n Quiet Framework (CnQF) */
>>  		amd_pmf_init_cnqf(dev);
>>  		dev_dbg(dev->dev, "CnQF Init done\n");
>>  	}
>> @@ -272,6 +286,13 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>>  {
>>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>>  		amd_pmf_deinit_sps(dev);
>> +
>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>> +		amd_pmf_deinit_auto_mode(dev);
>> +		/* If auto mode is supported, there is no need to proceed */
>> +		return;
>> +	}
>> +
>>  	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
>>  		amd_pmf_deinit_cnqf(dev);
>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>> index 452266809dfa..0532f49e9613 100644
>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>> @@ -18,6 +18,7 @@
>>  #define APMF_FUNC_VERIFY_INTERFACE			0
>>  #define APMF_FUNC_GET_SYS_PARAMS			1
>>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>> +#define APMF_FUNC_AUTO_MODE					5
>>  #define APMF_FUNC_SET_FAN_IDX				7
>>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>>  #define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
>> @@ -51,6 +52,7 @@
>>  struct apmf_if_functions {
>>  	bool system_params;
>>  	bool sbios_heartbeat;
>> +	bool auto_mode_def;
>>  	bool fan_table_idx;
>>  	bool static_slider_granular;
>>  	bool dyn_slider_ac;
>> @@ -197,6 +199,33 @@ struct fan_table_control {
>>  	unsigned long fan_id;
>>  };
>>  
>> +/* Auto Mode Layer */
>> +enum auto_mode_transition_priority {
>> +	AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */
>> +	AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
>> +	AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
>> +	AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */
>> +	AUTO_TRANSITION_MAX,
>> +};
>> +
>> +enum auto_mode_mode {
>> +	AUTO_QUIET,
>> +	AUTO_BALANCE,
>> +	AUTO_PERFORMANCE_ON_LAP,
>> +	AUTO_PERFORMANCE,
>> +	AUTO_MODE_MAX,
>> +};
>> +
>> +struct auto_mode_trans_params {
>> +	u32	time_constant; /* minimum time required to switch to next mode */
>> +	u32 power_delta; /* delta power to shift mode */
>> +	u32 power_threshold;
>> +	u32	timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */
>> +	u32 applied;
>> +	enum auto_mode_mode target_mode;
>> +	u32 shifting_up;
>> +};
>> +
>>  struct power_table_control {
>>  	u32 spl;
>>  	u32 sppt;
>> @@ -207,6 +236,74 @@ struct power_table_control {
>>  	u32 reserved[16];
>>  };
>>  
>> +struct auto_mode_mode_settings {
>> +	struct power_table_control power_control;
>> +	struct fan_table_control fan_control;
>> +	u32 power_floor;
>> +};
>> +
>> +struct auto_mode_mode_config {
>> +	struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX];
>> +	struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX];
>> +	enum auto_mode_mode current_mode;
>> +	bool on_lap;
>> +	bool better_perf;
>> +	u32 amt_enabled; /* Auto Mode Transition */
>> +	u32 avg_power;
>> +};
>> +
>> +struct apmf_auto_mode {
>> +	u16 size;
>> +	/* time constant */
>> +	u32 balanced_to_perf;
>> +	u32 perf_to_balanced;
>> +	u32 quiet_to_balanced;
>> +	u32 balanced_to_quiet;
>> +	/* power floor */
>> +	u32 pfloor_perf;
>> +	u32 pfloor_balanced;
>> +	u32 pfloor_quiet;
>> +	/* Power delta for mode change */
>> +	u32 pd_balanced_to_perf;
>> +	u32 pd_perf_to_balanced;
>> +	u32 pd_quiet_to_balanced;
>> +	u32 pd_balanced_to_quiet;
>> +	/* skin temperature limits */
>> +	u8 stt_apu_perf_on_lap; /* CQL ON */
>> +	u8 stt_hs2_perf_on_lap; /* CQL ON */
>> +	u8 stt_apu_perf;
>> +	u8 stt_hs2_perf;
>> +	u8 stt_apu_balanced;
>> +	u8 stt_hs2_balanced;
>> +	u8 stt_apu_quiet;
>> +	u8 stt_hs2_quiet;
>> +	u32 stt_min_limit_perf_on_lap; /* CQL ON */
>> +	u32 stt_min_limit_perf;
>> +	u32 stt_min_limit_balanced;
>> +	u32 stt_min_limit_quiet;
>> +	/* SPL based */
>> +	u32 fppt_perf_on_lap; /* CQL ON */
>> +	u32 sppt_perf_on_lap; /* CQL ON */
>> +	u32 spl_perf_on_lap; /* CQL ON */
>> +	u32 sppt_apu_only_perf_on_lap; /* CQL ON */
>> +	u32 fppt_perf;
>> +	u32 sppt_perf;
>> +	u32 spl_perf;
>> +	u32 sppt_apu_only_perf;
>> +	u32 fppt_balanced;
>> +	u32 sppt_balanced;
>> +	u32 spl_balanced;
>> +	u32 sppt_apu_only_balanced;
>> +	u32 fppt_quiet;
>> +	u32 sppt_quiet;
>> +	u32 spl_quiet;
>> +	u32 sppt_apu_only_quiet;
>> +	/* Fan ID */
>> +	u32 fan_id_perf;
>> +	u32 fan_id_balanced;
>> +	u32 fan_id_quiet;
>> +} __packed;
>> +
>>  /* CnQF Layer */
>>  enum cnqf_trans_priority {
>>  	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
>> @@ -317,6 +414,13 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>>  
>>  int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>  
>> +/* Auto Mode Layer */
>> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>> +
>>  /* CnQF Layer */
>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>  int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
> 

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-27 23:52     ` Limonciello, Mario
@ 2022-07-28 13:03       ` Hans de Goede
  2022-07-28 13:43         ` Limonciello, Mario
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-28 13:03 UTC (permalink / raw)
  To: Limonciello, Mario, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/28/22 01:52, Limonciello, Mario wrote:
> On 7/27/2022 16:46, Hans de Goede wrote:
>> <resend with Cc list fixed>
>>
>> Hi,
>>
>> On 7/12/22 16:58, Shyam Sundar S K wrote:
>>> The transition to auto-mode happens when the PMF driver receives
>>> AMT (Auto Mode transition) event. transition logic will reside in the
>>> PMF driver but the events would come from other supported drivers[1].
>>>
>>> The thermal parameters would vary between when a performance "on-lap" mode
>>> is detected and versus when not. The CQL event would get triggered from
>>> other drivers, so that PMF driver would adjust the system thermal config
>>> based on the ACPI inputs.
>>>
>>> OEMs can control whether or not to enable AMT or CQL via other supported
>>> drivers[1] but the actual transition logic resides in the AMD PMF driver.
>>> When an AMT event is received the automatic mode transition RAPL algorithm
>>> will run. When a CQL event is received an performance "on-lap" mode will
>>> be enabled and thermal parameters will be adjusted accordingly.
>>>
>>> [1]
>>> Link: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Fcommit%2F%3Fh%3Dreview-hans%26id%3D755b249250df1b612d982f3b702c831b26ecdf73&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cd72d8627f7ad4088aff308da7019afbf%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637945552890705546%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=4gLIOjMo%2FYO%2BkKdqtk4pcLHlsBrLiUb41cTKvuYrcHQ%3D&amp;reserved=0
>>>
>>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>> ---
>>>   drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>>>   drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>>>   drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>>>   drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>>>   drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>>>   5 files changed, 156 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>>> index e9f33e61659f..4bde86aeafa0 100644
>>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>>> @@ -12,6 +12,8 @@
>>>   #include "pmf.h"
>>>     #define APMF_METHOD "\\_SB_.PMF_.APMF"
>>> +#define APMF_CQL_NOTIFICATION    2
>>> +#define APMF_AMT_NOTIFICATION    3
>>>     static unsigned long supported_func;
>>>   @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>>   {
>>>       func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>>       func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>> +    func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>>>       func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>>       func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>>       func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>       return err;
>>>   }
>>>   +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
>>> +{
>>> +    union acpi_object *info;
>>> +    size_t size;
>>> +    int err = 0;
>>> +
>>> +    info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
>>> +    if (!info)
>>> +        return -EIO;
>>> +
>>> +    size = *(u16 *)info->buffer.pointer;
>>> +
>>> +    if (size < sizeof(union acpi_object)) {
>>> +        pr_err("PMF: buffer too small %zu\n", size);
>>> +        err = -EINVAL;
>>> +        goto out;
>>> +    }
>>> +
>>> +    size = min(sizeof(*req), size);
>>> +    memset(req, 0, sizeof(*req));
>>> +    memcpy(req, info->buffer.pointer, size);
>>> +
>>> +    pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
>>> +         req->pending_req, req->cql_event, req->amt_event);
>>> +
>>> +out:
>>> +    kfree(info);
>>> +    return err;
>>> +}
>>> +
>>>   static acpi_handle apmf_if_probe_handle(void)
>>>   {
>>>       acpi_handle handle = NULL;
>>> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>>>       return handle;
>>>   }
>>>   +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>> +{
>>> +    struct amd_pmf_dev *pmf_dev = data;
>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>> +    int ret;
>>> +
>>> +    if (apmf_if->func.sbios_requests) {
>>> +        struct apmf_sbios_req req;
>>> +
>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>> +        if (ret) {
>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>> +            return;
>>> +        }
>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>> +            if (req.amt_event)
>>> +                pmf_dev->is_amt_event = true;
>>> +            else
>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>> +        }
>>> +
>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>> +            if (req.cql_event)
>>> +                pmf_dev->is_cql_event = true;
>>> +            else
>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>> +
>>> +            /* update the target mode information */
>>> +            amd_pmf_update_2_cql(pmf_dev);
>>> +        }
>>> +    }
>>> +}
>>> +
>>>   void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>>>   {
>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>> +
>>>       if (pmf_dev->apmf_if->func.sbios_heartbeat)
>>>           cancel_delayed_work_sync(&pmf_dev->heart_beat);
>>> +
>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
>>> +        acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
>>> +                       apmf_event_handler);
>>>   }
>>>     int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>   {
>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>>       struct apmf_notification_cfg *n;
>>>       acpi_handle apmf_if_handle;
>>>       struct apmf_if *apmf_if;
>>> -    int ret;
>>> +    int ret, status;
>>>         apmf_if_handle = apmf_if_probe_handle();
>>>       if (!apmf_if_handle)
>>> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>           schedule_delayed_work(&pmf_dev->heart_beat, 0);
>>>       }
>>>   +    /* Install the APMF Notify handler */
>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>> +        status = acpi_install_notify_handler(ahandle,
>>> +                             ACPI_ALL_NOTIFY,
>>> +                             apmf_event_handler, pmf_dev);
>>> +        if (ACPI_FAILURE(status)) {
>>> +            dev_err(pmf_dev->dev, "failed to install notify handler\n");
>>> +            return -ENODEV;
>>> +        }
>>> +    }
>>> +
>>>   out:
>>>       return ret;
>>>   }
>>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>>> index 954fde25e71e..a85ef4b95cdb 100644
>>> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
>>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>>> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>       bool update = false;
>>>       int i, j;
>>>   +    if (!dev->amt_running && dev->is_amt_event) {
>>> +        dev_dbg(dev->dev, "setting AMT thermals\n");
>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>> +        dev->amt_running = true;
>>> +        return;
>>> +    }
>>> +
>>>       /* Get the average moving average computed by auto mode algorithm */
>>>       avg_power = amd_pmf_get_moving_avg(socket_power);
>>>   @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>       }
>>>   }
>>>   +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
>>> +{
>>> +    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>>> +            dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
>>> +    if ((config_store.current_mode == AUTO_PERFORMANCE ||
>>> +         config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
>>> +        config_store.current_mode !=
>>> +        config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
>>> +        config_store.current_mode =
>>> +                config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>> +    }
>>> +    dev_dbg(dev->dev, "updated CQL thermals\n");
>>> +}
>>> +
>>>   static void amd_pmf_get_power_threshold(void)
>>>   {
>>>       config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>>> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
>>> index 2b03ae1ad37f..eba8f0d79a62 100644
>>> --- a/drivers/platform/x86/amd/pmf/cnqf.c
>>> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
>>> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>>>       }
>>>   }
>>>   -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>>>   {
>>>       struct cnqf_tran_params *tp;
>>>       int src, i, j, index = 0;
>>> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>>>       }
>>>         for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
>>> -        config_store.trans_param[src][i].timer += time_lapsed_ms;
>>> +        config_store.trans_param[src][i].timer += time_elapsed_ms;
>>>           config_store.trans_param[src][i].total_power += socket_power;
>>>           config_store.trans_param[src][i].count++;
>>>   diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>>> index 674ddf599135..2a3dacfb2005 100644
>>> --- a/drivers/platform/x86/amd/pmf/core.c
>>> +++ b/drivers/platform/x86/amd/pmf/core.c
>>> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>       enum platform_profile_option current_profile;
>>>       ktime_t time_elapsed_ms;
>>>       int socket_power;
>>> +    u8 mode;
>>>         /* Get the current profile information */
>>>       platform_profile_get(&current_profile);
>>>   +    if (!dev->is_amt_event)
>>> +        dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
>>> +            dev->is_amt_event ? "Enabled" : "Disabled");
>>> +
>>>       /* Transfer table contents */
>>>       memset(&dev->m_table, 0, sizeof(dev->m_table));
>>>       amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
>>> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>       socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>>         if (current_profile == PLATFORM_PROFILE_BALANCED) {
>>
>> Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
>> on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
>> then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?
>>
>> Can you please explain the expected interactions between thinkpad_acpi and
>> this code here a bit more ?
>>
>> Since we now only call amd_pmf_trans_automode() based on the AMT flag and
>> that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
>> to be cleared when the profile is not balanced and can we thus not just drop
>> the current_profile == PLATFORM_PROFILE_BALANCED check all together?
>>
>> It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
>> then enable AMT, else disable it, logic belongs inside thinkpad_acpi
>> and not here?
>>
> 
> It actually already lives in thinkpad_acpi.
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/tree/drivers/platform/x86/thinkpad_acpi.c?h=review-hans#n10489
> 
> By the control point from thinkpad_acpi BIOS events will be emitted controlling whether AMT is running in a given mode.
> 
> So yes; there is no need for this here anymore.

Right. There still are some open questions / things which need fixing here though:

1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
   will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
   stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
   the amd-pmf module gets loaded first. So if the intend is for it to always be owned
   by thinkpad_acpi then the amd-pmf code must check for this and not even try to
   register its platform_profile support. We cannot rely on module ordering ensuring
   that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
   since there are no module load ordering guarantees.

2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
   enable AMT and then the periodically run workqueue function from amd-pmf
   will do its AMT thing. But what when the thinkpad_acpi platform_profile is
   set to low-power or performance. Should the amd-pmf code then apply the static
   slider settings for low-power/performance which it has read from the ACPI
   tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?

3. If the answer to 2. is "Yes the amd-pmf code should apply the static-slider
   settings" then we will still need patch 1/15 to allow the amd-pmd code to
   read the platform-profile setting from the thinkpad_acpi platform-profile
   provider;
   And if the answer is "No, the thinkpad ACPI/EC will take care of this"
   then we should probably make sure that the static slider code never runs
   at all on thinkpads.

Regards,

Hans


  



> 
>> Regards,
>>
>> Hans
>>
>>> -        /* Apply the Auto Mode transition */
>>> -        amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>> +        if (dev->is_amt_event) {
>>> +            /* Apply the Auto Mode transition */
>>> +            amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>> +        } else if (!dev->is_amt_event && dev->amt_running) {
>>> +            pr_debug("resetting AMT thermals\n");
>>> +            mode = amd_pmf_get_pprof_modes(dev);
>>> +            amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
>>> +            dev->amt_running = false;
>>> +        }
>>> +    } else {
>>> +        dev->amt_running = false;
>>>       }
>>>         if (dev->cnqf_feat) {
>>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>>> index 0532f49e9613..9ae9812353cd 100644
>>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>>> @@ -17,6 +17,7 @@
>>>   /* APMF Functions */
>>>   #define APMF_FUNC_VERIFY_INTERFACE            0
>>>   #define APMF_FUNC_GET_SYS_PARAMS            1
>>> +#define APMF_FUNC_SBIOS_REQUESTS            2
>>>   #define APMF_FUNC_SBIOS_HEARTBEAT            4
>>>   #define APMF_FUNC_AUTO_MODE                    5
>>>   #define APMF_FUNC_SET_FAN_IDX                7
>>> @@ -51,6 +52,7 @@
>>>   /* AMD PMF BIOS interfaces */
>>>   struct apmf_if_functions {
>>>       bool system_params;
>>> +    bool sbios_requests;
>>>       bool sbios_heartbeat;
>>>       bool auto_mode_def;
>>>       bool fan_table_idx;
>>> @@ -84,6 +86,24 @@ struct apmf_system_params {
>>>       u32 heartbeat_int;
>>>   } __packed;
>>>   +struct apmf_sbios_req {
>>> +    u16 size;
>>> +    u32 pending_req;
>>> +    u8 rsd;
>>> +    u8 cql_event;
>>> +    u8 amt_event;
>>> +    u32 fppt;
>>> +    u32 sppt;
>>> +    u32 fppt_apu_only;
>>> +    u32 spl;
>>> +    u32 stt_min_limit;
>>> +    u8 skin_temp_apu;
>>> +    u8 skin_temp_hs2;
>>> +    u8 dps_enable;
>>> +    u32 custom_policy_1;
>>> +    u32 custom_policy_2;
>>> +} __packed;
>>> +
>>>   struct apmf_fan_idx {
>>>       u16 size;
>>>       u8 fan_ctl_mode;
>>> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>>>   #endif /* CONFIG_DEBUG_FS */
>>>       bool cnqf_feat;
>>>       bool cnqf_running;
>>> +    bool is_amt_event;
>>> +    bool is_cql_event;
>>> +    bool amt_running;
>>>   };
>>>     struct apmf_sps_prop_granular {
>>> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>>   /* Auto Mode Layer */
>>>   void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>>>   int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>>> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>>>   void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>>>   void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>>> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
>>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>     /* CnQF Layer */
>>>   int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>   void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>>>   void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>>>   void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
>>> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>     #endif /* PMF_H */
>>
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature
  2022-07-28 12:57     ` Shyam Sundar S K
@ 2022-07-28 13:15       ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-07-28 13:15 UTC (permalink / raw)
  To: Shyam Sundar S K, markgross, Bastien Nocera
  Cc: platform-driver-x86, Patil.Reddy

Hi Shyam,

On 7/28/22 14:57, Shyam Sundar S K wrote:
> Hi Hans,
> 
> On 7/28/2022 2:52 AM, Hans de Goede wrote:
>> Hi,
>>
>> On 7/12/22 16:58, Shyam Sundar S K wrote:
>>> The objective of this feature is to track the moving average of system
>>> power over the time period specified and switch to the subsequent mode.
>>>
>>> This feature has 3 modes: quiet, balanced, performance.
>>>
>>> In order to do this, PMF driver will get the moving average of APU power
>>> from PMFW and power threshold, time constants, system config parameters
>>> from OEM inputs.
>>>
>>> System power as read by PMF driver from PMFW is the filtered value over
>>> the sampling window. Every sampling window, moving average of system power
>>> is computed. At the end of the monitoring window, the moving average is
>>> compared against the threshold for mode switch for decision making.
>>>
>>> With AMD managing the system config limits, any mode switch within
>>> auto-mode will result in limits of fPPT/sPPT/STAPM or STT being scaled
>>> down.
>>>
>>> When "auto mode" is enabled, the static slider control remains out of
>>> the PMF driver, so the platform_profile registration would not
>>> happen in PMF driver.
>>
>> This is not what happens in the code, the platform_profile registration
>> is still happening AFAICT.
> 
> platform_profile_register gets called in amd_pmf_init_sps() and it gets
> triggered only when APMF_FUNC_STATIC_SLIDER_GRANULAR (fn9) is enabled in
> the BIOS.
> 
> OEMs have been already told in the BIOS implementation guide that, if
> the "Auto Mode" is enabled, the "Static platform_profile" registration
> shall happen at the OEM driver side. In this case, it will be
> thinkpad_acpi which shall do the "platform_profile_register".

I see. IMHO it would still be good to check that we never end up
with both the static-slider + AMT support enabled at the same time.

It is better to catch this; ignore the static-slider support
(clear the feature flag I guess) and log an error then to get surprised
by firmware bugs and having to figure out what is going on when
a user reports some weird bug.

> 
>>
>> Like with CnQF for this other auto mode we also need to figure out the
>> relation with the regular static platform_profile settings.
>>
>> I see this in the code:
>>
>> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>  	/* Calculate the avg SoC power consumption */
>>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>  
>> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
>> +		/* Apply the Auto Mode transition */
>> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>> +	}
>> +
>>
>> So it seems that unlike CnQF this other auto-mode only does mode transitions
>> when the static-slider is set to balanced ?
> 
> CnQF and Auto-mode are complimentary to eachother. The key difference
> between them are:
> 
> 1. CnQF will have 4 internal modes (low-power, balanced, performance,
> turbo) and Auto-mode will not have the turbo mode.
> 
> 2. CnQF will be enabled only when the APCI call to the BIOS function
> returns a spl flag which gets set from the BIOS side and driver sees it.
> That is when the driver activates the CnQF. Irrespective of the
> platform_profile choice selection, if the CnQF is enabled the values
> passed in the BIOS tables for static slider are ignored.

Right, this is rather "rude" though, to ignore a clear user preference.

We really need to think of the userspace API results here. Arguably
when CnQF gets enabled the platform_profile handler should just be
unregistered to indicate that there is no platform_profile support.

IMHO that would be better then still advertising platform_profile
support and not have it working.

Also platform_profile support is also used to implement a low-battery
mode where userspace automatically switches the profile to low-power
when the battery is below say 20%. How is such a power-saver mode
supposed to be implemented with CnQF ?

> 3. Auto-mode will get activated only when an AMT ON event (which will be
> sent by thinkpad_acpi driver in this case) is received and the
> platform_profile is set to balanced. In other modes it keeps turned off.

Right, note that as discussed elsewhere in the thread the decision to
only enable AMT when platform_profile == balanced is a policy coded
inside the thinkpad_acpi code and IMHO this policy decision should not
be duplicated in the amd-pmf code. Instead it should just enable/disable
AMT soleley on the BIOS AMT enable/disable setting/events.

> 4. The sampling window, the threshold calculation mechanism and the
> power floor measurements are different w.r.t to CnQF and auto-mode.
> 
> 5. OEMs are free to choose what kind of optimizations they are looking
> for by enabling one or more of such features within PMF.
> 
> Hope it clarifies :-)

It helps a bit, but there are still questions. Off-list Mario suggested
to maybe just drop CnQF from the patch-set (for now) and first focus on
getting the amd-pmf code to work in the thinkpad case. And then once
that is solved re-introduce the CnQF patches/code. I think that might
be a good idea to move this forward a bit faster. Especially since
the CnQF story involves some userspace API issues (as mentioned) and
I would like to make sure we get the userspace API issues cleared up
before merging the CnQF bits.

>>> The transition to auto-mode only happens when the APMF fn5 is enabled
>>> in BIOS, platform_profile set to "balanced" and a AMT
>>> (Auto Mode transition) is received.
>>
>> Right, this part of the commit msg matches the code, but it conflicts with
>> the "When "auto mode" is enabled, the static slider control remains out of
>> the PMF driver:" part of the commit msg above.

Note this bit of the commit message needs to be claried that the mentioned
platform_profile setting is coming from another driver and not form
amd-pmf. Or better yet as mentioned just only check the BIOS AMT flag
and then this bit of the commit msg can simply be dropped.

Regards,

Hans



>>
>>>
>>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>> ---
>>>  drivers/platform/x86/amd/pmf/Makefile    |   2 +-
>>>  drivers/platform/x86/amd/pmf/acpi.c      |  28 ++
>>>  drivers/platform/x86/amd/pmf/auto-mode.c | 317 +++++++++++++++++++++++
>>>  drivers/platform/x86/amd/pmf/core.c      |  25 +-
>>>  drivers/platform/x86/amd/pmf/pmf.h       | 104 ++++++++
>>>  5 files changed, 473 insertions(+), 3 deletions(-)
>>>  create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c
>>>
>>> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
>>> index d02a0bdc6429..2a9568bf9064 100644
>>> --- a/drivers/platform/x86/amd/pmf/Makefile
>>> +++ b/drivers/platform/x86/amd/pmf/Makefile
>>> @@ -6,4 +6,4 @@
>>>  
>>>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
>>>  amd-pmf-objs := core.o acpi.o sps.o \
>>> -		cnqf.o
>>> +		cnqf.o auto-mode.o
>>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>>> index a3ff91c605b5..e9f33e61659f 100644
>>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>>> @@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>>  {
>>>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>> +	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>>  	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
>>> @@ -210,6 +211,33 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>>>  	return err;
>>>  }
>>>  
>>> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data)
>>> +{
>>> +	union acpi_object *info;
>>> +	size_t size;
>>> +	int err = 0;
>>> +
>>> +	info = apmf_if_call(ampf_if, APMF_FUNC_AUTO_MODE, NULL);
>>> +	if (!info)
>>> +		return -EIO;
>>> +
>>> +	size = *(u16 *)info->buffer.pointer;
>>> +
>>> +	if (size < sizeof(*data)) {
>>> +		pr_debug("buffer too small %zu\n", size);
>>> +		err = -EINVAL;
>>> +		goto out;
>>> +	}
>>> +
>>> +	size = min(sizeof(*data), size);
>>> +	memset(data, 0, sizeof(*data));
>>> +	memcpy(data, info->buffer.pointer, size);
>>> +
>>> +out:
>>> +	kfree(info);
>>> +	return err;
>>> +}
>>> +
>>
>> Please use apmf_if_call_store_buffer() for this (see review of 4/15)
>>
>> Regards,
>>
>> Hans
>>
>>
>>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
>>>  {
>>>  	union acpi_object *info;
>>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>>> new file mode 100644
>>> index 000000000000..954fde25e71e
>>> --- /dev/null
>>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>>> @@ -0,0 +1,317 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * AMD Platform Management Framework Driver
>>> + *
>>> + * Copyright (c) 2022, Advanced Micro Devices, Inc.
>>> + * All Rights Reserved.
>>> + *
>>> + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>> + */
>>> +
>>> +#include <linux/acpi.h>
>>> +#include <linux/list.h>
>>> +#include <linux/workqueue.h>
>>> +#include "pmf.h"
>>> +
>>> +#define AVG_SAMPLE_SIZE 3
>>> +
>>> +struct power_history {
>>> +	int socket_power;
>>> +	struct list_head list;
>>> +	int avg;
>>> +	int total;
>>> +};
>>> +
>>> +struct list_pdata {
>>> +	int total;
>>> +	int avg;
>>> +};
>>> +
>>> +static struct power_history power_list;
>>> +static struct list_pdata pdata;
>>> +
>>> +static struct auto_mode_mode_config config_store;
>>> +static const char *state_as_str(unsigned int state);
>>> +
>>> +static void amd_pmf_handle_automode(struct amd_pmf_dev *dev, bool op, int idx,
>>> +				    struct auto_mode_mode_config *table)
>>> +{
>>> +	if (op == SLIDER_OP_SET) {
>>> +		struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control;
>>> +
>>> +		amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL);
>>> +		amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL);
>>> +		amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL);
>>> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
>>> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
>>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>>> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
>>> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>>> +				 pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
>>> +	} else if (op == SLIDER_OP_GET) {
>>> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.spl);
>>> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.fppt);
>>> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.sppt);
>>> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.sppt_apu_only);
>>> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_min);
>>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_APU]);
>>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_HS2]);
>>> +	}
>>> +
>>> +	if (dev->apmf_if->func.fan_table_idx)
>>> +		apmf_update_fan_idx(dev->apmf_if, config_store.mode_set[idx].fan_control.manual,
>>> +				    config_store.mode_set[idx].fan_control.fan_id);
>>> +}
>>> +
>>> +static int amd_pmf_get_moving_avg(int socket_power)
>>> +{
>>> +	struct power_history *tmp;
>>> +	struct list_head *pos, *q;
>>> +	static int count;
>>> +
>>> +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
>>> +	tmp->socket_power = socket_power;
>>> +	list_add_tail(&tmp->list, &power_list.list);
>>> +
>>> +	list_for_each_safe(pos, q, &power_list.list) {
>>> +		if (count >= AVG_SAMPLE_SIZE) {
>>> +			tmp = list_first_entry(pos, struct power_history, list);
>>> +			list_del_init(pos);
>>> +			goto next;
>>> +		}
>>> +	}
>>> +
>>> +next:
>>> +	pdata.total = 0;
>>> +	pdata.avg = 0;
>>> +
>>> +	list_for_each(pos, &power_list.list) {
>>> +		tmp = list_entry(pos, struct power_history, list);
>>> +		pdata.total += tmp->socket_power;
>>> +		pdata.avg = pdata.total / AVG_SAMPLE_SIZE;
>>> +	}
>>> +
>>> +	count++;
>>> +	if (count >= AVG_SAMPLE_SIZE)
>>> +		return pdata.avg;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>>> +{
>>> +	int avg_power = 0;
>>> +	bool update = false;
>>> +	int i, j;
>>> +
>>> +	/* Get the average moving average computed by auto mode algorithm */
>>> +	avg_power = amd_pmf_get_moving_avg(socket_power);
>>> +
>>> +	for (i = 0; i < AUTO_TRANSITION_MAX; i++) {
>>> +		if ((config_store.transition[i].shifting_up && avg_power >=
>>> +		     config_store.transition[i].power_threshold) ||
>>> +		    (!config_store.transition[i].shifting_up && avg_power <=
>>> +		     config_store.transition[i].power_threshold)) {
>>> +			if (config_store.transition[i].timer <
>>> +			    config_store.transition[i].time_constant)
>>> +				config_store.transition[i].timer += time_elapsed_ms;
>>> +		} else {
>>> +			config_store.transition[i].timer = 0;
>>> +		}
>>> +
>>> +		if (config_store.transition[i].timer >=
>>> +		    config_store.transition[i].time_constant &&
>>> +		    !config_store.transition[i].applied) {
>>> +			config_store.transition[i].applied = true;
>>> +			update = true;
>>> +		} else if (config_store.transition[i].timer <=
>>> +			   config_store.transition[i].time_constant &&
>>> +			   config_store.transition[i].applied) {
>>> +			config_store.transition[i].applied = false;
>>> +			update = true;
>>> +		}
>>> +	}
>>> +
>>> +	dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power,
>>> +		state_as_str(config_store.current_mode));
>>> +
>>> +	if (update) {
>>> +		for (j = 0; j < AUTO_TRANSITION_MAX; j++) {
>>> +			/* Apply the mode with highest priority indentified */
>>> +			if (config_store.transition[j].applied) {
>>> +				if (config_store.current_mode !=
>>> +				    config_store.transition[j].target_mode) {
>>> +					config_store.current_mode =
>>> +							config_store.transition[j].target_mode;
>>> +					dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n",
>>> +						state_as_str(config_store.current_mode));
>>> +					amd_pmf_handle_automode(dev, SLIDER_OP_SET,
>>> +								config_store.current_mode, NULL);
>>> +				}
>>> +				break;
>>> +			}
>>> +		}
>>> +	}
>>> +}
>>> +
>>> +static void amd_pmf_get_power_threshold(void)
>>> +{
>>> +	config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>>> +				config_store.mode_set[AUTO_BALANCE].power_floor -
>>> +				config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta;
>>> +
>>> +	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold =
>>> +				config_store.mode_set[AUTO_BALANCE].power_floor -
>>> +				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta;
>>> +
>>> +	config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold =
>>> +			config_store.mode_set[AUTO_QUIET].power_floor -
>>> +			config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta;
>>> +
>>> +	config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold =
>>> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor -
>>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta;
>>> +}
>>> +
>>> +static const char *state_as_str(unsigned int state)
>>> +{
>>> +	switch (state) {
>>> +	case AUTO_QUIET:
>>> +		return "QUIET";
>>> +	case AUTO_BALANCE:
>>> +		return "BALANCED";
>>> +	case AUTO_PERFORMANCE_ON_LAP:
>>> +		return "ON_LAP";
>>> +	case AUTO_PERFORMANCE:
>>> +		return "PERFORMANCE";
>>> +	default:
>>> +		return "Unknown Auto Mode State";
>>> +	}
>>> +}
>>> +
>>> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev)
>>> +{
>>> +	struct apmf_auto_mode output;
>>> +	struct power_table_control *pwr_ctrl;
>>> +	int i;
>>> +
>>> +	if (dev->apmf_if->func.auto_mode_def) {
>>> +		apmf_get_auto_mode_def(dev->apmf_if, &output);
>>> +		/* time constant */
>>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant =
>>> +									output.balanced_to_quiet;
>>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant =
>>> +									output.balanced_to_perf;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant =
>>> +									output.quiet_to_balanced;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant =
>>> +									output.perf_to_balanced;
>>> +
>>> +		/* power floor */
>>> +		config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet;
>>> +		config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced;
>>> +		config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf;
>>> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf;
>>> +
>>> +		/* Power delta for mode change */
>>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta =
>>> +									output.pd_balanced_to_quiet;
>>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta =
>>> +									output.pd_balanced_to_perf;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta =
>>> +									output.pd_quiet_to_balanced;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta =
>>> +									output.pd_perf_to_balanced;
>>> +
>>> +		/* Power threshold */
>>> +		amd_pmf_get_power_threshold();
>>> +
>>> +		/* skin temperature limits */
>>> +		pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control;
>>> +		pwr_ctrl->spl = output.spl_quiet;
>>> +		pwr_ctrl->sppt = output.sppt_quiet;
>>> +		pwr_ctrl->fppt = output.fppt_quiet;
>>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet;
>>> +		pwr_ctrl->stt_min = output.stt_min_limit_quiet;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet;
>>> +
>>> +		pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control;
>>> +		pwr_ctrl->spl = output.spl_balanced;
>>> +		pwr_ctrl->sppt = output.sppt_balanced;
>>> +		pwr_ctrl->fppt = output.fppt_balanced;
>>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced;
>>> +		pwr_ctrl->stt_min = output.stt_min_limit_balanced;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced;
>>> +
>>> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control;
>>> +		pwr_ctrl->spl = output.spl_perf;
>>> +		pwr_ctrl->sppt = output.sppt_perf;
>>> +		pwr_ctrl->fppt = output.fppt_perf;
>>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf;
>>> +		pwr_ctrl->stt_min = output.stt_min_limit_perf;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf;
>>> +
>>> +		pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control;
>>> +		pwr_ctrl->spl = output.spl_perf_on_lap;
>>> +		pwr_ctrl->sppt = output.sppt_perf_on_lap;
>>> +		pwr_ctrl->fppt = output.fppt_perf_on_lap;
>>> +		pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap;
>>> +		pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap;
>>> +		pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap;
>>> +
>>> +		/* Fan ID */
>>> +		config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet;
>>> +		config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced;
>>> +		config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf;
>>> +		config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id =
>>> +										output.fan_id_perf;
>>> +
>>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET;
>>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>>> +									AUTO_PERFORMANCE;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode =
>>> +										AUTO_BALANCE;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode =
>>> +										AUTO_BALANCE;
>>> +
>>> +		config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false;
>>> +		config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true;
>>> +		config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up =
>>> +											false;
>>> +
>>> +		for (i = 0 ; i < AUTO_MODE_MAX ; i++) {
>>> +			if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO)
>>> +				config_store.mode_set[i].fan_control.manual = false;
>>> +			else
>>> +				config_store.mode_set[i].fan_control.manual = true;
>>> +		}
>>> +	}
>>> +}
>>> +
>>> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev)
>>> +{
>>> +	cancel_delayed_work_sync(&dev->work_buffer);
>>> +}
>>> +
>>> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev)
>>> +{
>>> +	INIT_LIST_HEAD(&power_list.list);
>>> +
>>> +	amd_pmf_init_metrics_table(dev);
>>> +	amd_pmf_load_defaults_auto_mode(dev);
>>> +
>>> +	/* update the thermal for Automode */
>>> +	amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>> +}
>>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>>> index bc267d333b76..674ddf599135 100644
>>> --- a/drivers/platform/x86/amd/pmf/core.c
>>> +++ b/drivers/platform/x86/amd/pmf/core.c
>>> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>  	/* Calculate the avg SoC power consumption */
>>>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>>  
>>> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
>>> +		/* Apply the Auto Mode transition */
>>> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>> +	}
>>> +
>>>  	if (dev->cnqf_feat) {
>>>  		/* Apply the CnQF transition */
>>>  		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
>>> @@ -260,9 +265,18 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
>>>  		amd_pmf_init_sps(dev);
>>>  		dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
>>>  	}
>>> -	/* Enable Cool n Quiet Framework (CnQF) */
>>> -	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>> +
>>> +	/* Enable Auto Mode */
>>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>> +		amd_pmf_init_auto_mode(dev);
>>> +		dev_dbg(dev->dev, "Auto Mode Init done\n");
>>> +		/*
>>> +		 * Auto mode and CnQF cannot co-exist. If auto mode is supported it takes
>>> +		 * higher priority over CnQF.
>>> +		 */
>>> +	} else if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
>>> +		/* Enable Cool n Quiet Framework (CnQF) */
>>>  		amd_pmf_init_cnqf(dev);
>>>  		dev_dbg(dev->dev, "CnQF Init done\n");
>>>  	}
>>> @@ -272,6 +286,13 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>>>  {
>>>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>>>  		amd_pmf_deinit_sps(dev);
>>> +
>>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>> +		amd_pmf_deinit_auto_mode(dev);
>>> +		/* If auto mode is supported, there is no need to proceed */
>>> +		return;
>>> +	}
>>> +
>>>  	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
>>>  		amd_pmf_deinit_cnqf(dev);
>>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>>> index 452266809dfa..0532f49e9613 100644
>>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>>> @@ -18,6 +18,7 @@
>>>  #define APMF_FUNC_VERIFY_INTERFACE			0
>>>  #define APMF_FUNC_GET_SYS_PARAMS			1
>>>  #define APMF_FUNC_SBIOS_HEARTBEAT			4
>>> +#define APMF_FUNC_AUTO_MODE					5
>>>  #define APMF_FUNC_SET_FAN_IDX				7
>>>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR	9
>>>  #define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
>>> @@ -51,6 +52,7 @@
>>>  struct apmf_if_functions {
>>>  	bool system_params;
>>>  	bool sbios_heartbeat;
>>> +	bool auto_mode_def;
>>>  	bool fan_table_idx;
>>>  	bool static_slider_granular;
>>>  	bool dyn_slider_ac;
>>> @@ -197,6 +199,33 @@ struct fan_table_control {
>>>  	unsigned long fan_id;
>>>  };
>>>  
>>> +/* Auto Mode Layer */
>>> +enum auto_mode_transition_priority {
>>> +	AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */
>>> +	AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */
>>> +	AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */
>>> +	AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */
>>> +	AUTO_TRANSITION_MAX,
>>> +};
>>> +
>>> +enum auto_mode_mode {
>>> +	AUTO_QUIET,
>>> +	AUTO_BALANCE,
>>> +	AUTO_PERFORMANCE_ON_LAP,
>>> +	AUTO_PERFORMANCE,
>>> +	AUTO_MODE_MAX,
>>> +};
>>> +
>>> +struct auto_mode_trans_params {
>>> +	u32	time_constant; /* minimum time required to switch to next mode */
>>> +	u32 power_delta; /* delta power to shift mode */
>>> +	u32 power_threshold;
>>> +	u32	timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */
>>> +	u32 applied;
>>> +	enum auto_mode_mode target_mode;
>>> +	u32 shifting_up;
>>> +};
>>> +
>>>  struct power_table_control {
>>>  	u32 spl;
>>>  	u32 sppt;
>>> @@ -207,6 +236,74 @@ struct power_table_control {
>>>  	u32 reserved[16];
>>>  };
>>>  
>>> +struct auto_mode_mode_settings {
>>> +	struct power_table_control power_control;
>>> +	struct fan_table_control fan_control;
>>> +	u32 power_floor;
>>> +};
>>> +
>>> +struct auto_mode_mode_config {
>>> +	struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX];
>>> +	struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX];
>>> +	enum auto_mode_mode current_mode;
>>> +	bool on_lap;
>>> +	bool better_perf;
>>> +	u32 amt_enabled; /* Auto Mode Transition */
>>> +	u32 avg_power;
>>> +};
>>> +
>>> +struct apmf_auto_mode {
>>> +	u16 size;
>>> +	/* time constant */
>>> +	u32 balanced_to_perf;
>>> +	u32 perf_to_balanced;
>>> +	u32 quiet_to_balanced;
>>> +	u32 balanced_to_quiet;
>>> +	/* power floor */
>>> +	u32 pfloor_perf;
>>> +	u32 pfloor_balanced;
>>> +	u32 pfloor_quiet;
>>> +	/* Power delta for mode change */
>>> +	u32 pd_balanced_to_perf;
>>> +	u32 pd_perf_to_balanced;
>>> +	u32 pd_quiet_to_balanced;
>>> +	u32 pd_balanced_to_quiet;
>>> +	/* skin temperature limits */
>>> +	u8 stt_apu_perf_on_lap; /* CQL ON */
>>> +	u8 stt_hs2_perf_on_lap; /* CQL ON */
>>> +	u8 stt_apu_perf;
>>> +	u8 stt_hs2_perf;
>>> +	u8 stt_apu_balanced;
>>> +	u8 stt_hs2_balanced;
>>> +	u8 stt_apu_quiet;
>>> +	u8 stt_hs2_quiet;
>>> +	u32 stt_min_limit_perf_on_lap; /* CQL ON */
>>> +	u32 stt_min_limit_perf;
>>> +	u32 stt_min_limit_balanced;
>>> +	u32 stt_min_limit_quiet;
>>> +	/* SPL based */
>>> +	u32 fppt_perf_on_lap; /* CQL ON */
>>> +	u32 sppt_perf_on_lap; /* CQL ON */
>>> +	u32 spl_perf_on_lap; /* CQL ON */
>>> +	u32 sppt_apu_only_perf_on_lap; /* CQL ON */
>>> +	u32 fppt_perf;
>>> +	u32 sppt_perf;
>>> +	u32 spl_perf;
>>> +	u32 sppt_apu_only_perf;
>>> +	u32 fppt_balanced;
>>> +	u32 sppt_balanced;
>>> +	u32 spl_balanced;
>>> +	u32 sppt_apu_only_balanced;
>>> +	u32 fppt_quiet;
>>> +	u32 sppt_quiet;
>>> +	u32 spl_quiet;
>>> +	u32 sppt_apu_only_quiet;
>>> +	/* Fan ID */
>>> +	u32 fan_id_perf;
>>> +	u32 fan_id_balanced;
>>> +	u32 fan_id_quiet;
>>> +} __packed;
>>> +
>>>  /* CnQF Layer */
>>>  enum cnqf_trans_priority {
>>>  	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
>>> @@ -317,6 +414,13 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>>>  
>>>  int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>>  
>>> +/* Auto Mode Layer */
>>> +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>>> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>>> +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>>> +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>> +
>>>  /* CnQF Layer */
>>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>>  int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 13:03       ` Hans de Goede
@ 2022-07-28 13:43         ` Limonciello, Mario
  2022-07-28 14:09           ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-28 13:43 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

On 7/28/2022 08:03, Hans de Goede wrote:
> Hi,
> 
> On 7/28/22 01:52, Limonciello, Mario wrote:
>> On 7/27/2022 16:46, Hans de Goede wrote:
>>> <resend with Cc list fixed>
>>>
>>> Hi,
>>>
>>> On 7/12/22 16:58, Shyam Sundar S K wrote:
>>>> The transition to auto-mode happens when the PMF driver receives
>>>> AMT (Auto Mode transition) event. transition logic will reside in the
>>>> PMF driver but the events would come from other supported drivers[1].
>>>>
>>>> The thermal parameters would vary between when a performance "on-lap" mode
>>>> is detected and versus when not. The CQL event would get triggered from
>>>> other drivers, so that PMF driver would adjust the system thermal config
>>>> based on the ACPI inputs.
>>>>
>>>> OEMs can control whether or not to enable AMT or CQL via other supported
>>>> drivers[1] but the actual transition logic resides in the AMD PMF driver.
>>>> When an AMT event is received the automatic mode transition RAPL algorithm
>>>> will run. When a CQL event is received an performance "on-lap" mode will
>>>> be enabled and thermal parameters will be adjusted accordingly.
>>>>
>>>> [1]
>>>> Link: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Fcommit%2F%3Fh%3Dreview-hans%26id%3D755b249250df1b612d982f3b702c831b26ecdf73&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cf7d0a10b43444af391fa08da709993b3%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637946102171795208%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=GRSR4jEbQu7yaY6CS%2BKienSw7majkwcazo8xoKHd2pA%3D&amp;reserved=0
>>>>
>>>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>>> ---
>>>>    drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>>>>    drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>>>>    drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>>>>    drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>>>>    drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>>>>    5 files changed, 156 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>>>> index e9f33e61659f..4bde86aeafa0 100644
>>>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>>>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>>>> @@ -12,6 +12,8 @@
>>>>    #include "pmf.h"
>>>>      #define APMF_METHOD "\\_SB_.PMF_.APMF"
>>>> +#define APMF_CQL_NOTIFICATION    2
>>>> +#define APMF_AMT_NOTIFICATION    3
>>>>      static unsigned long supported_func;
>>>>    @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>>>    {
>>>>        func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>>>        func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>>> +    func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>>>>        func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>>>        func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>>>        func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>>> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>>        return err;
>>>>    }
>>>>    +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
>>>> +{
>>>> +    union acpi_object *info;
>>>> +    size_t size;
>>>> +    int err = 0;
>>>> +
>>>> +    info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
>>>> +    if (!info)
>>>> +        return -EIO;
>>>> +
>>>> +    size = *(u16 *)info->buffer.pointer;
>>>> +
>>>> +    if (size < sizeof(union acpi_object)) {
>>>> +        pr_err("PMF: buffer too small %zu\n", size);
>>>> +        err = -EINVAL;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    size = min(sizeof(*req), size);
>>>> +    memset(req, 0, sizeof(*req));
>>>> +    memcpy(req, info->buffer.pointer, size);
>>>> +
>>>> +    pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
>>>> +         req->pending_req, req->cql_event, req->amt_event);
>>>> +
>>>> +out:
>>>> +    kfree(info);
>>>> +    return err;
>>>> +}
>>>> +
>>>>    static acpi_handle apmf_if_probe_handle(void)
>>>>    {
>>>>        acpi_handle handle = NULL;
>>>> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>>>>        return handle;
>>>>    }
>>>>    +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>>> +{
>>>> +    struct amd_pmf_dev *pmf_dev = data;
>>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>>> +    int ret;
>>>> +
>>>> +    if (apmf_if->func.sbios_requests) {
>>>> +        struct apmf_sbios_req req;
>>>> +
>>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>>> +        if (ret) {
>>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>>> +            return;
>>>> +        }
>>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>>> +            if (req.amt_event)
>>>> +                pmf_dev->is_amt_event = true;
>>>> +            else
>>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>>> +        }
>>>> +
>>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>>> +            if (req.cql_event)
>>>> +                pmf_dev->is_cql_event = true;
>>>> +            else
>>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>>> +
>>>> +            /* update the target mode information */
>>>> +            amd_pmf_update_2_cql(pmf_dev);
>>>> +        }
>>>> +    }
>>>> +}
>>>> +
>>>>    void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>>>>    {
>>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>>> +
>>>>        if (pmf_dev->apmf_if->func.sbios_heartbeat)
>>>>            cancel_delayed_work_sync(&pmf_dev->heart_beat);
>>>> +
>>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
>>>> +        acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
>>>> +                       apmf_event_handler);
>>>>    }
>>>>      int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>>    {
>>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>>>        struct apmf_notification_cfg *n;
>>>>        acpi_handle apmf_if_handle;
>>>>        struct apmf_if *apmf_if;
>>>> -    int ret;
>>>> +    int ret, status;
>>>>          apmf_if_handle = apmf_if_probe_handle();
>>>>        if (!apmf_if_handle)
>>>> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>>            schedule_delayed_work(&pmf_dev->heart_beat, 0);
>>>>        }
>>>>    +    /* Install the APMF Notify handler */
>>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>>> +        status = acpi_install_notify_handler(ahandle,
>>>> +                             ACPI_ALL_NOTIFY,
>>>> +                             apmf_event_handler, pmf_dev);
>>>> +        if (ACPI_FAILURE(status)) {
>>>> +            dev_err(pmf_dev->dev, "failed to install notify handler\n");
>>>> +            return -ENODEV;
>>>> +        }
>>>> +    }
>>>> +
>>>>    out:
>>>>        return ret;
>>>>    }
>>>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>>>> index 954fde25e71e..a85ef4b95cdb 100644
>>>> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
>>>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>>>> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>>        bool update = false;
>>>>        int i, j;
>>>>    +    if (!dev->amt_running && dev->is_amt_event) {
>>>> +        dev_dbg(dev->dev, "setting AMT thermals\n");
>>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>>> +        dev->amt_running = true;
>>>> +        return;
>>>> +    }
>>>> +
>>>>        /* Get the average moving average computed by auto mode algorithm */
>>>>        avg_power = amd_pmf_get_moving_avg(socket_power);
>>>>    @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>>        }
>>>>    }
>>>>    +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
>>>> +{
>>>> +    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>>>> +            dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
>>>> +    if ((config_store.current_mode == AUTO_PERFORMANCE ||
>>>> +         config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
>>>> +        config_store.current_mode !=
>>>> +        config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
>>>> +        config_store.current_mode =
>>>> +                config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
>>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>>> +    }
>>>> +    dev_dbg(dev->dev, "updated CQL thermals\n");
>>>> +}
>>>> +
>>>>    static void amd_pmf_get_power_threshold(void)
>>>>    {
>>>>        config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>>>> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
>>>> index 2b03ae1ad37f..eba8f0d79a62 100644
>>>> --- a/drivers/platform/x86/amd/pmf/cnqf.c
>>>> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
>>>> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>>>>        }
>>>>    }
>>>>    -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
>>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>>>>    {
>>>>        struct cnqf_tran_params *tp;
>>>>        int src, i, j, index = 0;
>>>> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>>>>        }
>>>>          for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
>>>> -        config_store.trans_param[src][i].timer += time_lapsed_ms;
>>>> +        config_store.trans_param[src][i].timer += time_elapsed_ms;
>>>>            config_store.trans_param[src][i].total_power += socket_power;
>>>>            config_store.trans_param[src][i].count++;
>>>>    diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>>>> index 674ddf599135..2a3dacfb2005 100644
>>>> --- a/drivers/platform/x86/amd/pmf/core.c
>>>> +++ b/drivers/platform/x86/amd/pmf/core.c
>>>> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>>        enum platform_profile_option current_profile;
>>>>        ktime_t time_elapsed_ms;
>>>>        int socket_power;
>>>> +    u8 mode;
>>>>          /* Get the current profile information */
>>>>        platform_profile_get(&current_profile);
>>>>    +    if (!dev->is_amt_event)
>>>> +        dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
>>>> +            dev->is_amt_event ? "Enabled" : "Disabled");
>>>> +
>>>>        /* Transfer table contents */
>>>>        memset(&dev->m_table, 0, sizeof(dev->m_table));
>>>>        amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
>>>> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>>        socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>>>          if (current_profile == PLATFORM_PROFILE_BALANCED) {
>>>
>>> Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
>>> on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
>>> then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?
>>>
>>> Can you please explain the expected interactions between thinkpad_acpi and
>>> this code here a bit more ?
>>>
>>> Since we now only call amd_pmf_trans_automode() based on the AMT flag and
>>> that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
>>> to be cleared when the profile is not balanced and can we thus not just drop
>>> the current_profile == PLATFORM_PROFILE_BALANCED check all together?
>>>
>>> It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
>>> then enable AMT, else disable it, logic belongs inside thinkpad_acpi
>>> and not here?
>>>
>>
>> It actually already lives in thinkpad_acpi.
>>
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Ftree%2Fdrivers%2Fplatform%2Fx86%2Fthinkpad_acpi.c%3Fh%3Dreview-hans%23n10489&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cf7d0a10b43444af391fa08da709993b3%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637946102171795208%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=cGxN8GG6LXnk%2FPDi%2BS1zNAkWRH4AiTKqF0eebcfcTRA%3D&amp;reserved=0
>>
>> By the control point from thinkpad_acpi BIOS events will be emitted controlling whether AMT is running in a given mode.
>>
>> So yes; there is no need for this here anymore.
> 
> Right. There still are some open questions / things which need fixing here though:
> 
> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>     will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>     stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>     the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>     by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>     register its platform_profile support. We cannot rely on module ordering ensuring
>     that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>     since there are no module load ordering guarantees.

This was my thought initially too while this was being developed, but 
actually there is some nuance here that is non-obvious.  The platform 
profile registering code in amd-pmf will examine bits set in the BIOS to 
decide whether or not to export platform profile support.  In Lenovo 
platforms that support thinkpad_acpi these bits are not set.  So 
platform profile support ONLY comes from thinkpad-acpi in those platforms.

> 
> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>     enable AMT and then the periodically run workqueue function from amd-pmf
>     will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>     set to low-power or performance. Should the amd-pmf code then apply the static
>     slider settings for low-power/performance which it has read from the ACPI
>     tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
> 

When thinkpad_acpi changes platform profile then a BIOS event goes 
through and amd-pmf receives that and will run based on the event.


> 3. If the answer to 2. is "Yes the amd-pmf code should apply the static-slider
>     settings" then we will still need patch 1/15 to allow the amd-pmd code to
>     read the platform-profile setting from the thinkpad_acpi platform-profile
>     provider;
>     And if the answer is "No, the thinkpad ACPI/EC will take care of this"
>     then we should probably make sure that the static slider code never runs
>     at all on thinkpads.

Yup, already handled.

> 
> Regards,
> 
> Hans
> 
> 
>    
> 
> 
> 
>>
>>> Regards,
>>>
>>> Hans
>>>
>>>> -        /* Apply the Auto Mode transition */
>>>> -        amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>>> +        if (dev->is_amt_event) {
>>>> +            /* Apply the Auto Mode transition */
>>>> +            amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>>> +        } else if (!dev->is_amt_event && dev->amt_running) {
>>>> +            pr_debug("resetting AMT thermals\n");
>>>> +            mode = amd_pmf_get_pprof_modes(dev);
>>>> +            amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
>>>> +            dev->amt_running = false;
>>>> +        }
>>>> +    } else {
>>>> +        dev->amt_running = false;
>>>>        }
>>>>          if (dev->cnqf_feat) {
>>>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>>>> index 0532f49e9613..9ae9812353cd 100644
>>>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>>>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>>>> @@ -17,6 +17,7 @@
>>>>    /* APMF Functions */
>>>>    #define APMF_FUNC_VERIFY_INTERFACE            0
>>>>    #define APMF_FUNC_GET_SYS_PARAMS            1
>>>> +#define APMF_FUNC_SBIOS_REQUESTS            2
>>>>    #define APMF_FUNC_SBIOS_HEARTBEAT            4
>>>>    #define APMF_FUNC_AUTO_MODE                    5
>>>>    #define APMF_FUNC_SET_FAN_IDX                7
>>>> @@ -51,6 +52,7 @@
>>>>    /* AMD PMF BIOS interfaces */
>>>>    struct apmf_if_functions {
>>>>        bool system_params;
>>>> +    bool sbios_requests;
>>>>        bool sbios_heartbeat;
>>>>        bool auto_mode_def;
>>>>        bool fan_table_idx;
>>>> @@ -84,6 +86,24 @@ struct apmf_system_params {
>>>>        u32 heartbeat_int;
>>>>    } __packed;
>>>>    +struct apmf_sbios_req {
>>>> +    u16 size;
>>>> +    u32 pending_req;
>>>> +    u8 rsd;
>>>> +    u8 cql_event;
>>>> +    u8 amt_event;
>>>> +    u32 fppt;
>>>> +    u32 sppt;
>>>> +    u32 fppt_apu_only;
>>>> +    u32 spl;
>>>> +    u32 stt_min_limit;
>>>> +    u8 skin_temp_apu;
>>>> +    u8 skin_temp_hs2;
>>>> +    u8 dps_enable;
>>>> +    u32 custom_policy_1;
>>>> +    u32 custom_policy_2;
>>>> +} __packed;
>>>> +
>>>>    struct apmf_fan_idx {
>>>>        u16 size;
>>>>        u8 fan_ctl_mode;
>>>> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>>>>    #endif /* CONFIG_DEBUG_FS */
>>>>        bool cnqf_feat;
>>>>        bool cnqf_running;
>>>> +    bool is_amt_event;
>>>> +    bool is_cql_event;
>>>> +    bool amt_running;
>>>>    };
>>>>      struct apmf_sps_prop_granular {
>>>> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>>>    /* Auto Mode Layer */
>>>>    void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>>>>    int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>>>> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>>>>    void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>>>>    void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>>>> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>>> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
>>>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>>      /* CnQF Layer */
>>>>    int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>>> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>>    void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>>>>    void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>>>>    void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
>>>> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>>      #endif /* PMF_H */
>>>
>>
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 13:43         ` Limonciello, Mario
@ 2022-07-28 14:09           ` Hans de Goede
  2022-07-28 14:38             ` Limonciello, Mario
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-28 14:09 UTC (permalink / raw)
  To: Limonciello, Mario, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/28/22 15:43, Limonciello, Mario wrote:
> On 7/28/2022 08:03, Hans de Goede wrote:
>> Hi,
>>
>> On 7/28/22 01:52, Limonciello, Mario wrote:
>>> On 7/27/2022 16:46, Hans de Goede wrote:
>>>> <resend with Cc list fixed>
>>>>
>>>> Hi,
>>>>
>>>> On 7/12/22 16:58, Shyam Sundar S K wrote:
>>>>> The transition to auto-mode happens when the PMF driver receives
>>>>> AMT (Auto Mode transition) event. transition logic will reside in the
>>>>> PMF driver but the events would come from other supported drivers[1].
>>>>>
>>>>> The thermal parameters would vary between when a performance "on-lap" mode
>>>>> is detected and versus when not. The CQL event would get triggered from
>>>>> other drivers, so that PMF driver would adjust the system thermal config
>>>>> based on the ACPI inputs.
>>>>>
>>>>> OEMs can control whether or not to enable AMT or CQL via other supported
>>>>> drivers[1] but the actual transition logic resides in the AMD PMF driver.
>>>>> When an AMT event is received the automatic mode transition RAPL algorithm
>>>>> will run. When a CQL event is received an performance "on-lap" mode will
>>>>> be enabled and thermal parameters will be adjusted accordingly.
>>>>>
>>>>> [1]
>>>>> Link: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Fcommit%2F%3Fh%3Dreview-hans%26id%3D755b249250df1b612d982f3b702c831b26ecdf73&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cf7d0a10b43444af391fa08da709993b3%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637946102171795208%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=GRSR4jEbQu7yaY6CS%2BKienSw7majkwcazo8xoKHd2pA%3D&amp;reserved=0
>>>>>
>>>>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>>>>> ---
>>>>>    drivers/platform/x86/amd/pmf/acpi.c      | 90 +++++++++++++++++++++++-
>>>>>    drivers/platform/x86/amd/pmf/auto-mode.c | 22 ++++++
>>>>>    drivers/platform/x86/amd/pmf/cnqf.c      |  4 +-
>>>>>    drivers/platform/x86/amd/pmf/core.c      | 18 ++++-
>>>>>    drivers/platform/x86/amd/pmf/pmf.h       | 29 +++++++-
>>>>>    5 files changed, 156 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>>>>> index e9f33e61659f..4bde86aeafa0 100644
>>>>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>>>>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>>>>> @@ -12,6 +12,8 @@
>>>>>    #include "pmf.h"
>>>>>      #define APMF_METHOD "\\_SB_.PMF_.APMF"
>>>>> +#define APMF_CQL_NOTIFICATION    2
>>>>> +#define APMF_AMT_NOTIFICATION    3
>>>>>      static unsigned long supported_func;
>>>>>    @@ -55,6 +57,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>>>>    {
>>>>>        func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>>>>        func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>>>> +    func->sbios_requests = mask & APMF_FUNC_SBIOS_REQUESTS;
>>>>>        func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>>>>        func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>>>>        func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>>>> @@ -292,6 +295,36 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>>>        return err;
>>>>>    }
>>>>>    +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req)
>>>>> +{
>>>>> +    union acpi_object *info;
>>>>> +    size_t size;
>>>>> +    int err = 0;
>>>>> +
>>>>> +    info = apmf_if_call(ampf_if, APMF_FUNC_SBIOS_REQUESTS, NULL);
>>>>> +    if (!info)
>>>>> +        return -EIO;
>>>>> +
>>>>> +    size = *(u16 *)info->buffer.pointer;
>>>>> +
>>>>> +    if (size < sizeof(union acpi_object)) {
>>>>> +        pr_err("PMF: buffer too small %zu\n", size);
>>>>> +        err = -EINVAL;
>>>>> +        goto out;
>>>>> +    }
>>>>> +
>>>>> +    size = min(sizeof(*req), size);
>>>>> +    memset(req, 0, sizeof(*req));
>>>>> +    memcpy(req, info->buffer.pointer, size);
>>>>> +
>>>>> +    pr_debug("PMF: %s: pending_req:%d cql:%d amt:%d\n", __func__,
>>>>> +         req->pending_req, req->cql_event, req->amt_event);
>>>>> +
>>>>> +out:
>>>>> +    kfree(info);
>>>>> +    return err;
>>>>> +}
>>>>> +
>>>>>    static acpi_handle apmf_if_probe_handle(void)
>>>>>    {
>>>>>        acpi_handle handle = NULL;
>>>>> @@ -312,18 +345,62 @@ static acpi_handle apmf_if_probe_handle(void)
>>>>>        return handle;
>>>>>    }
>>>>>    +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>>>> +{
>>>>> +    struct amd_pmf_dev *pmf_dev = data;
>>>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>>>> +    int ret;
>>>>> +
>>>>> +    if (apmf_if->func.sbios_requests) {
>>>>> +        struct apmf_sbios_req req;
>>>>> +
>>>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>>>> +        if (ret) {
>>>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>>>> +            return;
>>>>> +        }
>>>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>>>> +            if (req.amt_event)
>>>>> +                pmf_dev->is_amt_event = true;
>>>>> +            else
>>>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>>>> +        }
>>>>> +
>>>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>>>> +            if (req.cql_event)
>>>>> +                pmf_dev->is_cql_event = true;
>>>>> +            else
>>>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>>>> +
>>>>> +            /* update the target mode information */
>>>>> +            amd_pmf_update_2_cql(pmf_dev);
>>>>> +        }
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>    void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>>>>>    {
>>>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>>>> +
>>>>>        if (pmf_dev->apmf_if->func.sbios_heartbeat)
>>>>>            cancel_delayed_work_sync(&pmf_dev->heart_beat);
>>>>> +
>>>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE))
>>>>> +        acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
>>>>> +                       apmf_event_handler);
>>>>>    }
>>>>>      int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>>>    {
>>>>> +    acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
>>>>>        struct apmf_notification_cfg *n;
>>>>>        acpi_handle apmf_if_handle;
>>>>>        struct apmf_if *apmf_if;
>>>>> -    int ret;
>>>>> +    int ret, status;
>>>>>          apmf_if_handle = apmf_if_probe_handle();
>>>>>        if (!apmf_if_handle)
>>>>> @@ -360,6 +437,17 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>>>>            schedule_delayed_work(&pmf_dev->heart_beat, 0);
>>>>>        }
>>>>>    +    /* Install the APMF Notify handler */
>>>>> +    if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>>>> +        status = acpi_install_notify_handler(ahandle,
>>>>> +                             ACPI_ALL_NOTIFY,
>>>>> +                             apmf_event_handler, pmf_dev);
>>>>> +        if (ACPI_FAILURE(status)) {
>>>>> +            dev_err(pmf_dev->dev, "failed to install notify handler\n");
>>>>> +            return -ENODEV;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>>    out:
>>>>>        return ret;
>>>>>    }
>>>>> diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
>>>>> index 954fde25e71e..a85ef4b95cdb 100644
>>>>> --- a/drivers/platform/x86/amd/pmf/auto-mode.c
>>>>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>>>>> @@ -111,6 +111,13 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>>>        bool update = false;
>>>>>        int i, j;
>>>>>    +    if (!dev->amt_running && dev->is_amt_event) {
>>>>> +        dev_dbg(dev->dev, "setting AMT thermals\n");
>>>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>>>> +        dev->amt_running = true;
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>>        /* Get the average moving average computed by auto mode algorithm */
>>>>>        avg_power = amd_pmf_get_moving_avg(socket_power);
>>>>>    @@ -161,6 +168,21 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
>>>>>        }
>>>>>    }
>>>>>    +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
>>>>> +{
>>>>> +    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
>>>>> +            dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
>>>>> +    if ((config_store.current_mode == AUTO_PERFORMANCE ||
>>>>> +         config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
>>>>> +        config_store.current_mode !=
>>>>> +        config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
>>>>> +        config_store.current_mode =
>>>>> +                config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
>>>>> +        amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>>>> +    }
>>>>> +    dev_dbg(dev->dev, "updated CQL thermals\n");
>>>>> +}
>>>>> +
>>>>>    static void amd_pmf_get_power_threshold(void)
>>>>>    {
>>>>>        config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold =
>>>>> diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
>>>>> index 2b03ae1ad37f..eba8f0d79a62 100644
>>>>> --- a/drivers/platform/x86/amd/pmf/cnqf.c
>>>>> +++ b/drivers/platform/x86/amd/pmf/cnqf.c
>>>>> @@ -101,7 +101,7 @@ static const char *state_as_str(unsigned int state)
>>>>>        }
>>>>>    }
>>>>>    -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)
>>>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms)
>>>>>    {
>>>>>        struct cnqf_tran_params *tp;
>>>>>        int src, i, j, index = 0;
>>>>> @@ -117,7 +117,7 @@ void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_
>>>>>        }
>>>>>          for (i = 0; i < CNQF_TRANSITION_MAX; i++) {
>>>>> -        config_store.trans_param[src][i].timer += time_lapsed_ms;
>>>>> +        config_store.trans_param[src][i].timer += time_elapsed_ms;
>>>>>            config_store.trans_param[src][i].total_power += socket_power;
>>>>>            config_store.trans_param[src][i].count++;
>>>>>    diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>>>>> index 674ddf599135..2a3dacfb2005 100644
>>>>> --- a/drivers/platform/x86/amd/pmf/core.c
>>>>> +++ b/drivers/platform/x86/amd/pmf/core.c
>>>>> @@ -109,10 +109,15 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>>>        enum platform_profile_option current_profile;
>>>>>        ktime_t time_elapsed_ms;
>>>>>        int socket_power;
>>>>> +    u8 mode;
>>>>>          /* Get the current profile information */
>>>>>        platform_profile_get(&current_profile);
>>>>>    +    if (!dev->is_amt_event)
>>>>> +        dev_dbg(dev->dev, "%s amt event: %s\n", __func__,
>>>>> +            dev->is_amt_event ? "Enabled" : "Disabled");
>>>>> +
>>>>>        /* Transfer table contents */
>>>>>        memset(&dev->m_table, 0, sizeof(dev->m_table));
>>>>>        amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
>>>>> @@ -123,8 +128,17 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>>>        socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>>>>          if (current_profile == PLATFORM_PROFILE_BALANCED) {
>>>>
>>>> Hmm, I guess this is also why the platform_profile_get() is necessary ? Because
>>>> on Thinkpads thinkpad_acpi is expected to be the platform_profile provider and
>>>> then the PMF code wants to know the platform_profile setting from thinkpad_acpi ?
>>>>
>>>> Can you please explain the expected interactions between thinkpad_acpi and
>>>> this code here a bit more ?
>>>>
>>>> Since we now only call amd_pmf_trans_automode() based on the AMT flag and
>>>> that flag is controlled by the thinkpad BIOS/EC can we not expect that flag
>>>> to be cleared when the profile is not balanced and can we thus not just drop
>>>> the current_profile == PLATFORM_PROFILE_BALANCED check all together?
>>>>
>>>> It seems to me that if current_profile == PLATFORM_PROFILE_BALANCED
>>>> then enable AMT, else disable it, logic belongs inside thinkpad_acpi
>>>> and not here?
>>>>
>>>
>>> It actually already lives in thinkpad_acpi.
>>>
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fpdx86%2Fplatform-drivers-x86.git%2Ftree%2Fdrivers%2Fplatform%2Fx86%2Fthinkpad_acpi.c%3Fh%3Dreview-hans%23n10489&amp;data=05%7C01%7Cmario.limonciello%40amd.com%7Cf7d0a10b43444af391fa08da709993b3%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637946102171795208%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=cGxN8GG6LXnk%2FPDi%2BS1zNAkWRH4AiTKqF0eebcfcTRA%3D&amp;reserved=0
>>>
>>> By the control point from thinkpad_acpi BIOS events will be emitted controlling whether AMT is running in a given mode.
>>>
>>> So yes; there is no need for this here anymore.
>>
>> Right. There still are some open questions / things which need fixing here though:
>>
>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>     will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>     stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>     the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>     by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>     register its platform_profile support. We cannot rely on module ordering ensuring
>>     that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>     since there are no module load ordering guarantees.
> 
> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.

Right, Shyam mentioned this in another part of the thread. As I
mentioned there IHMO it would still be good to check this in the driver
though. To catch cases where a BIOS for some reasons advertises an
unexpected combination of features.

>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>     enable AMT and then the periodically run workqueue function from amd-pmf
>>     will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>     set to low-power or performance. Should the amd-pmf code then apply the static
>>     slider settings for low-power/performance which it has read from the ACPI
>>     tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>
> 
> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.

Hmm, I don't remember seeing anything for this in the patches. Actually this
reminds me that the code should probably reschedule (using mod_delayed_work)
the work to run immediately after a BIOS event, rather then waiting for
the next normally scheduled run.

But even then I don't remember seeing any code related to catching
platform-profile changes done outside amd-pmf... ?

There is this bit:

 	if (current_profile == PLATFORM_PROFILE_BALANCED) {
-		/* Apply the Auto Mode transition */
-		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+		if (dev->is_amt_event) {
+			/* Apply the Auto Mode transition */
+			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+		} else if (!dev->is_amt_event && dev->amt_running) {
+			pr_debug("resetting AMT thermals\n");
+			mode = amd_pmf_get_pprof_modes(dev);
+			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
+			dev->amt_running = false;
+		}
+	} else {
+		dev->amt_running = false;
 	}

But the new code here only applies the static slider settings on
is_amt_event edges (going from 1->0) and if the static slider support
bits are supposed to not be set then amd_pmf_load_defaults_sps() will
not have run because
is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR) will return
false.

So the values being set by amd_pmf_update_slider() will not have been
initialized and it will be setting everything to 0.

Also amd_pmf_get_pprof_modes() will always return POWER_MODE_POWER_SAVER
since pmf->current_profile is left at its 0 value (from kzalloc) in
this case.

So it seems that the code path where AMT is being disabled here is
buggy and it still is not clear to me where the limits get set
when thinkpad_acpi's platform_profile gets set to low-power
or performance.

Regards,

Hans


> 
> 
>> 3. If the answer to 2. is "Yes the amd-pmf code should apply the static-slider
>>     settings" then we will still need patch 1/15 to allow the amd-pmd code to
>>     read the platform-profile setting from the thinkpad_acpi platform-profile
>>     provider;
>>     And if the answer is "No, the thinkpad ACPI/EC will take care of this"
>>     then we should probably make sure that the static slider code never runs
>>     at all on thinkpads.
> 
> Yup, already handled.
> 
>>
>> Regards,
>>
>> Hans
>>
>>
>>   
>>
>>
>>>
>>>> Regards,
>>>>
>>>> Hans
>>>>
>>>>> -        /* Apply the Auto Mode transition */
>>>>> -        amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>>>> +        if (dev->is_amt_event) {
>>>>> +            /* Apply the Auto Mode transition */
>>>>> +            amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>>>> +        } else if (!dev->is_amt_event && dev->amt_running) {
>>>>> +            pr_debug("resetting AMT thermals\n");
>>>>> +            mode = amd_pmf_get_pprof_modes(dev);
>>>>> +            amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
>>>>> +            dev->amt_running = false;
>>>>> +        }
>>>>> +    } else {
>>>>> +        dev->amt_running = false;
>>>>>        }
>>>>>          if (dev->cnqf_feat) {
>>>>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>>>>> index 0532f49e9613..9ae9812353cd 100644
>>>>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>>>>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>>>>> @@ -17,6 +17,7 @@
>>>>>    /* APMF Functions */
>>>>>    #define APMF_FUNC_VERIFY_INTERFACE            0
>>>>>    #define APMF_FUNC_GET_SYS_PARAMS            1
>>>>> +#define APMF_FUNC_SBIOS_REQUESTS            2
>>>>>    #define APMF_FUNC_SBIOS_HEARTBEAT            4
>>>>>    #define APMF_FUNC_AUTO_MODE                    5
>>>>>    #define APMF_FUNC_SET_FAN_IDX                7
>>>>> @@ -51,6 +52,7 @@
>>>>>    /* AMD PMF BIOS interfaces */
>>>>>    struct apmf_if_functions {
>>>>>        bool system_params;
>>>>> +    bool sbios_requests;
>>>>>        bool sbios_heartbeat;
>>>>>        bool auto_mode_def;
>>>>>        bool fan_table_idx;
>>>>> @@ -84,6 +86,24 @@ struct apmf_system_params {
>>>>>        u32 heartbeat_int;
>>>>>    } __packed;
>>>>>    +struct apmf_sbios_req {
>>>>> +    u16 size;
>>>>> +    u32 pending_req;
>>>>> +    u8 rsd;
>>>>> +    u8 cql_event;
>>>>> +    u8 amt_event;
>>>>> +    u32 fppt;
>>>>> +    u32 sppt;
>>>>> +    u32 fppt_apu_only;
>>>>> +    u32 spl;
>>>>> +    u32 stt_min_limit;
>>>>> +    u8 skin_temp_apu;
>>>>> +    u8 skin_temp_hs2;
>>>>> +    u8 dps_enable;
>>>>> +    u32 custom_policy_1;
>>>>> +    u32 custom_policy_2;
>>>>> +} __packed;
>>>>> +
>>>>>    struct apmf_fan_idx {
>>>>>        u16 size;
>>>>>        u8 fan_ctl_mode;
>>>>> @@ -171,6 +191,9 @@ struct amd_pmf_dev {
>>>>>    #endif /* CONFIG_DEBUG_FS */
>>>>>        bool cnqf_feat;
>>>>>        bool cnqf_running;
>>>>> +    bool is_amt_event;
>>>>> +    bool is_cql_event;
>>>>> +    bool amt_running;
>>>>>    };
>>>>>      struct apmf_sps_prop_granular {
>>>>> @@ -417,9 +440,11 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx);
>>>>>    /* Auto Mode Layer */
>>>>>    void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev);
>>>>>    int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data);
>>>>> +int apmf_get_sbios_requests(struct apmf_if *ampf_if, struct apmf_sbios_req *req);
>>>>>    void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
>>>>>    void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
>>>>> -void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>>>> +void amd_pmf_update_2_cql(struct amd_pmf_dev *dev);
>>>>> +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>>>      /* CnQF Layer */
>>>>>    int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>>>> @@ -427,6 +452,6 @@ int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_o
>>>>>    void amd_pmf_init_cnqf(struct amd_pmf_dev *dev);
>>>>>    void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
>>>>>    void amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev);
>>>>> -void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
>>>>> +void amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
>>>>>      #endif /* PMF_H */
>>>>
>>>
>>
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 14:09           ` Hans de Goede
@ 2022-07-28 14:38             ` Limonciello, Mario
  2022-07-28 17:46               ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-28 14:38 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson


>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>      will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>      stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>      the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>      by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>      register its platform_profile support. We cannot rely on module ordering ensuring
>>>      that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>      since there are no module load ordering guarantees.
>>
>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
> 
> Right, Shyam mentioned this in another part of the thread. As I
> mentioned there IHMO it would still be good to check this in the driver
> though. To catch cases where a BIOS for some reasons advertises an
> unexpected combination of features.
> 
>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>      enable AMT and then the periodically run workqueue function from amd-pmf
>>>      will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>      set to low-power or performance. Should the amd-pmf code then apply the static
>>>      slider settings for low-power/performance which it has read from the ACPI
>>>      tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>
>>
>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
> 
> Hmm, I don't remember seeing anything for this in the patches. Actually this
> reminds me that the code should probably reschedule (using mod_delayed_work)
> the work to run immediately after a BIOS event, rather then waiting for
> the next normally scheduled run.
> 
> But even then I don't remember seeing any code related to catching
> platform-profile changes done outside amd-pmf... ?

It's not a platform profile change - it's an ACPI event.

When a user changes a platform profile then thinkpad_acpi will see 
whether it's balanced or not.  When changing to/from balanced 
thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.

This is the code you're looking for (in this specific patch):

+static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
+{
+	struct amd_pmf_dev *pmf_dev = data;
+	struct apmf_if *apmf_if = pmf_dev->apmf_if;
+	int ret;
+
+	if (apmf_if->func.sbios_requests) {
+		struct apmf_sbios_req req;
+
+		ret = apmf_get_sbios_requests(apmf_if, &req);
+		if (ret) {
+			dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
+			return;
+		}
+		if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
+			pr_debug("PMF: AMT is supported and notifications %s\n",
+				 req.amt_event ? "Enabled" : "Disabled");
+			if (req.amt_event)
+				pmf_dev->is_amt_event = true;
+			else
+				pmf_dev->is_amt_event = !!req.amt_event;
+		}
+
+		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
+			pr_debug("PMF: CQL is supported and notifications %s\n",
+				 req.cql_event ? "Enabled" : "Disabled");
+			if (req.cql_event)
+				pmf_dev->is_cql_event = true;
+			else
+				pmf_dev->is_cql_event = !!req.cql_event;
+
+			/* update the target mode information */
+			amd_pmf_update_2_cql(pmf_dev);
+		}
+	}
+}
+

> 
> There is this bit:
> 
>   	if (current_profile == PLATFORM_PROFILE_BALANCED) {
> -		/* Apply the Auto Mode transition */
> -		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		if (dev->is_amt_event) {
> +			/* Apply the Auto Mode transition */
> +			amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> +		} else if (!dev->is_amt_event && dev->amt_running) {
> +			pr_debug("resetting AMT thermals\n");
> +			mode = amd_pmf_get_pprof_modes(dev);
> +			amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
> +			dev->amt_running = false;
> +		}
> +	} else {
> +		dev->amt_running = false;
>   	}
> 
> But the new code here only applies the static slider settings on
> is_amt_event edges (going from 1->0) and if the static slider support
> bits are supposed to not be set then amd_pmf_load_defaults_sps() will
> not have run because
> is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR) will return
> false.
> 
> So the values being set by amd_pmf_update_slider() will not have been
> initialized and it will be setting everything to 0.

> 
> Also amd_pmf_get_pprof_modes() will always return POWER_MODE_POWER_SAVER
> since pmf->current_profile is left at its 0 value (from kzalloc) in
> this case.
> 
> So it seems that the code path where AMT is being disabled here is
> buggy and it still is not clear to me where the limits get set
> when thinkpad_acpi's platform_profile gets set to low-power
> or performance.
> 

I think you're right an extra check should end up in 
amd_pmf_update_slider that only runs code when the static slider returns 
true.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry
  2022-07-27 21:41   ` Hans de Goede
@ 2022-07-28 17:44     ` Shyam Sundar S K
  0 siblings, 0 replies; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-28 17:44 UTC (permalink / raw)
  To: Hans de Goede, markgross; +Cc: platform-driver-x86, Patil.Reddy

Hi Hans,

On 7/28/2022 3:11 AM, Hans de Goede wrote:
> Hi,
> 
> On 7/12/22 16:58, Shyam Sundar S K wrote:
>> Update the MAINTAINERS file with AMD PMF driver details.
>>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> 
> Thanks, patch looks good to me:
> 
> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
> 
> Regards,
> 
> Hans

I attempted to address all your review remarks in v1. Please have a
relook at the revision 2.

Thanks,
Shyam

> 
> 
>> ---
>>  MAINTAINERS | 7 +++++++
>>  1 file changed, 7 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index e01478062c56..d3f6cabcaab2 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -998,6 +998,13 @@ L:	platform-driver-x86@vger.kernel.org
>>  S:	Maintained
>>  F:	drivers/platform/x86/amd/pmc.c
>>  
>> +AMD PMF DRIVER
>> +M:	Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> +L:	platform-driver-x86@vger.kernel.org
>> +S:	Maintained
>> +F:	Documentation/ABI/testing/sysfs-amd-pmf
>> +F:	drivers/platform/x86/amd/pmf/
>> +
>>  AMD HSMP DRIVER
>>  M:	Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
>>  R:	Carlos Bilbao <carlos.bilbao@amd.com>
> 

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 14:38             ` Limonciello, Mario
@ 2022-07-28 17:46               ` Hans de Goede
  2022-07-28 18:06                 ` Limonciello, Mario
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-28 17:46 UTC (permalink / raw)
  To: Limonciello, Mario, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/28/22 16:38, Limonciello, Mario wrote:
> 
>>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>>      will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>>      stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>>      the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>>      by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>>      register its platform_profile support. We cannot rely on module ordering ensuring
>>>>      that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>>      since there are no module load ordering guarantees.
>>>
>>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
>>
>> Right, Shyam mentioned this in another part of the thread. As I
>> mentioned there IHMO it would still be good to check this in the driver
>> though. To catch cases where a BIOS for some reasons advertises an
>> unexpected combination of features.
>>
>>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>>      enable AMT and then the periodically run workqueue function from amd-pmf
>>>>      will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>>      set to low-power or performance. Should the amd-pmf code then apply the static
>>>>      slider settings for low-power/performance which it has read from the ACPI
>>>>      tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>>
>>>
>>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
>>
>> Hmm, I don't remember seeing anything for this in the patches. Actually this
>> reminds me that the code should probably reschedule (using mod_delayed_work)
>> the work to run immediately after a BIOS event, rather then waiting for
>> the next normally scheduled run.
>>
>> But even then I don't remember seeing any code related to catching
>> platform-profile changes done outside amd-pmf... ?
> 
> It's not a platform profile change - it's an ACPI event.
> 
> When a user changes a platform profile then thinkpad_acpi will see whether it's balanced or not.  When changing to/from balanced thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.
> 
> This is the code you're looking for (in this specific patch):
> 
> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
> +{
> +    struct amd_pmf_dev *pmf_dev = data;
> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
> +    int ret;
> +
> +    if (apmf_if->func.sbios_requests) {
> +        struct apmf_sbios_req req;
> +
> +        ret = apmf_get_sbios_requests(apmf_if, &req);
> +        if (ret) {
> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
> +            return;
> +        }
> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
> +            pr_debug("PMF: AMT is supported and notifications %s\n",
> +                 req.amt_event ? "Enabled" : "Disabled");
> +            if (req.amt_event)
> +                pmf_dev->is_amt_event = true;
> +            else
> +                pmf_dev->is_amt_event = !!req.amt_event;
> +        }
> +
> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
> +            pr_debug("PMF: CQL is supported and notifications %s\n",
> +                 req.cql_event ? "Enabled" : "Disabled");
> +            if (req.cql_event)
> +                pmf_dev->is_cql_event = true;
> +            else
> +                pmf_dev->is_cql_event = !!req.cql_event;
> +
> +            /* update the target mode information */
> +            amd_pmf_update_2_cql(pmf_dev);
> +        }
> +    }
> +}
> +

Right this is the AMT on/off path that bit I understand.
This happens when switching to / away from balanced mode.

My question is what does the equivalent of these lines:

+		amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
+		amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
+		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
+				 config_store.prop[src][idx].sppt_apu_only, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
+				 config_store.prop[src][idx].stt_min, NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
+		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);

When the profile is switched (by userspace, or through the hotkeys on
the laptop) to low-power or to performance mode ?

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 17:46               ` Hans de Goede
@ 2022-07-28 18:06                 ` Limonciello, Mario
  2022-07-28 18:17                   ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-28 18:06 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

On 7/28/2022 12:46, Hans de Goede wrote:
> Hi,
> 
> On 7/28/22 16:38, Limonciello, Mario wrote:
>>
>>>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>>>       will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>>>       stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>>>       the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>>>       by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>>>       register its platform_profile support. We cannot rely on module ordering ensuring
>>>>>       that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>>>       since there are no module load ordering guarantees.
>>>>
>>>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
>>>
>>> Right, Shyam mentioned this in another part of the thread. As I
>>> mentioned there IHMO it would still be good to check this in the driver
>>> though. To catch cases where a BIOS for some reasons advertises an
>>> unexpected combination of features.
>>>
>>>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>>>       enable AMT and then the periodically run workqueue function from amd-pmf
>>>>>       will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>>>       set to low-power or performance. Should the amd-pmf code then apply the static
>>>>>       slider settings for low-power/performance which it has read from the ACPI
>>>>>       tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>>>
>>>>
>>>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
>>>
>>> Hmm, I don't remember seeing anything for this in the patches. Actually this
>>> reminds me that the code should probably reschedule (using mod_delayed_work)
>>> the work to run immediately after a BIOS event, rather then waiting for
>>> the next normally scheduled run.
>>>
>>> But even then I don't remember seeing any code related to catching
>>> platform-profile changes done outside amd-pmf... ?
>>
>> It's not a platform profile change - it's an ACPI event.
>>
>> When a user changes a platform profile then thinkpad_acpi will see whether it's balanced or not.  When changing to/from balanced thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.
>>
>> This is the code you're looking for (in this specific patch):
>>
>> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>> +{
>> +    struct amd_pmf_dev *pmf_dev = data;
>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>> +    int ret;
>> +
>> +    if (apmf_if->func.sbios_requests) {
>> +        struct apmf_sbios_req req;
>> +
>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>> +        if (ret) {
>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>> +            return;
>> +        }
>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>> +                 req.amt_event ? "Enabled" : "Disabled");
>> +            if (req.amt_event)
>> +                pmf_dev->is_amt_event = true;
>> +            else
>> +                pmf_dev->is_amt_event = !!req.amt_event;
>> +        }
>> +
>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>> +                 req.cql_event ? "Enabled" : "Disabled");
>> +            if (req.cql_event)
>> +                pmf_dev->is_cql_event = true;
>> +            else
>> +                pmf_dev->is_cql_event = !!req.cql_event;
>> +
>> +            /* update the target mode information */
>> +            amd_pmf_update_2_cql(pmf_dev);
>> +        }
>> +    }
>> +}
>> +
> 
> Right this is the AMT on/off path that bit I understand.
> This happens when switching to / away from balanced mode.
> 
> My question is what does the equivalent of these lines:
> 
> +		amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
> +		amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
> +		amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
> +				 config_store.prop[src][idx].sppt_apu_only, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
> +				 config_store.prop[src][idx].stt_min, NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
> +		amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
> +				 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
> 
> When the profile is switched (by userspace, or through the hotkeys on
> the laptop) to low-power or to performance mode ?

Lenovo's firmware will handle the equivalent of changing relevant values 
for their platform through a BIOS interface in this case when they 
change ACPI platform profiles.  You will see in their driver something 
call "PSC" mode, and this is exactly that type of stuff.

> 
> Regards,
> 
> Hans
> 


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 18:06                 ` Limonciello, Mario
@ 2022-07-28 18:17                   ` Hans de Goede
  2022-07-28 21:01                     ` Limonciello, Mario
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-28 18:17 UTC (permalink / raw)
  To: Limonciello, Mario, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/28/22 20:06, Limonciello, Mario wrote:
> On 7/28/2022 12:46, Hans de Goede wrote:
>> Hi,
>>
>> On 7/28/22 16:38, Limonciello, Mario wrote:
>>>
>>>>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>>>>       will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>>>>       stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>>>>       the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>>>>       by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>>>>       register its platform_profile support. We cannot rely on module ordering ensuring
>>>>>>       that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>>>>       since there are no module load ordering guarantees.
>>>>>
>>>>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
>>>>
>>>> Right, Shyam mentioned this in another part of the thread. As I
>>>> mentioned there IHMO it would still be good to check this in the driver
>>>> though. To catch cases where a BIOS for some reasons advertises an
>>>> unexpected combination of features.
>>>>
>>>>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>>>>       enable AMT and then the periodically run workqueue function from amd-pmf
>>>>>>       will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>>>>       set to low-power or performance. Should the amd-pmf code then apply the static
>>>>>>       slider settings for low-power/performance which it has read from the ACPI
>>>>>>       tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>>>>
>>>>>
>>>>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
>>>>
>>>> Hmm, I don't remember seeing anything for this in the patches. Actually this
>>>> reminds me that the code should probably reschedule (using mod_delayed_work)
>>>> the work to run immediately after a BIOS event, rather then waiting for
>>>> the next normally scheduled run.
>>>>
>>>> But even then I don't remember seeing any code related to catching
>>>> platform-profile changes done outside amd-pmf... ?
>>>
>>> It's not a platform profile change - it's an ACPI event.
>>>
>>> When a user changes a platform profile then thinkpad_acpi will see whether it's balanced or not.  When changing to/from balanced thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.
>>>
>>> This is the code you're looking for (in this specific patch):
>>>
>>> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>> +{
>>> +    struct amd_pmf_dev *pmf_dev = data;
>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>> +    int ret;
>>> +
>>> +    if (apmf_if->func.sbios_requests) {
>>> +        struct apmf_sbios_req req;
>>> +
>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>> +        if (ret) {
>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>> +            return;
>>> +        }
>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>> +            if (req.amt_event)
>>> +                pmf_dev->is_amt_event = true;
>>> +            else
>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>> +        }
>>> +
>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>> +            if (req.cql_event)
>>> +                pmf_dev->is_cql_event = true;
>>> +            else
>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>> +
>>> +            /* update the target mode information */
>>> +            amd_pmf_update_2_cql(pmf_dev);
>>> +        }
>>> +    }
>>> +}
>>> +
>>
>> Right this is the AMT on/off path that bit I understand.
>> This happens when switching to / away from balanced mode.
>>
>> My question is what does the equivalent of these lines:
>>
>> +        amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
>> +        amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
>> +        amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
>> +        amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
>> +                 config_store.prop[src][idx].sppt_apu_only, NULL);
>> +        amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
>> +                 config_store.prop[src][idx].stt_min, NULL);
>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
>>
>> When the profile is switched (by userspace, or through the hotkeys on
>> the laptop) to low-power or to performance mode ?
> 
> Lenovo's firmware will handle the equivalent of changing relevant values for their platform through a BIOS interface in this case when they change ACPI platform profiles.  You will see in their driver something call "PSC" mode, and this is exactly that type of stuff.

Ok I see, thank you for clarifying this.

So as for the AMT mode, since that is Lenovo only, I guess that means
that there is no need to do call amd_pmf_update_slider() when AMT
is being disabled since at this point the firmware will have
already set the values.

Actually this seems to mean that we must ensure that the AMD-PMF
code stops touching these settings as soon as the event is received.

Which would imply killing the periodic work when an AMT off event
is received from within the event handling and then restating it
when AMT is on (and making sure the work being queued or not state
matches the AMT on/off state at driver probe time) ?

Regards,

Hans



^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 18:17                   ` Hans de Goede
@ 2022-07-28 21:01                     ` Limonciello, Mario
  2022-07-29 11:03                       ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-28 21:01 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

On 7/28/2022 13:17, Hans de Goede wrote:
> Hi,
> 
> On 7/28/22 20:06, Limonciello, Mario wrote:
>> On 7/28/2022 12:46, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 7/28/22 16:38, Limonciello, Mario wrote:
>>>>
>>>>>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>>>>>        will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>>>>>        stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>>>>>        the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>>>>>        by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>>>>>        register its platform_profile support. We cannot rely on module ordering ensuring
>>>>>>>        that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>>>>>        since there are no module load ordering guarantees.
>>>>>>
>>>>>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
>>>>>
>>>>> Right, Shyam mentioned this in another part of the thread. As I
>>>>> mentioned there IHMO it would still be good to check this in the driver
>>>>> though. To catch cases where a BIOS for some reasons advertises an
>>>>> unexpected combination of features.
>>>>>
>>>>>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>>>>>        enable AMT and then the periodically run workqueue function from amd-pmf
>>>>>>>        will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>>>>>        set to low-power or performance. Should the amd-pmf code then apply the static
>>>>>>>        slider settings for low-power/performance which it has read from the ACPI
>>>>>>>        tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>>>>>
>>>>>>
>>>>>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
>>>>>
>>>>> Hmm, I don't remember seeing anything for this in the patches. Actually this
>>>>> reminds me that the code should probably reschedule (using mod_delayed_work)
>>>>> the work to run immediately after a BIOS event, rather then waiting for
>>>>> the next normally scheduled run.
>>>>>
>>>>> But even then I don't remember seeing any code related to catching
>>>>> platform-profile changes done outside amd-pmf... ?
>>>>
>>>> It's not a platform profile change - it's an ACPI event.
>>>>
>>>> When a user changes a platform profile then thinkpad_acpi will see whether it's balanced or not.  When changing to/from balanced thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.
>>>>
>>>> This is the code you're looking for (in this specific patch):
>>>>
>>>> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>>> +{
>>>> +    struct amd_pmf_dev *pmf_dev = data;
>>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>>> +    int ret;
>>>> +
>>>> +    if (apmf_if->func.sbios_requests) {
>>>> +        struct apmf_sbios_req req;
>>>> +
>>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>>> +        if (ret) {
>>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>>> +            return;
>>>> +        }
>>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>>> +            if (req.amt_event)
>>>> +                pmf_dev->is_amt_event = true;
>>>> +            else
>>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>>> +        }
>>>> +
>>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>>> +            if (req.cql_event)
>>>> +                pmf_dev->is_cql_event = true;
>>>> +            else
>>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>>> +
>>>> +            /* update the target mode information */
>>>> +            amd_pmf_update_2_cql(pmf_dev);
>>>> +        }
>>>> +    }
>>>> +}
>>>> +
>>>
>>> Right this is the AMT on/off path that bit I understand.
>>> This happens when switching to / away from balanced mode.
>>>
>>> My question is what does the equivalent of these lines:
>>>
>>> +        amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
>>> +        amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
>>> +        amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
>>> +        amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
>>> +                 config_store.prop[src][idx].sppt_apu_only, NULL);
>>> +        amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
>>> +                 config_store.prop[src][idx].stt_min, NULL);
>>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
>>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
>>>
>>> When the profile is switched (by userspace, or through the hotkeys on
>>> the laptop) to low-power or to performance mode ?
>>
>> Lenovo's firmware will handle the equivalent of changing relevant values for their platform through a BIOS interface in this case when they change ACPI platform profiles.  You will see in their driver something call "PSC" mode, and this is exactly that type of stuff.
> 
> Ok I see, thank you for clarifying this.
> 
> So as for the AMT mode, since that is Lenovo only, I guess that means
> that there is no need to do call amd_pmf_update_slider() when AMT
> is being disabled since at this point the firmware will have
> already set the values.

Yeah, Shyam made this modification for v2 to make sure that code path 
isn't called unless static slider was set in the BIOS.

> 
> Actually this seems to mean that we must ensure that the AMD-PMF
> code stops touching these settings as soon as the event is received.
> 
> Which would imply killing the periodic work when an AMT off event
> is received from within the event handling and then restating it
> when AMT is on (and making sure the work being queued or not state
> matches the AMT on/off state at driver probe time) ?
> 

At first glance this seems plausible, but actually I think it should 
stay as is because CQL thermals can be set at any time (that's like a 
lap mode sensor event from thinkpad_acpi).  Even when AMT is turned off, 
you may want the CQL thermal profile set accordingly.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-28 21:01                     ` Limonciello, Mario
@ 2022-07-29 11:03                       ` Hans de Goede
  2022-07-29 15:43                         ` Limonciello, Mario
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-29 11:03 UTC (permalink / raw)
  To: Limonciello, Mario, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/28/22 23:01, Limonciello, Mario wrote:
> On 7/28/2022 13:17, Hans de Goede wrote:
>> Hi,
>>
>> On 7/28/22 20:06, Limonciello, Mario wrote:
>>> On 7/28/2022 12:46, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 7/28/22 16:38, Limonciello, Mario wrote:
>>>>>
>>>>>>>> 1. If I understand things right, then on ThinkPads /sys/firmware/apci/platform_profile
>>>>>>>>        will be registered by thinkpad_acpi. But in version 1 of this patchset nothing is
>>>>>>>>        stopping the amd-pmf code from registering /sys/firmware/apci/platform_profile if
>>>>>>>>        the amd-pmf module gets loaded first. So if the intend is for it to always be owned
>>>>>>>>        by thinkpad_acpi then the amd-pmf code must check for this and not even try to
>>>>>>>>        register its platform_profile support. We cannot rely on module ordering ensuring
>>>>>>>>        that thinkpad_acpi registers first and then amd-pmf will get an -EBUSY error,
>>>>>>>>        since there are no module load ordering guarantees.
>>>>>>>
>>>>>>> This was my thought initially too while this was being developed, but actually there is some nuance here that is non-obvious.  The platform profile registering code in amd-pmf will examine bits set in the BIOS to decide whether or not to export platform profile support.  In Lenovo platforms that support thinkpad_acpi these bits are not set.  So platform profile support ONLY comes from thinkpad-acpi in those platforms.
>>>>>>
>>>>>> Right, Shyam mentioned this in another part of the thread. As I
>>>>>> mentioned there IHMO it would still be good to check this in the driver
>>>>>> though. To catch cases where a BIOS for some reasons advertises an
>>>>>> unexpected combination of features.
>>>>>>
>>>>>>>> 2. So when the thinkpad_acpi platform_profile is set to balanced, then it will
>>>>>>>>        enable AMT and then the periodically run workqueue function from amd-pmf
>>>>>>>>        will do its AMT thing. But what when the thinkpad_acpi platform_profile is
>>>>>>>>        set to low-power or performance. Should the amd-pmf code then apply the static
>>>>>>>>        slider settings for low-power/performance which it has read from the ACPI
>>>>>>>>        tables?  Or will the ACPI/EC code on thinkpads take care of this themselves ?
>>>>>>>>
>>>>>>>
>>>>>>> When thinkpad_acpi changes platform profile then a BIOS event goes through and amd-pmf receives that and will run based on the event.
>>>>>>
>>>>>> Hmm, I don't remember seeing anything for this in the patches. Actually this
>>>>>> reminds me that the code should probably reschedule (using mod_delayed_work)
>>>>>> the work to run immediately after a BIOS event, rather then waiting for
>>>>>> the next normally scheduled run.
>>>>>>
>>>>>> But even then I don't remember seeing any code related to catching
>>>>>> platform-profile changes done outside amd-pmf... ?
>>>>>
>>>>> It's not a platform profile change - it's an ACPI event.
>>>>>
>>>>> When a user changes a platform profile then thinkpad_acpi will see whether it's balanced or not.  When changing to/from balanced thinkpad_acpi sends an AMT event.  amd-pmf reacts to said AMT event.
>>>>>
>>>>> This is the code you're looking for (in this specific patch):
>>>>>
>>>>> +static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
>>>>> +{
>>>>> +    struct amd_pmf_dev *pmf_dev = data;
>>>>> +    struct apmf_if *apmf_if = pmf_dev->apmf_if;
>>>>> +    int ret;
>>>>> +
>>>>> +    if (apmf_if->func.sbios_requests) {
>>>>> +        struct apmf_sbios_req req;
>>>>> +
>>>>> +        ret = apmf_get_sbios_requests(apmf_if, &req);
>>>>> +        if (ret) {
>>>>> +            dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
>>>>> +            return;
>>>>> +        }
>>>>> +        if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
>>>>> +            pr_debug("PMF: AMT is supported and notifications %s\n",
>>>>> +                 req.amt_event ? "Enabled" : "Disabled");
>>>>> +            if (req.amt_event)
>>>>> +                pmf_dev->is_amt_event = true;
>>>>> +            else
>>>>> +                pmf_dev->is_amt_event = !!req.amt_event;
>>>>> +        }
>>>>> +
>>>>> +        if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
>>>>> +            pr_debug("PMF: CQL is supported and notifications %s\n",
>>>>> +                 req.cql_event ? "Enabled" : "Disabled");
>>>>> +            if (req.cql_event)
>>>>> +                pmf_dev->is_cql_event = true;
>>>>> +            else
>>>>> +                pmf_dev->is_cql_event = !!req.cql_event;
>>>>> +
>>>>> +            /* update the target mode information */
>>>>> +            amd_pmf_update_2_cql(pmf_dev);
>>>>> +        }
>>>>> +    }
>>>>> +}
>>>>> +
>>>>
>>>> Right this is the AMT on/off path that bit I understand.
>>>> This happens when switching to / away from balanced mode.
>>>>
>>>> My question is what does the equivalent of these lines:
>>>>
>>>> +        amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
>>>> +                 config_store.prop[src][idx].sppt_apu_only, NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
>>>> +                 config_store.prop[src][idx].stt_min, NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
>>>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
>>>> +        amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
>>>> +                 config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
>>>>
>>>> When the profile is switched (by userspace, or through the hotkeys on
>>>> the laptop) to low-power or to performance mode ?
>>>
>>> Lenovo's firmware will handle the equivalent of changing relevant values for their platform through a BIOS interface in this case when they change ACPI platform profiles.  You will see in their driver something call "PSC" mode, and this is exactly that type of stuff.
>>
>> Ok I see, thank you for clarifying this.
>>
>> So as for the AMT mode, since that is Lenovo only, I guess that means
>> that there is no need to do call amd_pmf_update_slider() when AMT
>> is being disabled since at this point the firmware will have
>> already set the values.
> 
> Yeah, Shyam made this modification for v2 to make sure that code path isn't called unless static slider was set in the BIOS.

But this code path is only hit when AMT / auto mode is available and
when that is true then the static slider should never be set in the BIOS
so the whole amd_pmf_update_slider() call on AMT disable can simply
be dropped AFAICT.

> 
>>
>> Actually this seems to mean that we must ensure that the AMD-PMF
>> code stops touching these settings as soon as the event is received.
>>
>> Which would imply killing the periodic work when an AMT off event
>> is received from within the event handling and then restating it
>> when AMT is on (and making sure the work being queued or not state
>> matches the AMT on/off state at driver probe time) ?
>>
> 
> At first glance this seems plausible, but actually I think it should stay as is because CQL thermals can be set at any time (that's like a lap mode sensor event from thinkpad_acpi).  Even when AMT is turned off, you may want the CQL thermal profile set accordingly.

So the CQL code is to handle lapmode when AMT is active. But I would
expect the firmware to update the power-limits, etc. for lapmode itself
when in performance mode.

The amd_pmf_update_2_cql() function only does things when
config_store.current_mode == AUTO_PERFORMANCE (or AUTO_PERFORMANCE_ON_LAP)

And that reflects the last mode selected by the auto/AMT mode code, not
the mode actual set by thinkpad_acpi so if the last auto selected mode
was balanced and then AMT gets disabled because thinkpad_acpi switches
to performance mode, then on CQL events after the switch amd_pmf_update_2_cql()
will not do anything.

To me it seems that when AMT is off the AMD-PMF code should not touch
the power-limits, etc. at all and thus it should also always ignore
CQL events when AMT is off.

This assumes that the firmware takes care of udating the limits for
on lap / off lap when thinkpad_acpi's profile is set to performance.

If thinkpad_acpi does not do this then the AMD-PMF code should
check what mode has been selected by the thinkpad_acpi code in
amd_pmf_update_2_cql() when AMT is off.

Regards,

Hans




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-29 11:03                       ` Hans de Goede
@ 2022-07-29 15:43                         ` Limonciello, Mario
  2022-07-29 17:40                           ` Shyam Sundar S K
  0 siblings, 1 reply; 56+ messages in thread
From: Limonciello, Mario @ 2022-07-29 15:43 UTC (permalink / raw)
  To: Hans de Goede, Shyam Sundar S K, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

On 7/29/2022 06:03, Hans de Goede wrote:
>>>
>>> So as for the AMT mode, since that is Lenovo only, I guess that means
>>> that there is no need to do call amd_pmf_update_slider() when AMT
>>> is being disabled since at this point the firmware will have
>>> already set the values.
>>
>> Yeah, Shyam made this modification for v2 to make sure that code path isn't called unless static slider was set in the BIOS.
> 
> But this code path is only hit when AMT / auto mode is available and
> when that is true then the static slider should never be set in the BIOS
> so the whole amd_pmf_update_slider() call on AMT disable can simply
> be dropped AFAICT.

The reason to leave it in place but guarded like this is for validation 
of the feature behaves properly from AMD internal systems AMD test BIOS. 
  It can be used to prove out something works properly without needing 
to include extra drivers and software.

> 
>>
>>>
>>> Actually this seems to mean that we must ensure that the AMD-PMF
>>> code stops touching these settings as soon as the event is received.
>>>
>>> Which would imply killing the periodic work when an AMT off event
>>> is received from within the event handling and then restating it
>>> when AMT is on (and making sure the work being queued or not state
>>> matches the AMT on/off state at driver probe time) ?
>>>
>>
>> At first glance this seems plausible, but actually I think it should stay as is because CQL thermals can be set at any time (that's like a lap mode sensor event from thinkpad_acpi).  Even when AMT is turned off, you may want the CQL thermal profile set accordingly.
> 
> So the CQL code is to handle lapmode when AMT is active. But I would
> expect the firmware to update the power-limits, etc. for lapmode itself
> when in performance mode. >
> The amd_pmf_update_2_cql() function only does things when
> config_store.current_mode == AUTO_PERFORMANCE (or AUTO_PERFORMANCE_ON_LAP)
> 
> And that reflects the last mode selected by the auto/AMT mode code, not
> the mode actual set by thinkpad_acpi so if the last auto selected mode
> was balanced and then AMT gets disabled because thinkpad_acpi switches
> to performance mode, then on CQL events after the switch amd_pmf_update_2_cql()
> will not do anything.
> 
> To me it seems that when AMT is off the AMD-PMF code should not touch
> the power-limits, etc. at all and thus it should also always ignore
> CQL events when AMT is off.
> 
> This assumes that the firmware takes care of udating the limits for
> on lap / off lap when thinkpad_acpi's profile is set to performance.

Where does this assumption come from?  I guess that's how it's done on 
Lenovo's Intel systems?

AMT and CQL is a new feature on Lenovo AMD systems, this is the way that 
it's supposed to be done here.

> 
> If thinkpad_acpi does not do this then the AMD-PMF code should
> check what mode has been selected by the thinkpad_acpi code in
> amd_pmf_update_2_cql() when AMT is off.
> 

It is up to the firmware (and thinkpad_acpi) to decide when to send
the CQL events.

Shyam - any comments here?

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-29 15:43                         ` Limonciello, Mario
@ 2022-07-29 17:40                           ` Shyam Sundar S K
  2022-07-29 17:59                             ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-07-29 17:40 UTC (permalink / raw)
  To: Limonciello, Mario, Hans de Goede, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi Mario,

On 7/29/2022 9:13 PM, Limonciello, Mario wrote:
> On 7/29/2022 06:03, Hans de Goede wrote:
>>>>
>>>> So as for the AMT mode, since that is Lenovo only, I guess that means
>>>> that there is no need to do call amd_pmf_update_slider() when AMT
>>>> is being disabled since at this point the firmware will have
>>>> already set the values.
>>>
>>> Yeah, Shyam made this modification for v2 to make sure that code path
>>> isn't called unless static slider was set in the BIOS.
>>
>> But this code path is only hit when AMT / auto mode is available and
>> when that is true then the static slider should never be set in the BIOS
>> so the whole amd_pmf_update_slider() call on AMT disable can simply
>> be dropped AFAICT.
> 
> The reason to leave it in place but guarded like this is for validation
> of the feature behaves properly from AMD internal systems AMD test BIOS.
>  It can be used to prove out something works properly without needing to
> include extra drivers and software.

Yes. We will need this path to check on the internal CRB system to
validate the 'auto mode'. Whenever the amd-pmf driver gets the AMT
disable event we shall disable the power-settings w.r.t to 'auto mode'.

I moved the handling to amd_pmf_reset_amt() based on Hans review
remarks, and its guarded with a if() check, so that we accidentally
don't land up in updating the static slider.

Also left a note on the same function, so that it provides some
information on why the logic is being done in that way.

> 
>>
>>>
>>>>
>>>> Actually this seems to mean that we must ensure that the AMD-PMF
>>>> code stops touching these settings as soon as the event is received.
>>>>
>>>> Which would imply killing the periodic work when an AMT off event
>>>> is received from within the event handling and then restating it
>>>> when AMT is on (and making sure the work being queued or not state
>>>> matches the AMT on/off state at driver probe time) ?
>>>>
>>>
>>> At first glance this seems plausible, but actually I think it should
>>> stay as is because CQL thermals can be set at any time (that's like a
>>> lap mode sensor event from thinkpad_acpi).  Even when AMT is turned
>>> off, you may want the CQL thermal profile set accordingly.
>>
>> So the CQL code is to handle lapmode when AMT is active. But I would
>> expect the firmware to update the power-limits, etc. for lapmode itself
>> when in performance mode. >
>> The amd_pmf_update_2_cql() function only does things when
>> config_store.current_mode == AUTO_PERFORMANCE (or
>> AUTO_PERFORMANCE_ON_LAP)
>>
>> And that reflects the last mode selected by the auto/AMT mode code, not
>> the mode actual set by thinkpad_acpi so if the last auto selected mode
>> was balanced and then AMT gets disabled because thinkpad_acpi switches
>> to performance mode, then on CQL events after the switch
>> amd_pmf_update_2_cql()
>> will not do anything.
>>
>> To me it seems that when AMT is off the AMD-PMF code should not touch
>> the power-limits, etc. at all and thus it should also always ignore
>> CQL events when AMT is off.
>>
>> This assumes that the firmware takes care of udating the limits for
>> on lap / off lap when thinkpad_acpi's profile is set to performance.
> 
> Where does this assumption come from?  I guess that's how it's done on
> Lenovo's Intel systems?
> 
> AMT and CQL is a new feature on Lenovo AMD systems, this is the way that
> it's supposed to be done here.

Yes, this was newly designed for Lenovo AMD systems. The behavior is
same on windows too (atleast on the RMB laptops today) .

When the system is running in 'auto-mode performance' and the user keeps
the system on his lap, amd-pmf driver receives a 'CQL' event from Lenovo
BIOS. In this case, the amd-pmf driver shall apply thermal limits w.r.t
to 'auto-mode performance-on-lap' and not 'auto-mode performance'.


> 
>>
>> If thinkpad_acpi does not do this then the AMD-PMF code should
>> check what mode has been selected by the thinkpad_acpi code in
>> amd_pmf_update_2_cql() when AMT is off.
>>
> 
> It is up to the firmware (and thinkpad_acpi) to decide when to send
> the CQL events.
> 
> Shyam - any comments here?

Yes, I agree with Mario here.

Thanks,
Shyam

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-29 17:40                           ` Shyam Sundar S K
@ 2022-07-29 17:59                             ` Hans de Goede
  2022-08-01 10:29                               ` Shyam Sundar S K
  0 siblings, 1 reply; 56+ messages in thread
From: Hans de Goede @ 2022-07-29 17:59 UTC (permalink / raw)
  To: Shyam Sundar S K, Limonciello, Mario, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 7/29/22 19:40, Shyam Sundar S K wrote:
> Hi Mario,
> 
> On 7/29/2022 9:13 PM, Limonciello, Mario wrote:
>> On 7/29/2022 06:03, Hans de Goede wrote:
>>>>>
>>>>> So as for the AMT mode, since that is Lenovo only, I guess that means
>>>>> that there is no need to do call amd_pmf_update_slider() when AMT
>>>>> is being disabled since at this point the firmware will have
>>>>> already set the values.
>>>>
>>>> Yeah, Shyam made this modification for v2 to make sure that code path
>>>> isn't called unless static slider was set in the BIOS.
>>>
>>> But this code path is only hit when AMT / auto mode is available and
>>> when that is true then the static slider should never be set in the BIOS
>>> so the whole amd_pmf_update_slider() call on AMT disable can simply
>>> be dropped AFAICT.
>>
>> The reason to leave it in place but guarded like this is for validation
>> of the feature behaves properly from AMD internal systems AMD test BIOS.
>>  It can be used to prove out something works properly without needing to
>> include extra drivers and software.
> 
> Yes. We will need this path to check on the internal CRB system to
> validate the 'auto mode'. Whenever the amd-pmf driver gets the AMT
> disable event we shall disable the power-settings w.r.t to 'auto mode'.
> 
> I moved the handling to amd_pmf_reset_amt() based on Hans review
> remarks, and its guarded with a if() check, so that we accidentally
> don't land up in updating the static slider.
> 
> Also left a note on the same function, so that it provides some
> information on why the logic is being done in that way.
> 
>>
>>>
>>>>
>>>>>
>>>>> Actually this seems to mean that we must ensure that the AMD-PMF
>>>>> code stops touching these settings as soon as the event is received.
>>>>>
>>>>> Which would imply killing the periodic work when an AMT off event
>>>>> is received from within the event handling and then restating it
>>>>> when AMT is on (and making sure the work being queued or not state
>>>>> matches the AMT on/off state at driver probe time) ?
>>>>>
>>>>
>>>> At first glance this seems plausible, but actually I think it should
>>>> stay as is because CQL thermals can be set at any time (that's like a
>>>> lap mode sensor event from thinkpad_acpi).  Even when AMT is turned
>>>> off, you may want the CQL thermal profile set accordingly.
>>>
>>> So the CQL code is to handle lapmode when AMT is active. But I would
>>> expect the firmware to update the power-limits, etc. for lapmode itself
>>> when in performance mode. >
>>> The amd_pmf_update_2_cql() function only does things when
>>> config_store.current_mode == AUTO_PERFORMANCE (or
>>> AUTO_PERFORMANCE_ON_LAP)
>>>
>>> And that reflects the last mode selected by the auto/AMT mode code, not
>>> the mode actual set by thinkpad_acpi so if the last auto selected mode
>>> was balanced and then AMT gets disabled because thinkpad_acpi switches
>>> to performance mode, then on CQL events after the switch
>>> amd_pmf_update_2_cql()
>>> will not do anything.
>>>
>>> To me it seems that when AMT is off the AMD-PMF code should not touch
>>> the power-limits, etc. at all and thus it should also always ignore
>>> CQL events when AMT is off.
>>>
>>> This assumes that the firmware takes care of udating the limits for
>>> on lap / off lap when thinkpad_acpi's profile is set to performance.
>>
>> Where does this assumption come from?  I guess that's how it's done on
>> Lenovo's Intel systems?
>>
>> AMT and CQL is a new feature on Lenovo AMD systems, this is the way that
>> it's supposed to be done here.
> 
> Yes, this was newly designed for Lenovo AMD systems. The behavior is
> same on windows too (atleast on the RMB laptops today) .
> 
> When the system is running in 'auto-mode performance' and the user keeps
> the system on his lap, amd-pmf driver receives a 'CQL' event from Lenovo
> BIOS. In this case, the amd-pmf driver shall apply thermal limits w.r.t
> to 'auto-mode performance-on-lap' and not 'auto-mode performance'.

The question here is not about the 'auto-mode performance' mode
but what to do when AMT / 'auto-mode performance' is disabled.

What should the behavior of the AMD-PMf code be when it receives
a CQL event when AMT is disabled ?

>>> If thinkpad_acpi does not do this then the AMD-PMF code should
>>> check what mode has been selected by the thinkpad_acpi code in
>>> amd_pmf_update_2_cql() when AMT is off.

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-07-29 17:59                             ` Hans de Goede
@ 2022-08-01 10:29                               ` Shyam Sundar S K
  2022-08-01 11:08                                 ` Hans de Goede
  0 siblings, 1 reply; 56+ messages in thread
From: Shyam Sundar S K @ 2022-08-01 10:29 UTC (permalink / raw)
  To: Hans de Goede, Limonciello, Mario, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi Hans,

On 7/29/2022 11:29 PM, Hans de Goede wrote:
> Hi,
> 
> On 7/29/22 19:40, Shyam Sundar S K wrote:
>> Hi Mario,
>>
>> On 7/29/2022 9:13 PM, Limonciello, Mario wrote:
>>> On 7/29/2022 06:03, Hans de Goede wrote:
>>>>>>
>>>>>> So as for the AMT mode, since that is Lenovo only, I guess that means
>>>>>> that there is no need to do call amd_pmf_update_slider() when AMT
>>>>>> is being disabled since at this point the firmware will have
>>>>>> already set the values.
>>>>>
>>>>> Yeah, Shyam made this modification for v2 to make sure that code path
>>>>> isn't called unless static slider was set in the BIOS.
>>>>
>>>> But this code path is only hit when AMT / auto mode is available and
>>>> when that is true then the static slider should never be set in the BIOS
>>>> so the whole amd_pmf_update_slider() call on AMT disable can simply
>>>> be dropped AFAICT.
>>>
>>> The reason to leave it in place but guarded like this is for validation
>>> of the feature behaves properly from AMD internal systems AMD test BIOS.
>>>  It can be used to prove out something works properly without needing to
>>> include extra drivers and software.
>>
>> Yes. We will need this path to check on the internal CRB system to
>> validate the 'auto mode'. Whenever the amd-pmf driver gets the AMT
>> disable event we shall disable the power-settings w.r.t to 'auto mode'.
>>
>> I moved the handling to amd_pmf_reset_amt() based on Hans review
>> remarks, and its guarded with a if() check, so that we accidentally
>> don't land up in updating the static slider.
>>
>> Also left a note on the same function, so that it provides some
>> information on why the logic is being done in that way.
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>> Actually this seems to mean that we must ensure that the AMD-PMF
>>>>>> code stops touching these settings as soon as the event is received.
>>>>>>
>>>>>> Which would imply killing the periodic work when an AMT off event
>>>>>> is received from within the event handling and then restating it
>>>>>> when AMT is on (and making sure the work being queued or not state
>>>>>> matches the AMT on/off state at driver probe time) ?
>>>>>>
>>>>>
>>>>> At first glance this seems plausible, but actually I think it should
>>>>> stay as is because CQL thermals can be set at any time (that's like a
>>>>> lap mode sensor event from thinkpad_acpi).  Even when AMT is turned
>>>>> off, you may want the CQL thermal profile set accordingly.
>>>>
>>>> So the CQL code is to handle lapmode when AMT is active. But I would
>>>> expect the firmware to update the power-limits, etc. for lapmode itself
>>>> when in performance mode. >
>>>> The amd_pmf_update_2_cql() function only does things when
>>>> config_store.current_mode == AUTO_PERFORMANCE (or
>>>> AUTO_PERFORMANCE_ON_LAP)
>>>>
>>>> And that reflects the last mode selected by the auto/AMT mode code, not
>>>> the mode actual set by thinkpad_acpi so if the last auto selected mode
>>>> was balanced and then AMT gets disabled because thinkpad_acpi switches
>>>> to performance mode, then on CQL events after the switch
>>>> amd_pmf_update_2_cql()
>>>> will not do anything.
>>>>
>>>> To me it seems that when AMT is off the AMD-PMF code should not touch
>>>> the power-limits, etc. at all and thus it should also always ignore
>>>> CQL events when AMT is off.
>>>>
>>>> This assumes that the firmware takes care of udating the limits for
>>>> on lap / off lap when thinkpad_acpi's profile is set to performance.
>>>
>>> Where does this assumption come from?  I guess that's how it's done on
>>> Lenovo's Intel systems?
>>>
>>> AMT and CQL is a new feature on Lenovo AMD systems, this is the way that
>>> it's supposed to be done here.
>>
>> Yes, this was newly designed for Lenovo AMD systems. The behavior is
>> same on windows too (atleast on the RMB laptops today) .
>>
>> When the system is running in 'auto-mode performance' and the user keeps
>> the system on his lap, amd-pmf driver receives a 'CQL' event from Lenovo
>> BIOS. In this case, the amd-pmf driver shall apply thermal limits w.r.t
>> to 'auto-mode performance-on-lap' and not 'auto-mode performance'.
> 
> The question here is not about the 'auto-mode performance' mode
> but what to do when AMT / 'auto-mode performance' is disabled.
> 
> What should the behavior of the AMD-PMf code be when it receives
> a CQL event when AMT is disabled ?

When:
1. AMT is disabled and we get a CQL event, it becomes a no-op to the
amd-pmf driver.
2. AMT is enabled:
  - Avg. SoC power is higher than a selected measure, the amd-pmf driver
tries to move to 'auto-mode performance' and apply the thermals set in
the BIOS for 'auto-mode peformance' but in this scenario, when we are in
'auto-mode performance' and user moves the laptop from desk to lap, we
receive a 'on-lap' event. In this case we apply thermals w.r.t to
'auto-mode performance-on-lap' and not 'auto-mode performance'.

That is what is being done in amd_pmf_update_2_cql() with a check:
	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;

Update of CQL happens only when AMT is active.

Thanks,
Shyam

> 
>>>> If thinkpad_acpi does not do this then the AMD-PMF code should
>>>> check what mode has been selected by the thinkpad_acpi code in
>>>> amd_pmf_update_2_cql() when AMT is off.
> 
> Regards,
> 
> Hans
> 

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode
  2022-08-01 10:29                               ` Shyam Sundar S K
@ 2022-08-01 11:08                                 ` Hans de Goede
  0 siblings, 0 replies; 56+ messages in thread
From: Hans de Goede @ 2022-08-01 11:08 UTC (permalink / raw)
  To: Shyam Sundar S K, Limonciello, Mario, markgross
  Cc: platform-driver-x86, Patil.Reddy, Mark Pearson

Hi,

On 8/1/22 12:29, Shyam Sundar S K wrote:
> Hi Hans,
> 
> On 7/29/2022 11:29 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 7/29/22 19:40, Shyam Sundar S K wrote:
>>> Hi Mario,
>>>
>>> On 7/29/2022 9:13 PM, Limonciello, Mario wrote:
>>>> On 7/29/2022 06:03, Hans de Goede wrote:
>>>>>>>
>>>>>>> So as for the AMT mode, since that is Lenovo only, I guess that means
>>>>>>> that there is no need to do call amd_pmf_update_slider() when AMT
>>>>>>> is being disabled since at this point the firmware will have
>>>>>>> already set the values.
>>>>>>
>>>>>> Yeah, Shyam made this modification for v2 to make sure that code path
>>>>>> isn't called unless static slider was set in the BIOS.
>>>>>
>>>>> But this code path is only hit when AMT / auto mode is available and
>>>>> when that is true then the static slider should never be set in the BIOS
>>>>> so the whole amd_pmf_update_slider() call on AMT disable can simply
>>>>> be dropped AFAICT.
>>>>
>>>> The reason to leave it in place but guarded like this is for validation
>>>> of the feature behaves properly from AMD internal systems AMD test BIOS.
>>>>  It can be used to prove out something works properly without needing to
>>>> include extra drivers and software.
>>>
>>> Yes. We will need this path to check on the internal CRB system to
>>> validate the 'auto mode'. Whenever the amd-pmf driver gets the AMT
>>> disable event we shall disable the power-settings w.r.t to 'auto mode'.
>>>
>>> I moved the handling to amd_pmf_reset_amt() based on Hans review
>>> remarks, and its guarded with a if() check, so that we accidentally
>>> don't land up in updating the static slider.
>>>
>>> Also left a note on the same function, so that it provides some
>>> information on why the logic is being done in that way.
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>> Actually this seems to mean that we must ensure that the AMD-PMF
>>>>>>> code stops touching these settings as soon as the event is received.
>>>>>>>
>>>>>>> Which would imply killing the periodic work when an AMT off event
>>>>>>> is received from within the event handling and then restating it
>>>>>>> when AMT is on (and making sure the work being queued or not state
>>>>>>> matches the AMT on/off state at driver probe time) ?
>>>>>>>
>>>>>>
>>>>>> At first glance this seems plausible, but actually I think it should
>>>>>> stay as is because CQL thermals can be set at any time (that's like a
>>>>>> lap mode sensor event from thinkpad_acpi).  Even when AMT is turned
>>>>>> off, you may want the CQL thermal profile set accordingly.
>>>>>
>>>>> So the CQL code is to handle lapmode when AMT is active. But I would
>>>>> expect the firmware to update the power-limits, etc. for lapmode itself
>>>>> when in performance mode. >
>>>>> The amd_pmf_update_2_cql() function only does things when
>>>>> config_store.current_mode == AUTO_PERFORMANCE (or
>>>>> AUTO_PERFORMANCE_ON_LAP)
>>>>>
>>>>> And that reflects the last mode selected by the auto/AMT mode code, not
>>>>> the mode actual set by thinkpad_acpi so if the last auto selected mode
>>>>> was balanced and then AMT gets disabled because thinkpad_acpi switches
>>>>> to performance mode, then on CQL events after the switch
>>>>> amd_pmf_update_2_cql()
>>>>> will not do anything.
>>>>>
>>>>> To me it seems that when AMT is off the AMD-PMF code should not touch
>>>>> the power-limits, etc. at all and thus it should also always ignore
>>>>> CQL events when AMT is off.
>>>>>
>>>>> This assumes that the firmware takes care of udating the limits for
>>>>> on lap / off lap when thinkpad_acpi's profile is set to performance.
>>>>
>>>> Where does this assumption come from?  I guess that's how it's done on
>>>> Lenovo's Intel systems?
>>>>
>>>> AMT and CQL is a new feature on Lenovo AMD systems, this is the way that
>>>> it's supposed to be done here.
>>>
>>> Yes, this was newly designed for Lenovo AMD systems. The behavior is
>>> same on windows too (atleast on the RMB laptops today) .
>>>
>>> When the system is running in 'auto-mode performance' and the user keeps
>>> the system on his lap, amd-pmf driver receives a 'CQL' event from Lenovo
>>> BIOS. In this case, the amd-pmf driver shall apply thermal limits w.r.t
>>> to 'auto-mode performance-on-lap' and not 'auto-mode performance'.
>>
>> The question here is not about the 'auto-mode performance' mode
>> but what to do when AMT / 'auto-mode performance' is disabled.
>>
>> What should the behavior of the AMD-PMf code be when it receives
>> a CQL event when AMT is disabled ?
> 
> When:
> 1. AMT is disabled and we get a CQL event, it becomes a no-op to the
> amd-pmf driver.

But that is not what happens in the current (v2) code:

1. The apmf_event_handler() is always registered as long as the driver is bound
   (which it must be to catch AMT being re-enabled)

2. apmf_event_handler() does:

+		if (req.pending_req & BIT(APMF_CQL_NOTIFICATION)) {
+			pr_debug("PMF: CQL is supported and notifications %s\n",
+				 req.cql_event ? "Enabled" : "Disabled");
+			pmf_dev->is_cql_event = !!req.cql_event;
+
+			/* update the target mode information */
+			amd_pmf_update_2_cql(pmf_dev);
+		}

3. amd_pmf_update_2_cql() does:

+void amd_pmf_update_2_cql(struct amd_pmf_dev *dev)
+{
+	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
+			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;
+	if ((config_store.current_mode == AUTO_PERFORMANCE ||
+	     config_store.current_mode == AUTO_PERFORMANCE_ON_LAP) &&
+	    config_store.current_mode !=
+	    config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode) {
+		config_store.current_mode =
+				config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode;
+		amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
+	}
+	dev_dbg(dev->dev, "updated CQL thermals\n");
+}

Note this does not check dev->is_amt_event at all so CQL events
may lead to amd_pmf_handle_automode() getting called, which sets
the limits even when AMT is disabled.

 
> 2. AMT is enabled:
>   - Avg. SoC power is higher than a selected measure, the amd-pmf driver
> tries to move to 'auto-mode performance' and apply the thermals set in
> the BIOS for 'auto-mode peformance' but in this scenario, when we are in
> 'auto-mode performance' and user moves the laptop from desk to lap, we
> receive a 'on-lap' event. In this case we apply thermals w.r.t to
> 'auto-mode performance-on-lap' and not 'auto-mode performance'.
> 
> That is what is being done in amd_pmf_update_2_cql() with a check:
> 	config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode =
> 			dev->is_cql_event ? AUTO_PERFORMANCE_ON_LAP : AUTO_PERFORMANCE;

Right this much I understand.

> Update of CQL happens only when AMT is active.

So you mean the SBIOS is expected to only send CQL events when it has
set AMT to be enabled ?  AFAIK the CQL events are basically on-lap /
not-on-lap events, these can easily also happen when not in AMT mode,
so the SBIOS would need to explicitly not send these events when
lap-mode changes when AMT is disabled.

Since firmware bugs do happen and can sometimes be hard to fix /
have long times to get published IMHO it would be best if 
amd_pmf_update_2_cql() would check dev->is_amt_event and return
early from the function if that is not set.

Regards,

Hans


^ permalink raw reply	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2022-08-01 11:09 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-12 14:58 [PATCH v1 00/15] platform/x86/amd/pmf: Introduce AMD PMF Driver Shyam Sundar S K
2022-07-12 14:58 ` [PATCH v1 01/15] ACPI: platform_profile: Add support for notification chains Shyam Sundar S K
2022-07-12 15:03   ` Limonciello, Mario
2022-07-27 13:24     ` Hans de Goede
2022-07-27 20:38   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 02/15] platform/x86/amd/pmf: Add support for PMF core layer Shyam Sundar S K
2022-07-27 13:57   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 03/15] platform/x86/amd/pmf: Add support for PMF APCI layer Shyam Sundar S K
2022-07-27 13:57   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 04/15] platform/x86/amd/pmf: Add support SPS PMF feature Shyam Sundar S K
2022-07-27 19:29   ` Hans de Goede
2022-07-27 20:26     ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 05/15] platform/x86/amd/pmf: Add debugfs information Shyam Sundar S K
2022-07-27 19:50   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 06/15] platform/x86/amd/pmf: Add heartbeat signal support Shyam Sundar S K
2022-07-27 19:53   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 07/15] platform/x86/amd/pmf: Add fan control support Shyam Sundar S K
2022-07-27 20:11   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 08/15] platform/x86/amd/pmf: Get performance metrics from PMFW Shyam Sundar S K
2022-07-27 20:36   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 09/15] platform/x86/amd/pmf: Add support for CnQF Shyam Sundar S K
2022-07-27 20:51   ` Hans de Goede
2022-07-27 21:00   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 10/15] platform/x86/amd/pmf: Add sysfs to toggle CnQF Shyam Sundar S K
2022-07-27 20:52   ` Hans de Goede
2022-07-27 21:12     ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 11/15] Documentation/ABI/testing/sysfs-amd-pmf: Add ABI doc for AMD PMF Shyam Sundar S K
2022-07-27 20:52   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature Shyam Sundar S K
2022-07-27 21:22   ` Hans de Goede
2022-07-28 12:57     ` Shyam Sundar S K
2022-07-28 13:15       ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 13/15] platform/x86/amd/pmf: Handle AMT and CQL events for Auto mode Shyam Sundar S K
2022-07-27 21:33   ` Hans de Goede
2022-07-27 21:44   ` Hans de Goede
2022-07-27 21:46   ` Hans de Goede
2022-07-27 23:52     ` Limonciello, Mario
2022-07-28 13:03       ` Hans de Goede
2022-07-28 13:43         ` Limonciello, Mario
2022-07-28 14:09           ` Hans de Goede
2022-07-28 14:38             ` Limonciello, Mario
2022-07-28 17:46               ` Hans de Goede
2022-07-28 18:06                 ` Limonciello, Mario
2022-07-28 18:17                   ` Hans de Goede
2022-07-28 21:01                     ` Limonciello, Mario
2022-07-29 11:03                       ` Hans de Goede
2022-07-29 15:43                         ` Limonciello, Mario
2022-07-29 17:40                           ` Shyam Sundar S K
2022-07-29 17:59                             ` Hans de Goede
2022-08-01 10:29                               ` Shyam Sundar S K
2022-08-01 11:08                                 ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 14/15] platform/x86/amd/pmf: Force load driver on older supported platforms Shyam Sundar S K
2022-07-27 21:40   ` Hans de Goede
2022-07-12 14:58 ` [PATCH v1 15/15] MAINTAINERS: Add AMD PMF driver entry Shyam Sundar S K
2022-07-27 21:41   ` Hans de Goede
2022-07-28 17:44     ` Shyam Sundar S K

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).