]> Git Repo - linux.git/blob - drivers/platform/x86/amd/pmf/spc.c
Linux 6.14-rc3
[linux.git] / drivers / platform / x86 / amd / pmf / spc.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 <[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
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
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
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
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
98 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
99 #endif
100
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
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
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
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
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
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                 val = TA_BEST_PERFORMANCE;
223                 break;
224         case PLATFORM_PROFILE_BALANCED:
225                 val = TA_BETTER_PERFORMANCE;
226                 break;
227         case PLATFORM_PROFILE_LOW_POWER:
228                 val = TA_BEST_BATTERY;
229                 break;
230         default:
231                 dev_err(dev->dev, "Unknown Platform Profile.\n");
232                 return -EOPNOTSUPP;
233         }
234         in->ev_info.power_slider = val;
235
236         return 0;
237 }
238
239 static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
240 {
241         struct amd_sfh_info sfh_info;
242
243         /* Get the latest information from SFH */
244         in->ev_info.user_present = false;
245
246         /* Get ALS data */
247         if (!amd_get_sfh_info(&sfh_info, MT_ALS))
248                 in->ev_info.ambient_light = sfh_info.ambient_light;
249         else
250                 dev_dbg(dev->dev, "ALS is not enabled/detected\n");
251
252         /* get HPD data */
253         if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
254                 if (sfh_info.user_present == SFH_USER_PRESENT)
255                         in->ev_info.user_present = true;
256         } else {
257                 dev_dbg(dev->dev, "HPD is not enabled/detected\n");
258         }
259
260         /* Get SRA (Secondary Accelerometer) data */
261         if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
262                 in->ev_info.platform_type = sfh_info.platform_type;
263                 in->ev_info.device_state = sfh_info.laptop_placement;
264         } else {
265                 dev_dbg(dev->dev, "SRA is not enabled/detected\n");
266         }
267 }
268
269 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
270 {
271         /* TA side lid open is 1 and close is 0, hence the ! here */
272         in->ev_info.lid_state = !acpi_lid_open();
273         in->ev_info.power_source = amd_pmf_get_power_source();
274         amd_pmf_get_smu_info(dev, in);
275         amd_pmf_get_battery_info(dev, in);
276         amd_pmf_get_slider_info(dev, in);
277         amd_pmf_get_sensor_info(dev, in);
278         amd_pmf_get_custom_bios_inputs(dev, in);
279 }
This page took 0.047742 seconds and 4 git commands to generate.