Re: [PATCH v1 12/15] platform/x86/amd/pmf: Add support for Auto mode feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Shyam,

On 7/28/22 14:57, Shyam Sundar S K wrote:
> Hi Hans,
> 
> On 7/28/2022 2:52 AM, Hans de Goede wrote:
>> Hi,
>>
>> On 7/12/22 16:58, Shyam Sundar S K wrote:
>>> 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.
>>>
>>> This feature has 3 modes: quiet, balanced, performance.
>>>
>>> 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.
>>
>> This is not what happens in the code, the platform_profile registration
>> is still happening AFAICT.
> 
> platform_profile_register gets called in amd_pmf_init_sps() and it gets
> triggered only when APMF_FUNC_STATIC_SLIDER_GRANULAR (fn9) is enabled in
> the BIOS.
> 
> OEMs have been already told in the BIOS implementation guide that, if
> the "Auto Mode" is enabled, the "Static platform_profile" registration
> shall happen at the OEM driver side. In this case, it will be
> thinkpad_acpi which shall do the "platform_profile_register".

I see. IMHO it would still be good to check that we never end up
with both the static-slider + AMT support enabled at the same time.

It is better to catch this; ignore the static-slider support
(clear the feature flag I guess) and log an error then to get surprised
by firmware bugs and having to figure out what is going on when
a user reports some weird bug.

> 
>>
>> Like with CnQF for this other auto mode we also need to figure out the
>> relation with the regular static platform_profile settings.
>>
>> I see this in the code:
>>
>> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>  	/* Calculate the avg SoC power consumption */
>>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>  
>> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
>> +		/* Apply the Auto Mode transition */
>> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>> +	}
>> +
>>
>> So it seems that unlike CnQF this other auto-mode only does mode transitions
>> when the static-slider is set to balanced ?
> 
> CnQF and Auto-mode are complimentary to eachother. The key difference
> between them are:
> 
> 1. CnQF will have 4 internal modes (low-power, balanced, performance,
> turbo) and Auto-mode will not have the turbo mode.
> 
> 2. CnQF will be enabled only when the APCI call to the BIOS function
> returns a spl flag which gets set from the BIOS side and driver sees it.
> That is when the driver activates the CnQF. Irrespective of the
> platform_profile choice selection, if the CnQF is enabled the values
> passed in the BIOS tables for static slider are ignored.

Right, this is rather "rude" though, to ignore a clear user preference.

We really need to think of the userspace API results here. Arguably
when CnQF gets enabled the platform_profile handler should just be
unregistered to indicate that there is no platform_profile support.

IMHO that would be better then still advertising platform_profile
support and not have it working.

Also platform_profile support is also used to implement a low-battery
mode where userspace automatically switches the profile to low-power
when the battery is below say 20%. How is such a power-saver mode
supposed to be implemented with CnQF ?

> 3. Auto-mode will get activated only when an AMT ON event (which will be
> sent by thinkpad_acpi driver in this case) is received and the
> platform_profile is set to balanced. In other modes it keeps turned off.

Right, note that as discussed elsewhere in the thread the decision to
only enable AMT when platform_profile == balanced is a policy coded
inside the thinkpad_acpi code and IMHO this policy decision should not
be duplicated in the amd-pmf code. Instead it should just enable/disable
AMT soleley on the BIOS AMT enable/disable setting/events.

> 4. The sampling window, the threshold calculation mechanism and the
> power floor measurements are different w.r.t to CnQF and auto-mode.
> 
> 5. OEMs are free to choose what kind of optimizations they are looking
> for by enabling one or more of such features within PMF.
> 
> Hope it clarifies :-)

It helps a bit, but there are still questions. Off-list Mario suggested
to maybe just drop CnQF from the patch-set (for now) and first focus on
getting the amd-pmf code to work in the thinkpad case. And then once
that is solved re-introduce the CnQF patches/code. I think that might
be a good idea to move this forward a bit faster. Especially since
the CnQF story involves some userspace API issues (as mentioned) and
I would like to make sure we get the userspace API issues cleared up
before merging the CnQF bits.

