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 <[email protected]>
9  *          Patil Rajesh Reddy <[email protected]>
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
platform_type_as_str(u16 platform_type)19 static 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 
laptop_placement_as_str(u16 device_state)43 static 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 
ta_slider_as_str(unsigned int state)59 static 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 
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)73 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
74 {
75 	dev_dbg(dev->dev, "==== TA inputs START ====\n");
76 	dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
77 	dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
78 	dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
79 	dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
80 	dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
81 	dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
82 	dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
83 	dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
84 	dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
85 	dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
86 	dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
87 	dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
88 	dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
89 	dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
90 	dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type));
91 	dev_dbg(dev->dev, "Laptop placement: %s\n",
92 		laptop_placement_as_str(in->ev_info.device_state));
93 	dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1);
94 	dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2);
95 	dev_dbg(dev->dev, "==== TA inputs END ====\n");
96 }
97 #else
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)98 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
99 #endif
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 	if (!pdev->req.pending_req)
105 		return;
106 
107 	switch (pdev->req.pending_req) {
108 	case BIT(NOTIFY_CUSTOM_BIOS_INPUT1):
109 		in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1];
110 		break;
111 	case BIT(NOTIFY_CUSTOM_BIOS_INPUT2):
112 		in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2];
113 		break;
114 	default:
115 		dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req);
116 	}
117 
118 	/* Clear pending requests after handling */
119 	memset(&pdev->req, 0, sizeof(pdev->req));
120 }
121 
amd_pmf_get_c0_residency(u16 * core_res,size_t size,struct ta_pmf_enact_table * in)122 static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
123 {
124 	u16 max, avg = 0;
125 	int i;
126 
127 	/* Get the avg and max C0 residency of all the cores */
128 	max = *core_res;
129 	for (i = 0; i < size; i++) {
130 		avg += core_res[i];
131 		if (core_res[i] > max)
132 			max = core_res[i];
133 	}
134 	avg = DIV_ROUND_CLOSEST(avg, size);
135 	in->ev_info.avg_c0residency = avg;
136 	in->ev_info.max_c0residency = max;
137 }
138 
amd_pmf_get_smu_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)139 static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
140 {
141 	/* Get the updated metrics table data */
142 	memset(dev->buf, 0, dev->mtable_size);
143 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
144 
145 	switch (dev->cpu_id) {
146 	case AMD_CPU_ID_PS:
147 		memcpy(&dev->m_table, dev->buf, dev->mtable_size);
148 		in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
149 		in->ev_info.skin_temperature = dev->m_table.skin_temp;
150 		in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
151 		amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency,
152 					 ARRAY_SIZE(dev->m_table.avg_core_c0residency), in);
153 		break;
154 	case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
155 	case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
156 		memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
157 		in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power;
158 		in->ev_info.skin_temperature = dev->m_table_v2.skin_temp;
159 		in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity;
160 		amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency,
161 					 ARRAY_SIZE(dev->m_table_v2.core_c0residency), in);
162 		break;
163 	default:
164 		dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id);
165 	}
166 }
167 
168 static const char * const pmf_battery_supply_name[] = {
169 	"BATT",
170 	"BAT0",
171 };
172 
amd_pmf_get_battery_prop(enum power_supply_property prop)173 static int amd_pmf_get_battery_prop(enum power_supply_property prop)
174 {
175 	union power_supply_propval value;
176 	struct power_supply *psy;
177 	int i, ret;
178 
179 	for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
180 		psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
181 		if (!psy)
182 			continue;
183 
184 		ret = power_supply_get_property(psy, prop, &value);
185 		if (ret) {
186 			power_supply_put(psy);
187 			return ret;
188 		}
189 	}
190 
191 	return value.intval;
192 }
193 
amd_pmf_get_battery_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)194 static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
195 {
196 	int val;
197 
198 	val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
199 	if (val < 0)
200 		return val;
201 	if (val != 1)
202 		return -ENODEV;
203 
204 	in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
205 	/* all values in mWh metrics */
206 	in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
207 		MILLIWATT_PER_WATT;
208 	in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
209 		MILLIWATT_PER_WATT;
210 	in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
211 		MILLIWATT_PER_WATT;
212 
213 	return 0;
214 }
215 
amd_pmf_get_slider_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)216 static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
217 {
218 	int val;
219 
220 	switch (dev->current_profile) {
221 	case PLATFORM_PROFILE_PERFORMANCE:
222 	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
223 		val = TA_BEST_PERFORMANCE;
224 		break;
225 	case PLATFORM_PROFILE_BALANCED:
226 		val = TA_BETTER_PERFORMANCE;
227 		break;
228 	case PLATFORM_PROFILE_LOW_POWER:
229 	case PLATFORM_PROFILE_QUIET:
230 		val = TA_BEST_BATTERY;
231 		break;
232 	default:
233 		dev_err(dev->dev, "Unknown Platform Profile.\n");
234 		return -EOPNOTSUPP;
235 	}
236 	in->ev_info.power_slider = val;
237 
238 	return 0;
239 }
240 
amd_pmf_get_sensor_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)241 static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
242 {
243 	struct amd_sfh_info sfh_info;
244 
245 	/* Get the latest information from SFH */
246 	in->ev_info.user_present = false;
247 
248 	/* Get ALS data */
249 	if (!amd_get_sfh_info(&sfh_info, MT_ALS))
250 		in->ev_info.ambient_light = sfh_info.ambient_light;
251 	else
252 		dev_dbg(dev->dev, "ALS is not enabled/detected\n");
253 
254 	/* get HPD data */
255 	if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
256 		if (sfh_info.user_present == SFH_USER_PRESENT)
257 			in->ev_info.user_present = true;
258 	} else {
259 		dev_dbg(dev->dev, "HPD is not enabled/detected\n");
260 	}
261 
262 	/* Get SRA (Secondary Accelerometer) data */
263 	if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
264 		in->ev_info.platform_type = sfh_info.platform_type;
265 		in->ev_info.device_state = sfh_info.laptop_placement;
266 	} else {
267 		dev_dbg(dev->dev, "SRA is not enabled/detected\n");
268 	}
269 }
270 
amd_pmf_populate_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)271 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
272 {
273 	/* TA side lid open is 1 and close is 0, hence the ! here */
274 	in->ev_info.lid_state = !acpi_lid_open();
275 	in->ev_info.power_source = amd_pmf_get_power_source();
276 	amd_pmf_get_smu_info(dev, in);
277 	amd_pmf_get_battery_info(dev, in);
278 	amd_pmf_get_slider_info(dev, in);
279 	amd_pmf_get_sensor_info(dev, in);
280 	amd_pmf_get_custom_bios_inputs(dev, in);
281 }
282