xref: /linux/drivers/platform/x86/amd/pmf/spc.c (revision 5b05bb3f6c5716fab6911e12d60dd1f43ad9806a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * AMD Platform Management Framework Driver - Smart PC Capabilities
4  *
5  * Copyright (c) 2023, Advanced Micro Devices, Inc.
6  * All Rights Reserved.
7  *
8  * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9  *          Patil Rajesh Reddy <Patil.Reddy@amd.com>
10  */
11 
12 #include <acpi/button.h>
13 #include <linux/amd-pmf.h>
14 #include <linux/amd-pmf-io.h>
15 #include <linux/cleanup.h>
16 #include <linux/power_supply.h>
17 #include <linux/units.h>
18 #include "pmf.h"
19 
20 #ifdef CONFIG_AMD_PMF_DEBUG
amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table * in,int index)21 u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index)
22 {
23 	switch (index) {
24 	case 0 ... 1:
25 		return in->ev_info.bios_input_1[index];
26 	case 2 ... 9:
27 		return in->ev_info.bios_input_2[index - 2];
28 	default:
29 		return 0;
30 	}
31 }
32 EXPORT_SYMBOL(amd_pmf_get_ta_custom_bios_inputs);
33 
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)34 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
35 {
36 	int i;
37 
38 	dev_dbg(dev->dev, "==== TA inputs START ====\n");
39 	dev_dbg(dev->dev, "Slider State: %s\n",
40 		amd_pmf_get_slider_position(in->ev_info.power_slider));
41 	dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
42 	dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
43 	dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
44 	dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
45 	dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
46 	dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
47 	dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
48 	dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
49 	dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
50 	dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
51 	dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
52 	dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
53 	dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
54 	dev_dbg(dev->dev, "Platform type: %s\n",
55 		amd_pmf_get_platform_type(in->ev_info.platform_type));
56 	dev_dbg(dev->dev, "Laptop placement: %s\n",
57 		amd_pmf_get_laptop_placement(in->ev_info.device_state));
58 	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
59 		dev_dbg(dev->dev, "Custom BIOS input%d: %u\n", i + 1,
60 			amd_pmf_get_ta_custom_bios_inputs(in, i));
61 	dev_dbg(dev->dev, "==== TA inputs END ====\n");
62 }
63 #else
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)64 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
65 #endif
66 
67 /*
68  * This helper function sets the appropriate BIOS input value in the TA enact
69  * table based on the provided index. We need this approach because the custom
70  * BIOS input array is not continuous, due to the existing TA structure layout.
71  */
amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table * in,int index,u32 value)72 static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int index, u32 value)
73 {
74 	switch (index) {
75 	case 0 ... 1:
76 		in->ev_info.bios_input_1[index] = value;
77 		break;
78 	case 2 ... 9:
79 		in->ev_info.bios_input_2[index - 2] = value;
80 		break;
81 	default:
82 		return;
83 	}
84 }
85 
amd_pmf_update_bios_inputs(struct amd_pmf_dev * pdev,struct pmf_bios_input_entry * data,const struct amd_pmf_pb_bitmap * inputs,struct ta_pmf_enact_table * in)86 static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, struct pmf_bios_input_entry *data,
87 				       const struct amd_pmf_pb_bitmap *inputs,
88 				       struct ta_pmf_enact_table *in)
89 {
90 	unsigned int i;
91 
92 	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {
93 		if (!(data->preq & inputs[i].bit_mask))
94 			continue;
95 		amd_pmf_set_ta_custom_bios_input(in, i, data->val[i]);
96 		pdev->cb_prev.custom_bios_inputs[i] = data->val[i];
97 		dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, data->val[i]);
98 	}
99 }
100 
amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev * pdev,struct ta_pmf_enact_table * in)101 static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
102 					   struct ta_pmf_enact_table *in)
103 {
104 	struct pmf_cbi_ring_buffer *rb = &pdev->cbi_buf;
105 	unsigned int i;
106 
107 	guard(mutex)(&pdev->cbi_mutex);
108 
109 	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
110 		amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);
111 
112 	if (CIRC_CNT(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0)
113 		return;
114 
115 	/* If no active custom BIOS input pending request, do not consume further work */
116 	if (!rb->data[rb->tail].preq)
117 		goto out_rbadvance;
118 
119 	if (!pdev->smart_pc_enabled)
120 		return;
121 
122 	switch (pdev->pmf_if_version) {
123 	case PMF_IF_V1:
124 		if (!is_apmf_bios_input_notifications_supported(pdev))
125 			return;
126 		amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs_v1, in);
127 		break;
128 	case PMF_IF_V2:
129 		amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs, in);
130 		break;
131 	default:
132 		break;
133 	}
134 
135 out_rbadvance:
136 	rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
137 }
138 
amd_pmf_get_c0_residency(u16 * core_res,size_t size,struct ta_pmf_enact_table * in)139 static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
140 {
141 	u16 max, avg = 0;
142 	int i;
143 
144 	/* Get the avg and max C0 residency of all the cores */
145 	max = *core_res;
146 	for (i = 0; i < size; i++) {
147 		avg += core_res[i];
148 		if (core_res[i] > max)
149 			max = core_res[i];
150 	}
151 	avg = DIV_ROUND_CLOSEST(avg, size);
152 	in->ev_info.avg_c0residency = avg;
153 	in->ev_info.max_c0residency = max;
154 }
155 
amd_pmf_get_smu_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)156 static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
157 {
158 	/* Get the updated metrics table data */
159 	memset(dev->buf, 0, dev->mtable_size);
160 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
161 
162 	switch (dev->cpu_id) {
163 	case AMD_CPU_ID_PS:
164 		memcpy(&dev->m_table, dev->buf, dev->mtable_size);
165 		in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
166 		in->ev_info.skin_temperature = dev->m_table.skin_temp;
167 		in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
168 		amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency,
169 					 ARRAY_SIZE(dev->m_table.avg_core_c0residency), in);
170 		break;
171 	case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
172 	case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
173 		memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
174 		in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power;
175 		in->ev_info.skin_temperature = dev->m_table_v2.skin_temp;
176 		in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity;
177 		amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency,
178 					 ARRAY_SIZE(dev->m_table_v2.core_c0residency), in);
179 		break;
180 	default:
181 		dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id);
182 	}
183 }
184 
185 static const char * const pmf_battery_supply_name[] = {
186 	"BATT",
187 	"BAT0",
188 };
189 
amd_pmf_get_battery_prop(enum power_supply_property prop)190 static int amd_pmf_get_battery_prop(enum power_supply_property prop)
191 {
192 	union power_supply_propval value;
193 	struct power_supply *psy;
194 	int i, ret;
195 
196 	for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
197 		psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
198 		if (!psy)
199 			continue;
200 
201 		ret = power_supply_get_property(psy, prop, &value);
202 		if (ret) {
203 			power_supply_put(psy);
204 			return ret;
205 		}
206 	}
207 
208 	return value.intval;
209 }
210 
amd_pmf_get_battery_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)211 static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
212 {
213 	int val;
214 
215 	val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
216 	if (val < 0)
217 		return val;
218 	if (val != 1)
219 		return -ENODEV;
220 
221 	in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
222 	/* all values in mWh metrics */
223 	in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
224 		MILLIWATT_PER_WATT;
225 	in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
226 		MILLIWATT_PER_WATT;
227 	in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
228 		MILLIWATT_PER_WATT;
229 
230 	return 0;
231 }
232 
amd_pmf_get_slider_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)233 static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
234 {
235 	int val;
236 
237 	switch (dev->current_profile) {
238 	case PLATFORM_PROFILE_PERFORMANCE:
239 	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
240 		val = AMD_PMF_TA_BEST_PERFORMANCE;
241 		break;
242 	case PLATFORM_PROFILE_BALANCED:
243 		val = AMD_PMF_TA_BETTER_PERFORMANCE;
244 		break;
245 	case PLATFORM_PROFILE_LOW_POWER:
246 	case PLATFORM_PROFILE_QUIET:
247 		val = AMD_PMF_TA_BEST_BATTERY;
248 		break;
249 	default:
250 		dev_err(dev->dev, "Unknown Platform Profile.\n");
251 		return -EOPNOTSUPP;
252 	}
253 	in->ev_info.power_slider = val;
254 
255 	return 0;
256 }
257 
amd_pmf_get_sensor_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)258 static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
259 {
260 	struct amd_sfh_info sfh_info;
261 
262 	/* Get the latest information from SFH */
263 	in->ev_info.user_present = false;
264 
265 	/* Get ALS data */
266 	if (!amd_get_sfh_info(&sfh_info, MT_ALS))
267 		in->ev_info.ambient_light = sfh_info.ambient_light;
268 	else
269 		dev_dbg(dev->dev, "ALS is not enabled/detected\n");
270 
271 	/* get HPD data */
272 	if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
273 		if (sfh_info.user_present == SFH_USER_PRESENT)
274 			in->ev_info.user_present = true;
275 	} else {
276 		dev_dbg(dev->dev, "HPD is not enabled/detected\n");
277 	}
278 
279 	/* Get SRA (Secondary Accelerometer) data */
280 	if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
281 		in->ev_info.platform_type = sfh_info.platform_type;
282 		in->ev_info.device_state = sfh_info.laptop_placement;
283 	} else {
284 		dev_dbg(dev->dev, "SRA is not enabled/detected\n");
285 	}
286 }
287 
amd_pmf_populate_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)288 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
289 {
290 	/* TA side lid open is 1 and close is 0, hence the ! here */
291 	in->ev_info.lid_state = !acpi_lid_open();
292 	in->ev_info.power_source = amd_pmf_get_power_source();
293 	amd_pmf_get_smu_info(dev, in);
294 	amd_pmf_get_battery_info(dev, in);
295 	amd_pmf_get_slider_info(dev, in);
296 	amd_pmf_get_sensor_info(dev, in);
297 	amd_pmf_get_custom_bios_inputs(dev, in);
298 }
299