xref: /linux/drivers/platform/x86/amd/pmf/util.c (revision 5b05bb3f6c5716fab6911e12d60dd1f43ad9806a)
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 
amd_pmf_populate_data(struct amd_pmf_dev * pdev,struct amd_pmf_info * info)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 
amd_pmf_set_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)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 
amd_pmf_open(struct inode * inode,struct file * filp)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 
amd_pmf_cdev_register(struct amd_pmf_dev * dev)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 
amd_pmf_cdev_unregister(void)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