File Index
Files and directories
Sign in
FILE

spc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/amd/pmf/spc.c C
  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-io.h>
 14#include <linux/power_supply.h>
 15#include <linux/units.h>
 16#include "pmf.h"
 17
 18#ifdef CONFIG_AMD_PMF_DEBUG
 19static const char *platform_type_as_str(u16 platform_type)
 20{
 21	switch (platform_type) {
 22	case CLAMSHELL:
 23		return "CLAMSHELL";
 24	case FLAT:
 25		return "FLAT";
 26	case TENT:
 27		return "TENT";
 28	case STAND:
 29		return "STAND";
 30	case TABLET:
 31		return "TABLET";
 32	case BOOK:
 33		return "BOOK";
 34	case PRESENTATION:
 35		return "PRESENTATION";
 36	case PULL_FWD:
 37		return "PULL_FWD";
 38	default:
 39		return "UNKNOWN";
 40	}
 41}
 42
 43static const char *laptop_placement_as_str(u16 device_state)
 44{
 45	switch (device_state) {
 46	case ON_TABLE:
 47		return "ON_TABLE";
 48	case ON_LAP_MOTION:
 49		return "ON_LAP_MOTION";
 50	case IN_BAG:
 51		return "IN_BAG";
 52	case OUT_OF_BAG:
 53		return "OUT_OF_BAG";
 54	default:
 55		return "UNKNOWN";
 56	}
 57}
 58
 59static const char *ta_slider_as_str(unsigned int state)
 60{
 61	switch (state) {
 62	case TA_BEST_PERFORMANCE:
 63		return "PERFORMANCE";
 64	case TA_BETTER_PERFORMANCE:
 65		return "BALANCED";
 66	case TA_BEST_BATTERY:
 67		return "POWER_SAVER";
 68	default:
 69		return "Unknown TA Slider State";
 70	}
 71}
 72
 73static u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index)
 74{
 75	switch (index) {
 76	case 0 ... 1:
 77		return in->ev_info.bios_input_1[index];
 78	case 2 ... 9:
 79		return in->ev_info.bios_input_2[index - 2];
 80	default:
 81		return 0;
 82	}
 83}
 84
 85void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 86{
 87	int i;
 88
 89	dev_dbg(dev->dev, "==== TA inputs START ====\n");
 90	dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
 91	dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
 92	dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
 93	dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
 94	dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
 95	dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
 96	dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
 97	dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
 98	dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
 99	dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
100	dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
101	dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
102	dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
103	dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
104	dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type));
105	dev_dbg(dev->dev, "Laptop placement: %s\n",
106		laptop_placement_as_str(in->ev_info.device_state));
107	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
108		dev_dbg(dev->dev, "Custom BIOS input%d: %u\n", i + 1,
109			amd_pmf_get_ta_custom_bios_inputs(in, i));
110	dev_dbg(dev->dev, "==== TA inputs END ====\n");
111}
112#else
113void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
114#endif
115
116/*
117 * This helper function sets the appropriate BIOS input value in the TA enact
118 * table based on the provided index. We need this approach because the custom
119 * BIOS input array is not continuous, due to the existing TA structure layout.
120 */
121static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int index, u32 value)
122{
123	switch (index) {
124	case 0 ... 1:
125		in->ev_info.bios_input_1[index] = value;
126		break;
127	case 2 ... 9:
128		in->ev_info.bios_input_2[index - 2] = value;
129		break;
130	default:
131		return;
132	}
133}
134
135static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, u32 pending_req,
136				       const struct amd_pmf_pb_bitmap *inputs,
137				       const u32 *custom_policy, struct ta_pmf_enact_table *in)
138{
139	unsigned int i;
140
141	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {
142		if (!(pending_req & inputs[i].bit_mask))
143			continue;
144		amd_pmf_set_ta_custom_bios_input(in, i, custom_policy[i]);
145		pdev->cb_prev.custom_bios_inputs[i] = custom_policy[i];
146		dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, custom_policy[i]);
147	}
148}
149
150static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
151					   struct ta_pmf_enact_table *in)
152{
153	unsigned int i;
154
155	for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
156		amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);
157
158	if (!(pdev->req.pending_req || pdev->req1.pending_req))
159		return;
160
161	if (!pdev->smart_pc_enabled)
162		return;
163
164	switch (pdev->pmf_if_version) {
165	case PMF_IF_V1:
166		if (!is_apmf_bios_input_notifications_supported(pdev))
167			return;
168		amd_pmf_update_bios_inputs(pdev, pdev->req1.pending_req, custom_bios_inputs_v1,
169					   pdev->req1.custom_policy, in);
170		break;
171	case PMF_IF_V2:
172		amd_pmf_update_bios_inputs(pdev, pdev->req.pending_req, custom_bios_inputs,
173					   pdev->req.custom_policy, in);
174		break;
175	default:
176		break;
177	}
178
179	/* Clear pending requests after handling */
180	memset(&pdev->req, 0, sizeof(pdev->req));
181	memset(&pdev->req1, 0, sizeof(pdev->req1));
182}
183
184static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
185{
186	u16 max, avg = 0;
187	int i;
188
189	/* Get the avg and max C0 residency of all the cores */
190	max = *core_res;
191	for (i = 0; i < size; i++) {
192		avg += core_res[i];
193		if (core_res[i] > max)
194			max = core_res[i];
195	}
196	avg = DIV_ROUND_CLOSEST(avg, size);
197	in->ev_info.avg_c0residency = avg;
198	in->ev_info.max_c0residency = max;
199}
200
201static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
202{
203	/* Get the updated metrics table data */
204	memset(dev->buf, 0, dev->mtable_size);
205	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
206
207	switch (dev->cpu_id) {
208	case AMD_CPU_ID_PS:
209		memcpy(&dev->m_table, dev->buf, dev->mtable_size);
210		in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
211		in->ev_info.skin_temperature = dev->m_table.skin_temp;
212		in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
213		amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency,
214					 ARRAY_SIZE(dev->m_table.avg_core_c0residency), in);
215		break;
216	case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
217	case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
218		memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
219		in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power;
220		in->ev_info.skin_temperature = dev->m_table_v2.skin_temp;
221		in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity;
222		amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency,
223					 ARRAY_SIZE(dev->m_table_v2.core_c0residency), in);
224		break;
225	default:
226		dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id);
227	}
228}
229
230static const char * const pmf_battery_supply_name[] = {
231	"BATT",
232	"BAT0",
233};
234
235static int amd_pmf_get_battery_prop(enum power_supply_property prop)
236{
237	union power_supply_propval value;
238	struct power_supply *psy;
239	int i, ret;
240
241	for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
242		psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
243		if (!psy)
244			continue;
245
246		ret = power_supply_get_property(psy, prop, &value);
247		if (ret) {
248			power_supply_put(psy);
249			return ret;
250		}
251	}
252
253	return value.intval;
254}
255
256static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
257{
258	int val;
259
260	val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
261	if (val < 0)
262		return val;
263	if (val != 1)
264		return -ENODEV;
265
266	in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
267	/* all values in mWh metrics */
268	in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
269		MILLIWATT_PER_WATT;
270	in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
271		MILLIWATT_PER_WATT;
272	in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
273		MILLIWATT_PER_WATT;
274
275	return 0;
276}
277
278static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
279{
280	int val;
281
282	switch (dev->current_profile) {
283	case PLATFORM_PROFILE_PERFORMANCE:
284	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
285		val = TA_BEST_PERFORMANCE;
286		break;
287	case PLATFORM_PROFILE_BALANCED:
288		val = TA_BETTER_PERFORMANCE;
289		break;
290	case PLATFORM_PROFILE_LOW_POWER:
291	case PLATFORM_PROFILE_QUIET:
292		val = TA_BEST_BATTERY;
293		break;
294	default:
295		dev_err(dev->dev, "Unknown Platform Profile.\n");
296		return -EOPNOTSUPP;
297	}
298	in->ev_info.power_slider = val;
299
300	return 0;
301}
302
303static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
304{
305	struct amd_sfh_info sfh_info;
306
307	/* Get the latest information from SFH */
308	in->ev_info.user_present = false;
309
310	/* Get ALS data */
311	if (!amd_get_sfh_info(&sfh_info, MT_ALS))
312		in->ev_info.ambient_light = sfh_info.ambient_light;
313	else
314		dev_dbg(dev->dev, "ALS is not enabled/detected\n");
315
316	/* get HPD data */
317	if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
318		if (sfh_info.user_present == SFH_USER_PRESENT)
319			in->ev_info.user_present = true;
320	} else {
321		dev_dbg(dev->dev, "HPD is not enabled/detected\n");
322	}
323
324	/* Get SRA (Secondary Accelerometer) data */
325	if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
326		in->ev_info.platform_type = sfh_info.platform_type;
327		in->ev_info.device_state = sfh_info.laptop_placement;
328	} else {
329		dev_dbg(dev->dev, "SRA is not enabled/detected\n");
330	}
331}
332
333void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
334{
335	/* TA side lid open is 1 and close is 0, hence the ! here */
336	in->ev_info.lid_state = !acpi_lid_open();
337	in->ev_info.power_source = amd_pmf_get_power_source();
338	amd_pmf_get_smu_info(dev, in);
339	amd_pmf_get_battery_info(dev, in);
340	amd_pmf_get_slider_info(dev, in);
341	amd_pmf_get_sensor_info(dev, in);
342	amd_pmf_get_custom_bios_inputs(dev, in);
343}
~/ Read-only file preview.