drivers/platform/x86/amd/pmf/auto-mode.c

Source file repositories/reference/linux-study-clean/drivers/platform/x86/amd/pmf/auto-mode.c

File Facts

System
Linux kernel
Corpus path
drivers/platform/x86/amd/pmf/auto-mode.c
Extension
.c
Size
17558 bytes
Lines
442
Domain
Driver Families
Bucket
drivers/platform
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static void amd_pmf_dump_auto_mode_defaults(struct auto_mode_mode_config *data) {}
#endif

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, SET_CMD, pwr_ctrl->spl, NULL);
	amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, pwr_ctrl->fppt, NULL);
	amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, pwr_ctrl->sppt, NULL);
	amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, pwr_ctrl->sppt_apu_only, NULL);
	amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, pwr_ctrl->stt_min, NULL);
	amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
			 fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL);
	amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
			 fixp_q88_fromint(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;
		}

#ifdef CONFIG_AMD_PMF_DEBUG
		dev_dbg(dev->dev, "[AUTO MODE] average_power : %d mW mode: %s\n", avg_power,
			state_as_str(config_store.current_mode));

		dev_dbg(dev->dev, "[AUTO MODE] time: %lld ms timer: %u ms tc: %u ms\n",
			time_elapsed_ms, config_store.transition[i].timer,
			config_store.transition[i].time_constant);

		dev_dbg(dev->dev, "[AUTO MODE] shiftup: %u pt: %u mW pf: %u mW pd: %u mW\n",
			config_store.transition[i].shifting_up,
			config_store.transition[i].power_threshold,
			config_store.mode_set[i].power_floor,
			config_store.transition[i].power_delta);
#endif
	}

Annotation

Implementation Notes