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
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_get_ta_custom_bios_inputs(struct ta_pmf_enact_table * in,int index)73 static 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
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)85 void 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
amd_pmf_dump_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)113 void 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 */
amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table * in,int index,u32 value)121 static 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
amd_pmf_update_bios_inputs(struct amd_pmf_dev * pdev,u32 pending_req,const struct amd_pmf_pb_bitmap * inputs,const u32 * custom_policy,struct ta_pmf_enact_table * in)135 static 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
amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev * pdev,struct ta_pmf_enact_table * in)150 static 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
amd_pmf_get_c0_residency(u16 * core_res,size_t size,struct ta_pmf_enact_table * in)184 static 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
amd_pmf_get_smu_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)201 static 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
230 static const char * const pmf_battery_supply_name[] = {
231 "BATT",
232 "BAT0",
233 };
234
amd_pmf_get_battery_prop(enum power_supply_property prop)235 static 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
amd_pmf_get_battery_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)256 static 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
amd_pmf_get_slider_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)278 static 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
amd_pmf_get_sensor_info(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)303 static 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
amd_pmf_populate_ta_inputs(struct amd_pmf_dev * dev,struct ta_pmf_enact_table * in)333 void 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 }
344