diff mbox series

[v4,08/11] platform/x86/amd/pmf: Add support for Auto mode feature

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

Commit Message

Shyam Sundar S K Aug. 2, 2022, 3:11 p.m. UTC
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

Comments

Hans de Goede Aug. 5, 2022, 10:52 a.m. UTC | #1
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 mbox series

Patch

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 */