>>> 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.
>>
>> Right, this part of the commit msg matches the code, but it conflicts with
>> the "When "auto mode" is enabled, the static slider control remains out of
>> the PMF driver:" part of the commit msg above.

Note this bit of the commit message needs to be claried that the mentioned
platform_profile setting is coming from another driver and not form
amd-pmf. Or better yet as mentioned just only check the BIOS AMT flag
and then this bit of the commit msg can simply be dropped.

Regards,

Hans



>>
>>>
>>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@xxxxxxx>
>>> ---
>>>  drivers/platform/x86/amd/pmf/Makefile    |   2 +-
>>>  drivers/platform/x86/amd/pmf/acpi.c      |  28 ++
>>>  drivers/platform/x86/amd/pmf/auto-mode.c | 317 +++++++++++++++++++++++
>>>  drivers/platform/x86/amd/pmf/core.c      |  25 +-
>>>  drivers/platform/x86/amd/pmf/pmf.h       | 104 ++++++++
>>>  5 files changed, 473 insertions(+), 3 deletions(-)
>>>  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 d02a0bdc6429..2a9568bf9064 100644
>>> --- a/drivers/platform/x86/amd/pmf/Makefile
>>> +++ b/drivers/platform/x86/amd/pmf/Makefile
>>> @@ -6,4 +6,4 @@
>>>  
>>>  obj-$(CONFIG_AMD_PMF) += amd-pmf.o
>>>  amd-pmf-objs := core.o acpi.o sps.o \
>>> -		cnqf.o
>>> +		cnqf.o auto-mode.o
>>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>>> index a3ff91c605b5..e9f33e61659f 100644
>>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>>> @@ -55,6 +55,7 @@ static void apmf_if_parse_functions(struct apmf_if_functions *func, u32 mask)
>>>  {
>>>  	func->system_params = mask & APMF_FUNC_GET_SYS_PARAMS;
>>>  	func->static_slider_granular = mask & APMF_FUNC_STATIC_SLIDER_GRANULAR;
>>> +	func->auto_mode_def = mask & APMF_FUNC_AUTO_MODE;
>>>  	func->fan_table_idx = mask & APMF_FUNC_SET_FAN_IDX;
>>>  	func->dyn_slider_ac = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_AC;
>>>  	func->dyn_slider_dc = mask & APMF_FUNC_DYN_SLIDER_GRANULAR_DC;
>>> @@ -210,6 +211,33 @@ int apmf_update_fan_idx(struct apmf_if *ampf_if, bool manual, u32 idx)
>>>  	return err;
>>>  }
>>>  
>>> +int apmf_get_auto_mode_def(struct apmf_if *ampf_if, struct apmf_auto_mode *data)
>>> +{
>>> +	union acpi_object *info;
>>> +	size_t size;
>>> +	int err = 0;
>>> +
>>> +	info = apmf_if_call(ampf_if, APMF_FUNC_AUTO_MODE, NULL);
>>> +	if (!info)
>>> +		return -EIO;
>>> +
>>> +	size = *(u16 *)info->buffer.pointer;
>>> +
>>> +	if (size < sizeof(*data)) {
>>> +		pr_debug("buffer too small %zu\n", size);
>>> +		err = -EINVAL;
>>> +		goto out;
>>> +	}
>>> +
>>> +	size = min(sizeof(*data), size);
>>> +	memset(data, 0, sizeof(*data));
>>> +	memcpy(data, info->buffer.pointer, size);
>>> +
>>> +out:
>>> +	kfree(info);
>>> +	return err;
>>> +}
>>> +
>>
>> Please use apmf_if_call_store_buffer() for this (see review of 4/15)
>>
>> Regards,
>>
>> Hans
>>
>>
>>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data)
>>>  {
>>>  	union acpi_object *info;
>>> 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..954fde25e71e
>>> --- /dev/null
>>> +++ b/drivers/platform/x86/amd/pmf/auto-mode.c
>>> @@ -0,0 +1,317 @@
>>> +// 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@xxxxxxx>
>>> + */
>>> +
>>> +#include <linux/acpi.h>
>>> +#include <linux/list.h>
>>> +#include <linux/workqueue.h>
>>> +#include "pmf.h"
>>> +
>>> +#define AVG_SAMPLE_SIZE 3
>>> +
>>> +struct power_history {
>>> +	int socket_power;
>>> +	struct list_head list;
>>> +	int avg;
>>> +	int total;
>>> +};
>>> +
>>> +struct list_pdata {
>>> +	int total;
>>> +	int avg;
>>> +};
>>> +
>>> +static struct power_history power_list;
>>> +static struct list_pdata pdata;
>>> +
>>> +static struct auto_mode_mode_config config_store;
>>> +static const char *state_as_str(unsigned int state);
>>> +
>>> +static void amd_pmf_handle_automode(struct amd_pmf_dev *dev, bool op, int idx,
>>> +				    struct auto_mode_mode_config *table)
>>> +{
>>> +	if (op == SLIDER_OP_SET) {
>>> +		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);
>>> +	} else if (op == SLIDER_OP_GET) {
>>> +		amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.spl);
>>> +		amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.fppt);
>>> +		amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.sppt);
>>> +		amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.sppt_apu_only);
>>> +		amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_min);
>>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_APU]);
>>> +		amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
>>> +				 &table->mode_set[idx].power_control.stt_skin_temp[STT_TEMP_HS2]);
>>> +	}
>>> +
>>> +	if (dev->apmf_if->func.fan_table_idx)
>>> +		apmf_update_fan_idx(dev->apmf_if, config_store.mode_set[idx].fan_control.manual,
>>> +				    config_store.mode_set[idx].fan_control.fan_id);
>>> +}
>>> +
>>> +static int amd_pmf_get_moving_avg(int socket_power)
>>> +{
>>> +	struct power_history *tmp;
>>> +	struct list_head *pos, *q;
>>> +	static int count;
>>> +
>>> +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
>>> +	tmp->socket_power = socket_power;
>>> +	list_add_tail(&tmp->list, &power_list.list);
>>> +
>>> +	list_for_each_safe(pos, q, &power_list.list) {
>>> +		if (count >= AVG_SAMPLE_SIZE) {
>>> +			tmp = list_first_entry(pos, struct power_history, list);
>>> +			list_del_init(pos);
>>> +			goto next;
>>> +		}
>>> +	}
>>> +
>>> +next:
>>> +	pdata.total = 0;
>>> +	pdata.avg = 0;
>>> +
>>> +	list_for_each(pos, &power_list.list) {
>>> +		tmp = list_entry(pos, struct power_history, list);
>>> +		pdata.total += tmp->socket_power;
>>> +		pdata.avg = pdata.total / AVG_SAMPLE_SIZE;
>>> +	}
>>> +
>>> +	count++;
>>> +	if (count >= AVG_SAMPLE_SIZE)
>>> +		return pdata.avg;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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(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_handle_automode(dev, SLIDER_OP_SET,
>>> +								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;
>>> +
>>> +	if (dev->apmf_if->func.auto_mode_def) {
>>> +		apmf_get_auto_mode_def(dev->apmf_if, &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;
>>> +		}
>>> +	}
>>> +}
>>> +
>>> +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)
>>> +{
>>> +	INIT_LIST_HEAD(&power_list.list);
>>> +
>>> +	amd_pmf_init_metrics_table(dev);
>>> +	amd_pmf_load_defaults_auto_mode(dev);
>>> +
>>> +	/* update the thermal for Automode */
>>> +	amd_pmf_handle_automode(dev, SLIDER_OP_SET, config_store.current_mode, NULL);
>>> +}
>>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>>> index bc267d333b76..674ddf599135 100644
>>> --- a/drivers/platform/x86/amd/pmf/core.c
>>> +++ b/drivers/platform/x86/amd/pmf/core.c
>>> @@ -122,6 +122,11 @@ static void amd_pmf_get_metrics(struct work_struct *work)
>>>  	/* Calculate the avg SoC power consumption */
>>>  	socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
>>>  
>>> +	if (current_profile == PLATFORM_PROFILE_BALANCED) {
>>> +		/* Apply the Auto Mode transition */
>>> +		amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
>>> +	}
>>> +
>>>  	if (dev->cnqf_feat) {
>>>  		/* Apply the CnQF transition */
>>>  		amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
>>> @@ -260,9 +265,18 @@ 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 Cool n Quiet Framework (CnQF) */
>>> -	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>> +
>>> +	/* Enable Auto Mode */
>>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>> +		amd_pmf_init_auto_mode(dev);
>>> +		dev_dbg(dev->dev, "Auto Mode Init done\n");
>>> +		/*
>>> +		 * Auto mode and CnQF cannot co-exist. If auto mode is supported it takes
>>> +		 * higher priority over CnQF.
>>> +		 */
>>> +	} else if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC)) {
>>> +		/* Enable Cool n Quiet Framework (CnQF) */
>>>  		amd_pmf_init_cnqf(dev);
>>>  		dev_dbg(dev->dev, "CnQF Init done\n");
>>>  	}
>>> @@ -272,6 +286,13 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
>>>  {
>>>  	if (is_apmf_func_supported(APMF_FUNC_STATIC_SLIDER_GRANULAR))
>>>  		amd_pmf_deinit_sps(dev);
>>> +
>>> +	if (is_apmf_func_supported(APMF_FUNC_AUTO_MODE)) {
>>> +		amd_pmf_deinit_auto_mode(dev);
>>> +		/* If auto mode is supported, there is no need to proceed */
>>> +		return;
>>> +	}
>>> +
>>>  	if (is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_AC) ||
>>>  	    is_apmf_func_supported(APMF_FUNC_DYN_SLIDER_GRANULAR_DC))
>>>  		amd_pmf_deinit_cnqf(dev);
>>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>>> index 452266809dfa..0532f49e9613 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
>>>  #define APMF_FUNC_DYN_SLIDER_GRANULAR_AC	11
>>> @@ -51,6 +52,7 @@
>>>  struct apmf_if_functions {
>>>  	bool system_params;
>>>  	bool sbios_heartbeat;
>>> +	bool auto_mode_def;
>>>  	bool fan_table_idx;
>>>  	bool static_slider_granular;
>>>  	bool dyn_slider_ac;
>>> @@ -197,6 +199,33 @@ struct fan_table_control {
>>>  	unsigned long fan_id;
>>>  };
>>>  
>>> +/* 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 power_table_control {
>>>  	u32 spl;
>>>  	u32 sppt;
>>> @@ -207,6 +236,74 @@ struct power_table_control {
>>>  	u32 reserved[16];
>>>  };
>>>  
>>> +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;
>>> +	bool on_lap;
>>> +	bool better_perf;
>>> +	u32 amt_enabled; /* Auto Mode Transition */
>>> +	u32 avg_power;
>>> +};
>>> +
>>> +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;
>>> +
>>>  /* CnQF Layer */
>>>  enum cnqf_trans_priority {
>>>  	CNQF_TRANSITION_TO_TURBO, /* Any other mode to Turbo Mode */
>>> @@ -317,6 +414,13 @@ void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev);
>>>  
>>>  int apmf_update_fan_idx(struct apmf_if *ampf_if, 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 apmf_if *ampf_if, 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_lapsed_ms);
>>> +
>>>  /* CnQF Layer */
>>>  int apmf_get_dyn_slider_def_ac(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>>  int apmf_get_dyn_slider_def_dc(struct apmf_if *ampf_if, struct apmf_dyn_slider_output *data);
>>
> 




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux