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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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