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 *ta_slider_as_str(unsigned int state) 20 { 21 switch (state) { 22 case TA_BEST_PERFORMANCE: 23 return "PERFORMANCE"; 24 case TA_BETTER_PERFORMANCE: 25 return "BALANCED"; 26 case TA_BEST_BATTERY: 27 return "POWER_SAVER"; 28 default: 29 return "Unknown TA Slider State"; 30 } 31 } 32 33 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 34 { 35 dev_dbg(dev->dev, "==== TA inputs START ====\n"); 36 dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider)); 37 dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source)); 38 dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage); 39 dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design); 40 dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity); 41 dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate); 42 dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power); 43 dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature); 44 dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency); 45 dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency); 46 dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy); 47 dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); 48 dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away"); 49 dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light); 50 dev_dbg(dev->dev, "==== TA inputs END ====\n"); 51 } 52 #else 53 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} 54 #endif 55 56 static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) 57 { 58 u16 max, avg = 0; 59 int i; 60 61 /* Get the avg and max C0 residency of all the cores */ 62 max = *core_res; 63 for (i = 0; i < size; i++) { 64 avg += core_res[i]; 65 if (core_res[i] > max) 66 max = core_res[i]; 67 } 68 avg = DIV_ROUND_CLOSEST(avg, size); 69 in->ev_info.avg_c0residency = avg; 70 in->ev_info.max_c0residency = max; 71 } 72 73 static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 74 { 75 /* Get the updated metrics table data */ 76 memset(dev->buf, 0, dev->mtable_size); 77 amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); 78 79 switch (dev->cpu_id) { 80 case AMD_CPU_ID_PS: 81 memcpy(&dev->m_table, dev->buf, dev->mtable_size); 82 in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; 83 in->ev_info.skin_temperature = dev->m_table.skin_temp; 84 in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; 85 amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency, 86 ARRAY_SIZE(dev->m_table.avg_core_c0residency), in); 87 break; 88 case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: 89 case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: 90 memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size); 91 in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power; 92 in->ev_info.skin_temperature = dev->m_table_v2.skin_temp; 93 in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity; 94 amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency, 95 ARRAY_SIZE(dev->m_table_v2.core_c0residency), in); 96 break; 97 default: 98 dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id); 99 } 100 } 101 102 static const char * const pmf_battery_supply_name[] = { 103 "BATT", 104 "BAT0", 105 }; 106 107 static int amd_pmf_get_battery_prop(enum power_supply_property prop) 108 { 109 union power_supply_propval value; 110 struct power_supply *psy; 111 int i, ret; 112 113 for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) { 114 psy = power_supply_get_by_name(pmf_battery_supply_name[i]); 115 if (!psy) 116 continue; 117 118 ret = power_supply_get_property(psy, prop, &value); 119 if (ret) { 120 power_supply_put(psy); 121 return ret; 122 } 123 } 124 125 return value.intval; 126 } 127 128 static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 129 { 130 int val; 131 132 val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT); 133 if (val < 0) 134 return val; 135 if (val != 1) 136 return -ENODEV; 137 138 in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY); 139 /* all values in mWh metrics */ 140 in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) / 141 MILLIWATT_PER_WATT; 142 in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) / 143 MILLIWATT_PER_WATT; 144 in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) / 145 MILLIWATT_PER_WATT; 146 147 return 0; 148 } 149 150 static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 151 { 152 int val; 153 154 switch (dev->current_profile) { 155 case PLATFORM_PROFILE_PERFORMANCE: 156 val = TA_BEST_PERFORMANCE; 157 break; 158 case PLATFORM_PROFILE_BALANCED: 159 val = TA_BETTER_PERFORMANCE; 160 break; 161 case PLATFORM_PROFILE_LOW_POWER: 162 val = TA_BEST_BATTERY; 163 break; 164 default: 165 dev_err(dev->dev, "Unknown Platform Profile.\n"); 166 return -EOPNOTSUPP; 167 } 168 in->ev_info.power_slider = val; 169 170 return 0; 171 } 172 173 static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 174 { 175 struct amd_sfh_info sfh_info; 176 177 /* Get the latest information from SFH */ 178 in->ev_info.user_present = false; 179 180 /* Get ALS data */ 181 if (!amd_get_sfh_info(&sfh_info, MT_ALS)) 182 in->ev_info.ambient_light = sfh_info.ambient_light; 183 else 184 dev_dbg(dev->dev, "ALS is not enabled/detected\n"); 185 186 /* get HPD data */ 187 if (!amd_get_sfh_info(&sfh_info, MT_HPD)) { 188 if (sfh_info.user_present == SFH_USER_PRESENT) 189 in->ev_info.user_present = true; 190 } else { 191 dev_dbg(dev->dev, "HPD is not enabled/detected\n"); 192 } 193 } 194 195 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) 196 { 197 /* TA side lid open is 1 and close is 0, hence the ! here */ 198 in->ev_info.lid_state = !acpi_lid_open(); 199 in->ev_info.power_source = amd_pmf_get_power_source(); 200 amd_pmf_get_smu_info(dev, in); 201 amd_pmf_get_battery_info(dev, in); 202 amd_pmf_get_slider_info(dev, in); 203 amd_pmf_get_sensor_info(dev, in); 204 } 205