Message ID | 20220802151149.2123699-9-Shyam-sundar.S-k@amd.com (mailing list archive) |
---|---|
State | Accepted, archived |
Headers | show |
Series | platform/x86/amd/pmf: Introduce AMD PMF Driver | expand |
Hi, On 8/2/22 17:11, Shyam Sundar S K wrote: > This feature has 3 modes quiet, balanced, performance > > The objective of this feature is to track the moving average of system > power over the time period specified and switch to the subsequent mode. > > In order to do this, PMF driver will get the moving average of APU power > from PMFW and power threshold, time constants, system config parameters > from OEM inputs. > > System power as read by PMF driver from PMFW is the filtered value over > the sampling window. Every sampling window, moving average of system power > is computed. At the end of the monitoring window, the moving average is > compared against the threshold for mode switch for decision making. > > With AMD managing the system config limits, any mode switch within > auto-mode will result in limits of fPPT/sPPT/STAPM or STT being scaled > down. > > When "auto mode" is enabled, the static slider control remains out of > the PMF driver, so the platform_profile registration would not > happen in PMF driver. > > The transition to auto-mode only happens when the APMF fn5 is enabled > in BIOS, platform_profile set to "balanced" and a AMT > (Auto Mode transition) is received. > > Reviewed-by: Hans de Goede <hdegoede@redhat.com> > Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> > --- > drivers/platform/x86/amd/pmf/Makefile | 3 +- > drivers/platform/x86/amd/pmf/acpi.c | 5 + > drivers/platform/x86/amd/pmf/auto-mode.c | 263 +++++++++++++++++++++++ > drivers/platform/x86/amd/pmf/core.c | 9 + > drivers/platform/x86/amd/pmf/pmf.h | 113 ++++++++++ > 5 files changed, 392 insertions(+), 1 deletion(-) > create mode 100644 drivers/platform/x86/amd/pmf/auto-mode.c > > diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile > index 557521a80427..ef2b08c9174d 100644 > --- a/drivers/platform/x86/amd/pmf/Makefile > +++ b/drivers/platform/x86/amd/pmf/Makefile > @@ -5,4 +5,5 @@ > # > > obj-$(CONFIG_AMD_PMF) += amd-pmf.o > -amd-pmf-objs := core.o acpi.o sps.o > +amd-pmf-objs := core.o acpi.o sps.o \ > + auto-mode.o > diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c > index 1fc4d1400364..401fee381e90 100644 > --- a/drivers/platform/x86/amd/pmf/acpi.c > +++ b/drivers/platform/x86/amd/pmf/acpi.c > @@ -144,6 +144,11 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) > return err; > } > > +int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data) > +{ > + return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data)); > +} > + > static int apmf_if_verify_interface(struct amd_pmf_dev *pdev) > { > struct apmf_verify_interface output; > diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c > new file mode 100644 > index 000000000000..99f5a2396b0b > --- /dev/null > +++ b/drivers/platform/x86/amd/pmf/auto-mode.c > @@ -0,0 +1,263 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * AMD Platform Management Framework Driver > + * > + * Copyright (c) 2022, Advanced Micro Devices, Inc. > + * All Rights Reserved. > + * > + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> > + */ > + > +#include <linux/acpi.h> > +#include <linux/workqueue.h> > +#include "pmf.h" > + > +static struct auto_mode_mode_config config_store; > +static const char *state_as_str(unsigned int state); > + > +static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx, > + struct auto_mode_mode_config *table) > +{ > + struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control; > + > + amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL); > + amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL); > + amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL); > + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL); > + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL); > + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, > + pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL); > + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, > + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL); > + > + if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) > + apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual, > + config_store.mode_set[idx].fan_control.fan_id); > +} > + > +static int amd_pmf_get_moving_avg(struct amd_pmf_dev *pdev, int socket_power) > +{ > + int i, total = 0; > + > + if (pdev->socket_power_history_idx == -1) { > + for (i = 0; i < AVG_SAMPLE_SIZE; i++) > + pdev->socket_power_history[i] = socket_power; > + } > + > + pdev->socket_power_history_idx = (pdev->socket_power_history_idx + 1) % AVG_SAMPLE_SIZE; > + pdev->socket_power_history[pdev->socket_power_history_idx] = socket_power; > + > + for (i = 0; i < AVG_SAMPLE_SIZE; i++) > + total += pdev->socket_power_history[i]; > + > + return total / AVG_SAMPLE_SIZE; > +} > + > +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms) > +{ > + int avg_power = 0; > + bool update = false; > + int i, j; > + > + /* Get the average moving average computed by auto mode algorithm */ > + avg_power = amd_pmf_get_moving_avg(dev, socket_power); > + > + for (i = 0; i < AUTO_TRANSITION_MAX; i++) { > + if ((config_store.transition[i].shifting_up && avg_power >= > + config_store.transition[i].power_threshold) || > + (!config_store.transition[i].shifting_up && avg_power <= > + config_store.transition[i].power_threshold)) { > + if (config_store.transition[i].timer < > + config_store.transition[i].time_constant) > + config_store.transition[i].timer += time_elapsed_ms; > + } else { > + config_store.transition[i].timer = 0; > + } > + > + if (config_store.transition[i].timer >= > + config_store.transition[i].time_constant && > + !config_store.transition[i].applied) { > + config_store.transition[i].applied = true; > + update = true; > + } else if (config_store.transition[i].timer <= > + config_store.transition[i].time_constant && > + config_store.transition[i].applied) { > + config_store.transition[i].applied = false; > + update = true; > + } > + } > + > + dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power, > + state_as_str(config_store.current_mode)); > + > + if (update) { > + for (j = 0; j < AUTO_TRANSITION_MAX; j++) { > + /* Apply the mode with highest priority indentified */ > + if (config_store.transition[j].applied) { > + if (config_store.current_mode != > + config_store.transition[j].target_mode) { > + config_store.current_mode = > + config_store.transition[j].target_mode; > + dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n", > + state_as_str(config_store.current_mode)); > + amd_pmf_set_automode(dev, config_store.current_mode, NULL); > + } > + break; > + } > + } > + } > +} > + > +static void amd_pmf_get_power_threshold(void) > +{ > + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold = > + config_store.mode_set[AUTO_BALANCE].power_floor - > + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta; > + > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold = > + config_store.mode_set[AUTO_BALANCE].power_floor - > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta; > + > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold = > + config_store.mode_set[AUTO_QUIET].power_floor - > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta; > + > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold = > + config_store.mode_set[AUTO_PERFORMANCE].power_floor - > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta; > +} > + > +static const char *state_as_str(unsigned int state) > +{ > + switch (state) { > + case AUTO_QUIET: > + return "QUIET"; > + case AUTO_BALANCE: > + return "BALANCED"; > + case AUTO_PERFORMANCE_ON_LAP: > + return "ON_LAP"; > + case AUTO_PERFORMANCE: > + return "PERFORMANCE"; > + default: > + return "Unknown Auto Mode State"; > + } > +} > + > +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev) This function is only used in this file, so it can be static, I will fix this up while merging this. Regards, Hans > +{ > + struct apmf_auto_mode output; > + struct power_table_control *pwr_ctrl; > + int i; > + > + apmf_get_auto_mode_def(dev, &output); > + /* time constant */ > + config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant = > + output.balanced_to_quiet; > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant = > + output.balanced_to_perf; > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant = > + output.quiet_to_balanced; > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant = > + output.perf_to_balanced; > + > + /* power floor */ > + config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet; > + config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced; > + config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf; > + config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf; > + > + /* Power delta for mode change */ > + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta = > + output.pd_balanced_to_quiet; > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta = > + output.pd_balanced_to_perf; > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta = > + output.pd_quiet_to_balanced; > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta = > + output.pd_perf_to_balanced; > + > + /* Power threshold */ > + amd_pmf_get_power_threshold(); > + > + /* skin temperature limits */ > + pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control; > + pwr_ctrl->spl = output.spl_quiet; > + pwr_ctrl->sppt = output.sppt_quiet; > + pwr_ctrl->fppt = output.fppt_quiet; > + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet; > + pwr_ctrl->stt_min = output.stt_min_limit_quiet; > + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet; > + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet; > + > + pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control; > + pwr_ctrl->spl = output.spl_balanced; > + pwr_ctrl->sppt = output.sppt_balanced; > + pwr_ctrl->fppt = output.fppt_balanced; > + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced; > + pwr_ctrl->stt_min = output.stt_min_limit_balanced; > + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced; > + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced; > + > + pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control; > + pwr_ctrl->spl = output.spl_perf; > + pwr_ctrl->sppt = output.sppt_perf; > + pwr_ctrl->fppt = output.fppt_perf; > + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf; > + pwr_ctrl->stt_min = output.stt_min_limit_perf; > + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf; > + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf; > + > + pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control; > + pwr_ctrl->spl = output.spl_perf_on_lap; > + pwr_ctrl->sppt = output.sppt_perf_on_lap; > + pwr_ctrl->fppt = output.fppt_perf_on_lap; > + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap; > + pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap; > + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap; > + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap; > + > + /* Fan ID */ > + config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet; > + config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced; > + config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf; > + config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id = > + output.fan_id_perf; > + > + config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET; > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode = > + AUTO_PERFORMANCE; > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode = > + AUTO_BALANCE; > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode = > + AUTO_BALANCE; > + > + config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false; > + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true; > + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true; > + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up = > + false; > + > + for (i = 0 ; i < AUTO_MODE_MAX ; i++) { > + if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO) > + config_store.mode_set[i].fan_control.manual = false; > + else > + config_store.mode_set[i].fan_control.manual = true; > + } > + > + /* set to initial default values */ > + config_store.current_mode = AUTO_BALANCE; > + dev->socket_power_history_idx = -1; > +} > + > +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev) > +{ > + cancel_delayed_work_sync(&dev->work_buffer); > +} > + > +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev) > +{ > + amd_pmf_load_defaults_auto_mode(dev); > + /* update the thermal limits for Automode */ > + amd_pmf_set_automode(dev, config_store.current_mode, NULL); > + amd_pmf_init_metrics_table(dev); > +} > diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c > index 762f769bf7ee..42d803b49d97 100644 > --- a/drivers/platform/x86/amd/pmf/core.c > +++ b/drivers/platform/x86/amd/pmf/core.c > @@ -240,12 +240,21 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) > amd_pmf_init_sps(dev); > dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); > } > + > + /* Enable Auto Mode */ > + if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { > + amd_pmf_init_auto_mode(dev); > + dev_dbg(dev->dev, "Auto Mode Init done\n"); > + } > } > > static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) > { > if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) > amd_pmf_deinit_sps(dev); > + > + if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) > + amd_pmf_deinit_auto_mode(dev); > } > > static const struct acpi_device_id amd_pmf_acpi_ids[] = { > diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h > index 42e4a5f512c0..ce092a529261 100644 > --- a/drivers/platform/x86/amd/pmf/pmf.h > +++ b/drivers/platform/x86/amd/pmf/pmf.h > @@ -18,6 +18,7 @@ > #define APMF_FUNC_VERIFY_INTERFACE 0 > #define APMF_FUNC_GET_SYS_PARAMS 1 > #define APMF_FUNC_SBIOS_HEARTBEAT 4 > +#define APMF_FUNC_AUTO_MODE 5 > #define APMF_FUNC_SET_FAN_IDX 7 > #define APMF_FUNC_STATIC_SLIDER_GRANULAR 9 > > @@ -44,6 +45,7 @@ > #define FAN_INDEX_AUTO 0xFFFFFFFF > > #define ARG_NONE 0 > +#define AVG_SAMPLE_SIZE 3 > > /* AMD PMF BIOS interfaces */ > struct apmf_verify_interface { > @@ -143,6 +145,8 @@ struct amd_pmf_dev { > struct smu_pmf_metrics m_table; > struct delayed_work work_buffer; > ktime_t start_time; > + int socket_power_history[AVG_SAMPLE_SIZE]; > + int socket_power_history_idx; > }; > > struct apmf_sps_prop_granular { > @@ -171,6 +175,107 @@ struct fan_table_control { > unsigned long fan_id; > }; > > +struct power_table_control { > + u32 spl; > + u32 sppt; > + u32 fppt; > + u32 sppt_apu_only; > + u32 stt_min; > + u32 stt_skin_temp[STT_TEMP_COUNT]; > + u32 reserved[16]; > +}; > + > +/* Auto Mode Layer */ > +enum auto_mode_transition_priority { > + AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */ > + AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */ > + AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */ > + AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */ > + AUTO_TRANSITION_MAX, > +}; > + > +enum auto_mode_mode { > + AUTO_QUIET, > + AUTO_BALANCE, > + AUTO_PERFORMANCE_ON_LAP, > + AUTO_PERFORMANCE, > + AUTO_MODE_MAX, > +}; > + > +struct auto_mode_trans_params { > + u32 time_constant; /* minimum time required to switch to next mode */ > + u32 power_delta; /* delta power to shift mode */ > + u32 power_threshold; > + u32 timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */ > + u32 applied; > + enum auto_mode_mode target_mode; > + u32 shifting_up; > +}; > + > +struct auto_mode_mode_settings { > + struct power_table_control power_control; > + struct fan_table_control fan_control; > + u32 power_floor; > +}; > + > +struct auto_mode_mode_config { > + struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX]; > + struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX]; > + enum auto_mode_mode current_mode; > +}; > + > +struct apmf_auto_mode { > + u16 size; > + /* time constant */ > + u32 balanced_to_perf; > + u32 perf_to_balanced; > + u32 quiet_to_balanced; > + u32 balanced_to_quiet; > + /* power floor */ > + u32 pfloor_perf; > + u32 pfloor_balanced; > + u32 pfloor_quiet; > + /* Power delta for mode change */ > + u32 pd_balanced_to_perf; > + u32 pd_perf_to_balanced; > + u32 pd_quiet_to_balanced; > + u32 pd_balanced_to_quiet; > + /* skin temperature limits */ > + u8 stt_apu_perf_on_lap; /* CQL ON */ > + u8 stt_hs2_perf_on_lap; /* CQL ON */ > + u8 stt_apu_perf; > + u8 stt_hs2_perf; > + u8 stt_apu_balanced; > + u8 stt_hs2_balanced; > + u8 stt_apu_quiet; > + u8 stt_hs2_quiet; > + u32 stt_min_limit_perf_on_lap; /* CQL ON */ > + u32 stt_min_limit_perf; > + u32 stt_min_limit_balanced; > + u32 stt_min_limit_quiet; > + /* SPL based */ > + u32 fppt_perf_on_lap; /* CQL ON */ > + u32 sppt_perf_on_lap; /* CQL ON */ > + u32 spl_perf_on_lap; /* CQL ON */ > + u32 sppt_apu_only_perf_on_lap; /* CQL ON */ > + u32 fppt_perf; > + u32 sppt_perf; > + u32 spl_perf; > + u32 sppt_apu_only_perf; > + u32 fppt_balanced; > + u32 sppt_balanced; > + u32 spl_balanced; > + u32 sppt_apu_only_balanced; > + u32 fppt_quiet; > + u32 sppt_quiet; > + u32 spl_quiet; > + u32 sppt_apu_only_quiet; > + /* Fan ID */ > + u32 fan_id_perf; > + u32 fan_id_balanced; > + u32 fan_id_quiet; > +} __packed; > + > /* Core Layer */ > int apmf_acpi_init(struct amd_pmf_dev *pmf_dev); > void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev); > @@ -191,4 +296,12 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev); > > > int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); > + > +/* Auto Mode Layer */ > +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev); > +int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data); > +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev); > +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev); > +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms); > + > #endif /* PMF_H */
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index 557521a80427..ef2b08c9174d 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -5,4 +5,5 @@ # obj-$(CONFIG_AMD_PMF) += amd-pmf.o -amd-pmf-objs := core.o acpi.o sps.o +amd-pmf-objs := core.o acpi.o sps.o \ + auto-mode.o diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1fc4d1400364..401fee381e90 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -144,6 +144,11 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) return err; } +int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data) +{ + return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data)); +} + static int apmf_if_verify_interface(struct amd_pmf_dev *pdev) { struct apmf_verify_interface output; diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c new file mode 100644 index 000000000000..99f5a2396b0b --- /dev/null +++ b/drivers/platform/x86/amd/pmf/auto-mode.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Platform Management Framework Driver + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + */ + +#include <linux/acpi.h> +#include <linux/workqueue.h> +#include "pmf.h" + +static struct auto_mode_mode_config config_store; +static const char *state_as_str(unsigned int state); + +static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx, + struct auto_mode_mode_config *table) +{ + struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control; + + amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL); + amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL); + amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL); + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL); + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL); + + if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) + apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual, + config_store.mode_set[idx].fan_control.fan_id); +} + +static int amd_pmf_get_moving_avg(struct amd_pmf_dev *pdev, int socket_power) +{ + int i, total = 0; + + if (pdev->socket_power_history_idx == -1) { + for (i = 0; i < AVG_SAMPLE_SIZE; i++) + pdev->socket_power_history[i] = socket_power; + } + + pdev->socket_power_history_idx = (pdev->socket_power_history_idx + 1) % AVG_SAMPLE_SIZE; + pdev->socket_power_history[pdev->socket_power_history_idx] = socket_power; + + for (i = 0; i < AVG_SAMPLE_SIZE; i++) + total += pdev->socket_power_history[i]; + + return total / AVG_SAMPLE_SIZE; +} + +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms) +{ + int avg_power = 0; + bool update = false; + int i, j; + + /* Get the average moving average computed by auto mode algorithm */ + avg_power = amd_pmf_get_moving_avg(dev, socket_power); + + for (i = 0; i < AUTO_TRANSITION_MAX; i++) { + if ((config_store.transition[i].shifting_up && avg_power >= + config_store.transition[i].power_threshold) || + (!config_store.transition[i].shifting_up && avg_power <= + config_store.transition[i].power_threshold)) { + if (config_store.transition[i].timer < + config_store.transition[i].time_constant) + config_store.transition[i].timer += time_elapsed_ms; + } else { + config_store.transition[i].timer = 0; + } + + if (config_store.transition[i].timer >= + config_store.transition[i].time_constant && + !config_store.transition[i].applied) { + config_store.transition[i].applied = true; + update = true; + } else if (config_store.transition[i].timer <= + config_store.transition[i].time_constant && + config_store.transition[i].applied) { + config_store.transition[i].applied = false; + update = true; + } + } + + dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power, + state_as_str(config_store.current_mode)); + + if (update) { + for (j = 0; j < AUTO_TRANSITION_MAX; j++) { + /* Apply the mode with highest priority indentified */ + if (config_store.transition[j].applied) { + if (config_store.current_mode != + config_store.transition[j].target_mode) { + config_store.current_mode = + config_store.transition[j].target_mode; + dev_dbg(dev->dev, "[AUTO_MODE] moving to mode:%s\n", + state_as_str(config_store.current_mode)); + amd_pmf_set_automode(dev, config_store.current_mode, NULL); + } + break; + } + } + } +} + +static void amd_pmf_get_power_threshold(void) +{ + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold = + config_store.mode_set[AUTO_BALANCE].power_floor - + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta; + + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold = + config_store.mode_set[AUTO_BALANCE].power_floor - + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta; + + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_threshold = + config_store.mode_set[AUTO_QUIET].power_floor - + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta; + + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold = + config_store.mode_set[AUTO_PERFORMANCE].power_floor - + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta; +} + +static const char *state_as_str(unsigned int state) +{ + switch (state) { + case AUTO_QUIET: + return "QUIET"; + case AUTO_BALANCE: + return "BALANCED"; + case AUTO_PERFORMANCE_ON_LAP: + return "ON_LAP"; + case AUTO_PERFORMANCE: + return "PERFORMANCE"; + default: + return "Unknown Auto Mode State"; + } +} + +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev) +{ + struct apmf_auto_mode output; + struct power_table_control *pwr_ctrl; + int i; + + apmf_get_auto_mode_def(dev, &output); + /* time constant */ + config_store.transition[AUTO_TRANSITION_TO_QUIET].time_constant = + output.balanced_to_quiet; + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant = + output.balanced_to_perf; + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant = + output.quiet_to_balanced; + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant = + output.perf_to_balanced; + + /* power floor */ + config_store.mode_set[AUTO_QUIET].power_floor = output.pfloor_quiet; + config_store.mode_set[AUTO_BALANCE].power_floor = output.pfloor_balanced; + config_store.mode_set[AUTO_PERFORMANCE].power_floor = output.pfloor_perf; + config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_floor = output.pfloor_perf; + + /* Power delta for mode change */ + config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta = + output.pd_balanced_to_quiet; + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta = + output.pd_balanced_to_perf; + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta = + output.pd_quiet_to_balanced; + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta = + output.pd_perf_to_balanced; + + /* Power threshold */ + amd_pmf_get_power_threshold(); + + /* skin temperature limits */ + pwr_ctrl = &config_store.mode_set[AUTO_QUIET].power_control; + pwr_ctrl->spl = output.spl_quiet; + pwr_ctrl->sppt = output.sppt_quiet; + pwr_ctrl->fppt = output.fppt_quiet; + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_quiet; + pwr_ctrl->stt_min = output.stt_min_limit_quiet; + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_quiet; + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_quiet; + + pwr_ctrl = &config_store.mode_set[AUTO_BALANCE].power_control; + pwr_ctrl->spl = output.spl_balanced; + pwr_ctrl->sppt = output.sppt_balanced; + pwr_ctrl->fppt = output.fppt_balanced; + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_balanced; + pwr_ctrl->stt_min = output.stt_min_limit_balanced; + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_balanced; + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_balanced; + + pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE].power_control; + pwr_ctrl->spl = output.spl_perf; + pwr_ctrl->sppt = output.sppt_perf; + pwr_ctrl->fppt = output.fppt_perf; + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf; + pwr_ctrl->stt_min = output.stt_min_limit_perf; + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf; + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf; + + pwr_ctrl = &config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].power_control; + pwr_ctrl->spl = output.spl_perf_on_lap; + pwr_ctrl->sppt = output.sppt_perf_on_lap; + pwr_ctrl->fppt = output.fppt_perf_on_lap; + pwr_ctrl->sppt_apu_only = output.sppt_apu_only_perf_on_lap; + pwr_ctrl->stt_min = output.stt_min_limit_perf_on_lap; + pwr_ctrl->stt_skin_temp[STT_TEMP_APU] = output.stt_apu_perf_on_lap; + pwr_ctrl->stt_skin_temp[STT_TEMP_HS2] = output.stt_hs2_perf_on_lap; + + /* Fan ID */ + config_store.mode_set[AUTO_QUIET].fan_control.fan_id = output.fan_id_quiet; + config_store.mode_set[AUTO_BALANCE].fan_control.fan_id = output.fan_id_balanced; + config_store.mode_set[AUTO_PERFORMANCE].fan_control.fan_id = output.fan_id_perf; + config_store.mode_set[AUTO_PERFORMANCE_ON_LAP].fan_control.fan_id = + output.fan_id_perf; + + config_store.transition[AUTO_TRANSITION_TO_QUIET].target_mode = AUTO_QUIET; + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].target_mode = + AUTO_PERFORMANCE; + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].target_mode = + AUTO_BALANCE; + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].target_mode = + AUTO_BALANCE; + + config_store.transition[AUTO_TRANSITION_TO_QUIET].shifting_up = false; + config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].shifting_up = true; + config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].shifting_up = true; + config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].shifting_up = + false; + + for (i = 0 ; i < AUTO_MODE_MAX ; i++) { + if (config_store.mode_set[i].fan_control.fan_id == FAN_INDEX_AUTO) + config_store.mode_set[i].fan_control.manual = false; + else + config_store.mode_set[i].fan_control.manual = true; + } + + /* set to initial default values */ + config_store.current_mode = AUTO_BALANCE; + dev->socket_power_history_idx = -1; +} + +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev) +{ + cancel_delayed_work_sync(&dev->work_buffer); +} + +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev) +{ + amd_pmf_load_defaults_auto_mode(dev); + /* update the thermal limits for Automode */ + amd_pmf_set_automode(dev, config_store.current_mode, NULL); + amd_pmf_init_metrics_table(dev); +} diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 762f769bf7ee..42d803b49d97 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -240,12 +240,21 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) amd_pmf_init_sps(dev); dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); } + + /* Enable Auto Mode */ + if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { + amd_pmf_init_auto_mode(dev); + dev_dbg(dev->dev, "Auto Mode Init done\n"); + } } static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) { if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) amd_pmf_deinit_sps(dev); + + if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) + amd_pmf_deinit_auto_mode(dev); } static const struct acpi_device_id amd_pmf_acpi_ids[] = { diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 42e4a5f512c0..ce092a529261 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -18,6 +18,7 @@ #define APMF_FUNC_VERIFY_INTERFACE 0 #define APMF_FUNC_GET_SYS_PARAMS 1 #define APMF_FUNC_SBIOS_HEARTBEAT 4 +#define APMF_FUNC_AUTO_MODE 5 #define APMF_FUNC_SET_FAN_IDX 7 #define APMF_FUNC_STATIC_SLIDER_GRANULAR 9 @@ -44,6 +45,7 @@ #define FAN_INDEX_AUTO 0xFFFFFFFF #define ARG_NONE 0 +#define AVG_SAMPLE_SIZE 3 /* AMD PMF BIOS interfaces */ struct apmf_verify_interface { @@ -143,6 +145,8 @@ struct amd_pmf_dev { struct smu_pmf_metrics m_table; struct delayed_work work_buffer; ktime_t start_time; + int socket_power_history[AVG_SAMPLE_SIZE]; + int socket_power_history_idx; }; struct apmf_sps_prop_granular { @@ -171,6 +175,107 @@ struct fan_table_control { unsigned long fan_id; }; +struct power_table_control { + u32 spl; + u32 sppt; + u32 fppt; + u32 sppt_apu_only; + u32 stt_min; + u32 stt_skin_temp[STT_TEMP_COUNT]; + u32 reserved[16]; +}; + +/* Auto Mode Layer */ +enum auto_mode_transition_priority { + AUTO_TRANSITION_TO_PERFORMANCE, /* Any other mode to Performance Mode */ + AUTO_TRANSITION_FROM_QUIET_TO_BALANCE, /* Quiet Mode to Balance Mode */ + AUTO_TRANSITION_TO_QUIET, /* Any other mode to Quiet Mode */ + AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE, /* Performance Mode to Balance Mode */ + AUTO_TRANSITION_MAX, +}; + +enum auto_mode_mode { + AUTO_QUIET, + AUTO_BALANCE, + AUTO_PERFORMANCE_ON_LAP, + AUTO_PERFORMANCE, + AUTO_MODE_MAX, +}; + +struct auto_mode_trans_params { + u32 time_constant; /* minimum time required to switch to next mode */ + u32 power_delta; /* delta power to shift mode */ + u32 power_threshold; + u32 timer; /* elapsed time. if timer > TimeThreshold, it will move to next mode */ + u32 applied; + enum auto_mode_mode target_mode; + u32 shifting_up; +}; + +struct auto_mode_mode_settings { + struct power_table_control power_control; + struct fan_table_control fan_control; + u32 power_floor; +}; + +struct auto_mode_mode_config { + struct auto_mode_trans_params transition[AUTO_TRANSITION_MAX]; + struct auto_mode_mode_settings mode_set[AUTO_MODE_MAX]; + enum auto_mode_mode current_mode; +}; + +struct apmf_auto_mode { + u16 size; + /* time constant */ + u32 balanced_to_perf; + u32 perf_to_balanced; + u32 quiet_to_balanced; + u32 balanced_to_quiet; + /* power floor */ + u32 pfloor_perf; + u32 pfloor_balanced; + u32 pfloor_quiet; + /* Power delta for mode change */ + u32 pd_balanced_to_perf; + u32 pd_perf_to_balanced; + u32 pd_quiet_to_balanced; + u32 pd_balanced_to_quiet; + /* skin temperature limits */ + u8 stt_apu_perf_on_lap; /* CQL ON */ + u8 stt_hs2_perf_on_lap; /* CQL ON */ + u8 stt_apu_perf; + u8 stt_hs2_perf; + u8 stt_apu_balanced; + u8 stt_hs2_balanced; + u8 stt_apu_quiet; + u8 stt_hs2_quiet; + u32 stt_min_limit_perf_on_lap; /* CQL ON */ + u32 stt_min_limit_perf; + u32 stt_min_limit_balanced; + u32 stt_min_limit_quiet; + /* SPL based */ + u32 fppt_perf_on_lap; /* CQL ON */ + u32 sppt_perf_on_lap; /* CQL ON */ + u32 spl_perf_on_lap; /* CQL ON */ + u32 sppt_apu_only_perf_on_lap; /* CQL ON */ + u32 fppt_perf; + u32 sppt_perf; + u32 spl_perf; + u32 sppt_apu_only_perf; + u32 fppt_balanced; + u32 sppt_balanced; + u32 spl_balanced; + u32 sppt_apu_only_balanced; + u32 fppt_quiet; + u32 sppt_quiet; + u32 spl_quiet; + u32 sppt_apu_only_quiet; + /* Fan ID */ + u32 fan_id_perf; + u32 fan_id_balanced; + u32 fan_id_quiet; +} __packed; + /* Core Layer */ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev); void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev); @@ -191,4 +296,12 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev); int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); + +/* Auto Mode Layer */ +void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev); +int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data); +void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev); +void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev); +void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms); + #endif /* PMF_H */