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 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 } 280