// 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 * Sanket Goswami */ #include #include #include #include #include #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; }