1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD Platform Management Framework Util Layer 4 * 5 * Copyright (c) 2026, Advanced Micro Devices, Inc. 6 * All Rights Reserved. 7 * 8 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 * Sanket Goswami <Sanket.Goswami@amd.com> 10 */ 11 12 #include <linux/amd-pmf.h> 13 #include <linux/minmax.h> 14 #include <linux/miscdevice.h> 15 #include <linux/mutex.h> 16 #include <linux/uaccess.h> 17 18 #include "pmf.h" 19 20 static struct amd_pmf_dev *pmf_dev_handle; 21 static DEFINE_MUTEX(pmf_util_lock); 22 23 static int amd_pmf_populate_data(struct amd_pmf_dev *pdev, struct amd_pmf_info *info) 24 { 25 struct ta_pmf_shared_memory *ta_sm = NULL; 26 struct ta_pmf_enact_table *in = NULL; 27 int idx; 28 29 if (!pdev || !info) 30 return -EINVAL; 31 32 if (!pdev->shbuf) 33 return -EINVAL; 34 35 ta_sm = pdev->shbuf; 36 in = &ta_sm->pmf_input.enact_table; 37 38 /* Set size */ 39 info->size = sizeof(*info); 40 41 /* PMF Feature support flags */ 42 if (is_apmf_func_supported(pdev, APMF_FUNC_AUTO_MODE)) 43 info->features_supported |= AMD_PMF_FEAT_AUTO_MODE; 44 if (is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) 45 info->features_supported |= AMD_PMF_FEAT_STATIC_POWER_SLIDER; 46 if (pdev->smart_pc_enabled) 47 info->features_supported |= AMD_PMF_FEAT_POLICY_BUILDER; 48 if (is_apmf_func_supported(pdev, APMF_FUNC_DYN_SLIDER_AC)) 49 info->features_supported |= AMD_PMF_FEAT_DYNAMIC_POWER_SLIDER_AC; 50 if (is_apmf_func_supported(pdev, APMF_FUNC_DYN_SLIDER_DC)) 51 info->features_supported |= AMD_PMF_FEAT_DYNAMIC_POWER_SLIDER_DC; 52 53 /* Device States */ 54 info->platform_type = in->ev_info.platform_type; 55 info->laptop_placement = in->ev_info.device_state; 56 info->lid_state = in->ev_info.lid_state; 57 info->user_presence = in->ev_info.user_present; 58 info->slider_position = in->ev_info.power_slider; 59 60 /* Thermal and Power Metrics */ 61 info->power_source = in->ev_info.power_source; 62 info->skin_temp = in->ev_info.skin_temperature; 63 info->gfx_busy = in->ev_info.gfx_busy; 64 info->ambient_light = in->ev_info.ambient_light; 65 info->avg_c0_residency = in->ev_info.avg_c0residency; 66 info->max_c0_residency = in->ev_info.max_c0residency; 67 info->socket_power = in->ev_info.socket_power; 68 69 /* Custom BIOS input parameters */ 70 for (idx = 0; idx < AMD_PMF_BIOS_PARAMS_MAX; idx++) 71 info->bios_input[idx] = amd_pmf_get_ta_custom_bios_inputs(in, idx); 72 73 /* BIOS output parameters */ 74 for (idx = 0; idx < AMD_PMF_BIOS_PARAMS_MAX; idx++) 75 info->bios_output[idx] = pdev->bios_output[idx]; 76 77 return 0; 78 } 79 80 static long amd_pmf_set_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 81 { 82 struct amd_pmf_dev *pdev = filp->private_data; 83 void __user *argp = (void __user *)arg; 84 struct amd_pmf_info info = {}; 85 size_t copy_size; 86 __u64 user_size; 87 int ret; 88 89 if (cmd != IOCTL_AMD_PMF_POPULATE_DATA) 90 return -ENOTTY; 91 92 /* First read just the size field from userspace */ 93 if (copy_from_user(&user_size, argp, sizeof(user_size))) 94 return -EFAULT; 95 96 guard(mutex)(&pmf_util_lock); 97 ret = amd_pmf_populate_data(pdev, &info); 98 if (ret) 99 return ret; 100 101 copy_size = min_t(size_t, user_size, sizeof(info)); 102 103 /* Set actual size being copied */ 104 info.size = copy_size; 105 106 if (copy_to_user(argp, &info, copy_size)) 107 return -EFAULT; 108 109 return 0; 110 } 111 112 static int amd_pmf_open(struct inode *inode, struct file *filp) 113 { 114 guard(mutex)(&pmf_util_lock); 115 if (!pmf_dev_handle) 116 return -ENODEV; 117 118 filp->private_data = pmf_dev_handle; 119 return 0; 120 } 121 122 static const struct file_operations pmf_if_ops = { 123 .owner = THIS_MODULE, 124 .open = amd_pmf_open, 125 .unlocked_ioctl = amd_pmf_set_ioctl, 126 }; 127 128 static struct miscdevice amd_pmf_util_if = { 129 .minor = MISC_DYNAMIC_MINOR, 130 .name = "amdpmf_interface", 131 .fops = &pmf_if_ops, 132 }; 133 134 int amd_pmf_cdev_register(struct amd_pmf_dev *dev) 135 { 136 int ret; 137 138 guard(mutex)(&pmf_util_lock); 139 pmf_dev_handle = dev; 140 ret = misc_register(&amd_pmf_util_if); 141 if (ret) 142 pmf_dev_handle = NULL; 143 144 return ret; 145 } 146 147 void amd_pmf_cdev_unregister(void) 148 { 149 guard(mutex)(&pmf_util_lock); 150 if (pmf_dev_handle) 151 misc_deregister(&amd_pmf_util_if); 152 pmf_dev_handle = NULL; 153 } 154