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