summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/amd/pmf/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/amd/pmf/util.c')
-rw-r--r--drivers/platform/x86/amd/pmf/util.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/drivers/platform/x86/amd/pmf/util.c b/drivers/platform/x86/amd/pmf/util.c
new file mode 100644
index 000000000000..4d1a61e4f1b9
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/util.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Platform Management Framework Util Layer
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Sanket Goswami <Sanket.Goswami@amd.com>
+ */
+
+#include <linux/amd-pmf.h>
+#include <linux/minmax.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+#include "pmf.h"
+
+static struct amd_pmf_dev *pmf_dev_handle;
+static DEFINE_MUTEX(pmf_util_lock);
+
+static int amd_pmf_populate_data(struct amd_pmf_dev *pdev, struct amd_pmf_info *info)
+{
+ struct ta_pmf_shared_memory *ta_sm = NULL;
+ struct ta_pmf_enact_table *in = NULL;
+ int idx;
+
+ if (!pdev || !info)
+ return -EINVAL;
+
+ if (!pdev->shbuf)
+ return -EINVAL;
+
+ ta_sm = pdev->shbuf;
+ in = &ta_sm->pmf_input.enact_table;
+
+ /* Set size */
+ info->size = sizeof(*info);
+
+ /* PMF Feature support flags */
+ if (is_apmf_func_supported(pdev, APMF_FUNC_AUTO_MODE))
+ info->features_supported |= AMD_PMF_FEAT_AUTO_MODE;
+ if (is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
+ info->features_supported |= AMD_PMF_FEAT_STATIC_POWER_SLIDER;
+ if (pdev->smart_pc_enabled)
+ info->features_supported |= AMD_PMF_FEAT_POLICY_BUILDER;
+ if (is_apmf_func_supported(pdev, APMF_FUNC_DYN_SLIDER_AC))
+ info->features_supported |= AMD_PMF_FEAT_DYNAMIC_POWER_SLIDER_AC;
+ if (is_apmf_func_supported(pdev, APMF_FUNC_DYN_SLIDER_DC))
+ info->features_supported |= AMD_PMF_FEAT_DYNAMIC_POWER_SLIDER_DC;
+
+ /* Device States */
+ info->platform_type = in->ev_info.platform_type;
+ info->laptop_placement = in->ev_info.device_state;
+ info->lid_state = in->ev_info.lid_state;
+ info->user_presence = in->ev_info.user_present;
+ info->slider_position = in->ev_info.power_slider;
+
+ /* Thermal and Power Metrics */
+ info->power_source = in->ev_info.power_source;
+ info->skin_temp = in->ev_info.skin_temperature;
+ info->gfx_busy = in->ev_info.gfx_busy;
+ info->ambient_light = in->ev_info.ambient_light;
+ info->avg_c0_residency = in->ev_info.avg_c0residency;
+ info->max_c0_residency = in->ev_info.max_c0residency;
+ info->socket_power = in->ev_info.socket_power;
+
+ /* Custom BIOS input parameters */
+ for (idx = 0; idx < AMD_PMF_BIOS_PARAMS_MAX; idx++)
+ info->bios_input[idx] = amd_pmf_get_ta_custom_bios_inputs(in, idx);
+
+ /* BIOS output parameters */
+ for (idx = 0; idx < AMD_PMF_BIOS_PARAMS_MAX; idx++)
+ info->bios_output[idx] = pdev->bios_output[idx];
+
+ return 0;
+}
+
+static long amd_pmf_set_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct amd_pmf_dev *pdev = filp->private_data;
+ void __user *argp = (void __user *)arg;
+ struct amd_pmf_info info = {};
+ size_t copy_size;
+ __u64 user_size;
+ int ret;
+
+ if (cmd != IOCTL_AMD_PMF_POPULATE_DATA)
+ return -ENOTTY;
+
+ /* First read just the size field from userspace */
+ if (copy_from_user(&user_size, argp, sizeof(user_size)))
+ return -EFAULT;
+
+ guard(mutex)(&pmf_util_lock);
+ ret = amd_pmf_populate_data(pdev, &info);
+ if (ret)
+ return ret;
+
+ copy_size = min_t(size_t, user_size, sizeof(info));
+
+ /* Set actual size being copied */
+ info.size = copy_size;
+
+ if (copy_to_user(argp, &info, copy_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int amd_pmf_open(struct inode *inode, struct file *filp)
+{
+ guard(mutex)(&pmf_util_lock);
+ if (!pmf_dev_handle)
+ return -ENODEV;
+
+ filp->private_data = pmf_dev_handle;
+ return 0;
+}
+
+static const struct file_operations pmf_if_ops = {
+ .owner = THIS_MODULE,
+ .open = amd_pmf_open,
+ .unlocked_ioctl = amd_pmf_set_ioctl,
+};
+
+static struct miscdevice amd_pmf_util_if = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "amdpmf_interface",
+ .fops = &pmf_if_ops,
+};
+
+int amd_pmf_cdev_register(struct amd_pmf_dev *dev)
+{
+ int ret;
+
+ guard(mutex)(&pmf_util_lock);
+ pmf_dev_handle = dev;
+ ret = misc_register(&amd_pmf_util_if);
+ if (ret)
+ pmf_dev_handle = NULL;
+
+ return ret;
+}
+
+void amd_pmf_cdev_unregister(void)
+{
+ guard(mutex)(&pmf_util_lock);
+ if (pmf_dev_handle)
+ misc_deregister(&amd_pmf_util_if);
+ pmf_dev_handle = NULL;
+}