468 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * AMD Platform Management Framework Driver4 *5 * Copyright (c) 2022, Advanced Micro Devices, Inc.6 * All Rights Reserved.7 *8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>9 */10 11#include <linux/string_choices.h>12#include <linux/workqueue.h>13#include "pmf.h"14 15static struct cnqf_config config_store;16 17#ifdef CONFIG_AMD_PMF_DEBUG18static const char *state_as_str_cnqf(unsigned int state)19{20 switch (state) {21 case APMF_CNQF_TURBO:22 return "turbo";23 case APMF_CNQF_PERFORMANCE:24 return "performance";25 case APMF_CNQF_BALANCE:26 return "balance";27 case APMF_CNQF_QUIET:28 return "quiet";29 default:30 return "Unknown CnQF State";31 }32}33 34static void amd_pmf_cnqf_dump_defaults(struct apmf_dyn_slider_output *data, int idx)35{36 int i;37 38 pr_debug("Dynamic Slider %s Defaults - BEGIN\n", idx ? "DC" : "AC");39 pr_debug("size: %u\n", data->size);40 pr_debug("flags: 0x%x\n", data->flags);41 42 /* Time constants */43 pr_debug("t_perf_to_turbo: %u ms\n", data->t_perf_to_turbo);44 pr_debug("t_balanced_to_perf: %u ms\n", data->t_balanced_to_perf);45 pr_debug("t_quiet_to_balanced: %u ms\n", data->t_quiet_to_balanced);46 pr_debug("t_balanced_to_quiet: %u ms\n", data->t_balanced_to_quiet);47 pr_debug("t_perf_to_balanced: %u ms\n", data->t_perf_to_balanced);48 pr_debug("t_turbo_to_perf: %u ms\n", data->t_turbo_to_perf);49 50 for (i = 0 ; i < CNQF_MODE_MAX ; i++) {51 pr_debug("pfloor_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].pfloor);52 pr_debug("fppt_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].fppt);53 pr_debug("sppt_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].sppt);54 pr_debug("sppt_apuonly_%s: %u mW\n",55 state_as_str_cnqf(i), data->ps[i].sppt_apu_only);56 pr_debug("spl_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].spl);57 pr_debug("stt_minlimit_%s: %u mW\n",58 state_as_str_cnqf(i), data->ps[i].stt_min_limit);59 pr_debug("stt_skintemp_apu_%s: %u C\n", state_as_str_cnqf(i),60 data->ps[i].stt_skintemp[STT_TEMP_APU]);61 pr_debug("stt_skintemp_hs2_%s: %u C\n", state_as_str_cnqf(i),62 data->ps[i].stt_skintemp[STT_TEMP_HS2]);63 pr_debug("fan_id_%s: %u\n", state_as_str_cnqf(i), data->ps[i].fan_id);64 }65 66 pr_debug("Dynamic Slider %s Defaults - END\n", idx ? "DC" : "AC");67}68#else69static void amd_pmf_cnqf_dump_defaults(struct apmf_dyn_slider_output *data, int idx) {}70#endif71 72static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,73 struct cnqf_config *table)74{75 struct power_table_control *pc;76 77 pc = &config_store.mode_set[src][idx].power_control;78 79 amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL);80 amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL);81 amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);82 amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);83 amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);84 amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],85 NULL);86 amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],87 NULL);88 89 if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))90 apmf_update_fan_idx(dev,91 config_store.mode_set[src][idx].fan_control.manual,92 config_store.mode_set[src][idx].fan_control.fan_id);93 94 return 0;95}96 97static void amd_pmf_update_power_threshold(int src)98{99 struct cnqf_mode_settings *ts;100 struct cnqf_tran_params *tp;101 102 tp = &config_store.trans_param[src][CNQF_TRANSITION_TO_QUIET];103 ts = &config_store.mode_set[src][CNQF_MODE_BALANCE];104 tp->power_threshold = ts->power_floor;105 106 tp = &config_store.trans_param[src][CNQF_TRANSITION_TO_TURBO];107 ts = &config_store.mode_set[src][CNQF_MODE_PERFORMANCE];108 tp->power_threshold = ts->power_floor;109 110 tp = &config_store.trans_param[src][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];111 ts = &config_store.mode_set[src][CNQF_MODE_BALANCE];112 tp->power_threshold = ts->power_floor;113 114 tp = &config_store.trans_param[src][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];115 ts = &config_store.mode_set[src][CNQF_MODE_PERFORMANCE];116 tp->power_threshold = ts->power_floor;117 118 tp = &config_store.trans_param[src][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];119 ts = &config_store.mode_set[src][CNQF_MODE_QUIET];120 tp->power_threshold = ts->power_floor;121 122 tp = &config_store.trans_param[src][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];123 ts = &config_store.mode_set[src][CNQF_MODE_TURBO];124 tp->power_threshold = ts->power_floor;125}126 127static const char *state_as_str(unsigned int state)128{129 switch (state) {130 case CNQF_MODE_QUIET:131 return "QUIET";132 case CNQF_MODE_BALANCE:133 return "BALANCED";134 case CNQF_MODE_TURBO:135 return "TURBO";136 case CNQF_MODE_PERFORMANCE:137 return "PERFORMANCE";138 default:139 return "Unknown CnQF mode";140 }141}142 143static int amd_pmf_cnqf_get_power_source(struct amd_pmf_dev *dev)144{145 if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) &&146 is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC))147 return amd_pmf_get_power_source();148 else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC))149 return POWER_SOURCE_DC;150 else151 return POWER_SOURCE_AC;152}153 154int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms)155{156 struct cnqf_tran_params *tp;157 int src, i, j;158 u32 avg_power = 0;159 160 src = amd_pmf_cnqf_get_power_source(dev);161 162 if (is_pprof_balanced(dev)) {163 amd_pmf_set_cnqf(dev, src, config_store.current_mode, NULL);164 } else {165 /*166 * Return from here if the platform_profile is not balanced167 * so that preference is given to user mode selection, rather168 * than enforcing CnQF to run all the time (if enabled)169 */170 return -EINVAL;171 }172 173 for (i = 0; i < CNQF_TRANSITION_MAX; i++) {174 config_store.trans_param[src][i].timer += time_lapsed_ms;175 config_store.trans_param[src][i].total_power += socket_power;176 config_store.trans_param[src][i].count++;177 178 tp = &config_store.trans_param[src][i];179 180#ifdef CONFIG_AMD_PMF_DEBUG181 dev_dbg(dev->dev, "avg_power: %u mW total_power: %u mW count: %u timer: %u ms\n",182 avg_power, config_store.trans_param[src][i].total_power,183 config_store.trans_param[src][i].count,184 config_store.trans_param[src][i].timer);185#endif186 if (tp->timer >= tp->time_constant && tp->count) {187 avg_power = tp->total_power / tp->count;188 189 /* Reset the indices */190 tp->timer = 0;191 tp->total_power = 0;192 tp->count = 0;193 194 if ((tp->shifting_up && avg_power >= tp->power_threshold) ||195 (!tp->shifting_up && avg_power <= tp->power_threshold)) {196 tp->priority = true;197 } else {198 tp->priority = false;199 }200 }201 }202 203 dev_dbg(dev->dev, "[CNQF] Avg power: %u mW socket power: %u mW mode:%s\n",204 avg_power, socket_power, state_as_str(config_store.current_mode));205 206#ifdef CONFIG_AMD_PMF_DEBUG207 dev_dbg(dev->dev, "[CNQF] priority1: %u priority2: %u priority3: %u\n",208 config_store.trans_param[src][0].priority,209 config_store.trans_param[src][1].priority,210 config_store.trans_param[src][2].priority);211 212 dev_dbg(dev->dev, "[CNQF] priority4: %u priority5: %u priority6: %u\n",213 config_store.trans_param[src][3].priority,214 config_store.trans_param[src][4].priority,215 config_store.trans_param[src][5].priority);216#endif217 218 for (j = 0; j < CNQF_TRANSITION_MAX; j++) {219 /* apply the highest priority */220 if (config_store.trans_param[src][j].priority) {221 if (config_store.current_mode !=222 config_store.trans_param[src][j].target_mode) {223 config_store.current_mode =224 config_store.trans_param[src][j].target_mode;225 dev_dbg(dev->dev, "Moving to Mode :%s\n",226 state_as_str(config_store.current_mode));227 amd_pmf_set_cnqf(dev, src,228 config_store.current_mode, NULL);229 }230 break;231 }232 }233 return 0;234}235 236static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output *out)237{238 struct cnqf_tran_params *tp;239 240 tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_QUIET];241 tp->time_constant = out->t_balanced_to_quiet;242 tp->target_mode = CNQF_MODE_QUIET;243 tp->shifting_up = false;244 245 tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE];246 tp->time_constant = out->t_balanced_to_perf;247 tp->target_mode = CNQF_MODE_PERFORMANCE;248 tp->shifting_up = true;249 250 tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE];251 tp->time_constant = out->t_quiet_to_balanced;252 tp->target_mode = CNQF_MODE_BALANCE;253 tp->shifting_up = true;254 255 tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE];256 tp->time_constant = out->t_perf_to_balanced;257 tp->target_mode = CNQF_MODE_BALANCE;258 tp->shifting_up = false;259 260 tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE];261 tp->time_constant = out->t_turbo_to_perf;262 tp->target_mode = CNQF_MODE_PERFORMANCE;263 tp->shifting_up = false;264 265 tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_TURBO];266 tp->time_constant = out->t_perf_to_turbo;267 tp->target_mode = CNQF_MODE_TURBO;268 tp->shifting_up = true;269}270 271static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output *out)272{273 struct cnqf_mode_settings *ms;274 275 /* Quiet Mode */276 ms = &config_store.mode_set[idx][CNQF_MODE_QUIET];277 ms->power_floor = out->ps[APMF_CNQF_QUIET].pfloor;278 ms->power_control.fppt = out->ps[APMF_CNQF_QUIET].fppt;279 ms->power_control.sppt = out->ps[APMF_CNQF_QUIET].sppt;280 ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_QUIET].sppt_apu_only;281 ms->power_control.spl = out->ps[APMF_CNQF_QUIET].spl;282 ms->power_control.stt_min = out->ps[APMF_CNQF_QUIET].stt_min_limit;283 ms->power_control.stt_skin_temp[STT_TEMP_APU] =284 out->ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU];285 ms->power_control.stt_skin_temp[STT_TEMP_HS2] =286 out->ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2];287 ms->fan_control.fan_id = out->ps[APMF_CNQF_QUIET].fan_id;288 289 /* Balance Mode */290 ms = &config_store.mode_set[idx][CNQF_MODE_BALANCE];291 ms->power_floor = out->ps[APMF_CNQF_BALANCE].pfloor;292 ms->power_control.fppt = out->ps[APMF_CNQF_BALANCE].fppt;293 ms->power_control.sppt = out->ps[APMF_CNQF_BALANCE].sppt;294 ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_BALANCE].sppt_apu_only;295 ms->power_control.spl = out->ps[APMF_CNQF_BALANCE].spl;296 ms->power_control.stt_min = out->ps[APMF_CNQF_BALANCE].stt_min_limit;297 ms->power_control.stt_skin_temp[STT_TEMP_APU] =298 out->ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU];299 ms->power_control.stt_skin_temp[STT_TEMP_HS2] =300 out->ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2];301 ms->fan_control.fan_id = out->ps[APMF_CNQF_BALANCE].fan_id;302 303 /* Performance Mode */304 ms = &config_store.mode_set[idx][CNQF_MODE_PERFORMANCE];305 ms->power_floor = out->ps[APMF_CNQF_PERFORMANCE].pfloor;306 ms->power_control.fppt = out->ps[APMF_CNQF_PERFORMANCE].fppt;307 ms->power_control.sppt = out->ps[APMF_CNQF_PERFORMANCE].sppt;308 ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_PERFORMANCE].sppt_apu_only;309 ms->power_control.spl = out->ps[APMF_CNQF_PERFORMANCE].spl;310 ms->power_control.stt_min = out->ps[APMF_CNQF_PERFORMANCE].stt_min_limit;311 ms->power_control.stt_skin_temp[STT_TEMP_APU] =312 out->ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU];313 ms->power_control.stt_skin_temp[STT_TEMP_HS2] =314 out->ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2];315 ms->fan_control.fan_id = out->ps[APMF_CNQF_PERFORMANCE].fan_id;316 317 /* Turbo Mode */318 ms = &config_store.mode_set[idx][CNQF_MODE_TURBO];319 ms->power_floor = out->ps[APMF_CNQF_TURBO].pfloor;320 ms->power_control.fppt = out->ps[APMF_CNQF_TURBO].fppt;321 ms->power_control.sppt = out->ps[APMF_CNQF_TURBO].sppt;322 ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_TURBO].sppt_apu_only;323 ms->power_control.spl = out->ps[APMF_CNQF_TURBO].spl;324 ms->power_control.stt_min = out->ps[APMF_CNQF_TURBO].stt_min_limit;325 ms->power_control.stt_skin_temp[STT_TEMP_APU] =326 out->ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU];327 ms->power_control.stt_skin_temp[STT_TEMP_HS2] =328 out->ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2];329 ms->fan_control.fan_id = out->ps[APMF_CNQF_TURBO].fan_id;330}331 332static int amd_pmf_check_flags(struct amd_pmf_dev *dev)333{334 struct apmf_dyn_slider_output out = {};335 336 if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC))337 apmf_get_dyn_slider_def_ac(dev, &out);338 else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC))339 apmf_get_dyn_slider_def_dc(dev, &out);340 341 return out.flags;342}343 344static int amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)345{346 struct apmf_dyn_slider_output out;347 int i, j, ret;348 349 for (i = 0; i < POWER_SOURCE_MAX; i++) {350 if (!is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC + i))351 continue;352 353 if (i == POWER_SOURCE_AC)354 ret = apmf_get_dyn_slider_def_ac(dev, &out);355 else356 ret = apmf_get_dyn_slider_def_dc(dev, &out);357 if (ret) {358 dev_err(dev->dev, "APMF apmf_get_dyn_slider_def_dc failed :%d\n", ret);359 return ret;360 }361 362 amd_pmf_cnqf_dump_defaults(&out, i);363 amd_pmf_update_mode_set(i, &out);364 amd_pmf_update_trans_data(i, &out);365 amd_pmf_update_power_threshold(i);366 367 for (j = 0; j < CNQF_MODE_MAX; j++) {368 if (config_store.mode_set[i][j].fan_control.fan_id == FAN_INDEX_AUTO)369 config_store.mode_set[i][j].fan_control.manual = false;370 else371 config_store.mode_set[i][j].fan_control.manual = true;372 }373 }374 375 /* set to initial default values */376 config_store.current_mode = CNQF_MODE_BALANCE;377 378 return 0;379}380 381static ssize_t cnqf_enable_store(struct device *dev,382 struct device_attribute *attr,383 const char *buf, size_t count)384{385 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);386 int result, src;387 bool input;388 389 result = kstrtobool(buf, &input);390 if (result)391 return result;392 393 src = amd_pmf_cnqf_get_power_source(pdev);394 pdev->cnqf_enabled = input;395 396 if (pdev->cnqf_enabled && is_pprof_balanced(pdev)) {397 amd_pmf_set_cnqf(pdev, src, config_store.current_mode, NULL);398 } else {399 if (is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))400 amd_pmf_set_sps_power_limits(pdev);401 }402 403 dev_dbg(pdev->dev, "Received CnQF %s\n", str_on_off(input));404 return count;405}406 407static ssize_t cnqf_enable_show(struct device *dev,408 struct device_attribute *attr,409 char *buf)410{411 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);412 413 return sysfs_emit(buf, "%s\n", str_on_off(pdev->cnqf_enabled));414}415 416static DEVICE_ATTR_RW(cnqf_enable);417 418static umode_t cnqf_feature_is_visible(struct kobject *kobj,419 struct attribute *attr, int n)420{421 struct device *dev = kobj_to_dev(kobj);422 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);423 424 return pdev->cnqf_supported ? attr->mode : 0;425}426 427static struct attribute *cnqf_feature_attrs[] = {428 &dev_attr_cnqf_enable.attr,429 NULL430};431 432const struct attribute_group cnqf_feature_attribute_group = {433 .is_visible = cnqf_feature_is_visible,434 .attrs = cnqf_feature_attrs,435};436 437void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev)438{439 cancel_delayed_work_sync(&dev->work_buffer);440}441 442int amd_pmf_init_cnqf(struct amd_pmf_dev *dev)443{444 int ret, src;445 446 /*447 * Note the caller of this function has already checked that both448 * APMF_FUNC_DYN_SLIDER_AC and APMF_FUNC_DYN_SLIDER_DC are supported.449 */450 451 ret = amd_pmf_load_defaults_cnqf(dev);452 if (ret < 0)453 return ret;454 455 amd_pmf_init_metrics_table(dev);456 457 dev->cnqf_supported = true;458 dev->cnqf_enabled = amd_pmf_check_flags(dev);459 460 /* update the thermal for CnQF */461 if (dev->cnqf_enabled && is_pprof_balanced(dev)) {462 src = amd_pmf_cnqf_get_power_source(dev);463 amd_pmf_set_cnqf(dev, src, config_store.current_mode, NULL);464 }465 466 return 0;467}468