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