18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * intel_powerclamp.c - package c-state idle injection
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012, Intel Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:
88c2ecf20Sopenharmony_ci *     Arjan van de Ven <arjan@linux.intel.com>
98c2ecf20Sopenharmony_ci *     Jacob Pan <jacob.jun.pan@linux.intel.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	TODO:
128c2ecf20Sopenharmony_ci *           1. better handle wakeup from external interrupts, currently a fixed
138c2ecf20Sopenharmony_ci *              compensation is added to clamping duration when excessive amount
148c2ecf20Sopenharmony_ci *              of wakeups are observed during idle time. the reason is that in
158c2ecf20Sopenharmony_ci *              case of external interrupts without need for ack, clamping down
168c2ecf20Sopenharmony_ci *              cpu in non-irq context does not reduce irq. for majority of the
178c2ecf20Sopenharmony_ci *              cases, clamping down cpu does help reduce irq as well, we should
188c2ecf20Sopenharmony_ci *              be able to differentiate the two cases and give a quantitative
198c2ecf20Sopenharmony_ci *              solution for the irqs that we can control. perhaps based on
208c2ecf20Sopenharmony_ci *              get_cpu_iowait_time_us()
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci *	     2. synchronization with other hw blocks
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/delay.h>
308c2ecf20Sopenharmony_ci#include <linux/kthread.h>
318c2ecf20Sopenharmony_ci#include <linux/cpu.h>
328c2ecf20Sopenharmony_ci#include <linux/thermal.h>
338c2ecf20Sopenharmony_ci#include <linux/slab.h>
348c2ecf20Sopenharmony_ci#include <linux/tick.h>
358c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
368c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
378c2ecf20Sopenharmony_ci#include <linux/sched/rt.h>
388c2ecf20Sopenharmony_ci#include <uapi/linux/sched/types.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <asm/nmi.h>
418c2ecf20Sopenharmony_ci#include <asm/msr.h>
428c2ecf20Sopenharmony_ci#include <asm/mwait.h>
438c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h>
448c2ecf20Sopenharmony_ci#include <asm/hardirq.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define MAX_TARGET_RATIO (50U)
478c2ecf20Sopenharmony_ci/* For each undisturbed clamping period (no extra wake ups during idle time),
488c2ecf20Sopenharmony_ci * we increment the confidence counter for the given target ratio.
498c2ecf20Sopenharmony_ci * CONFIDENCE_OK defines the level where runtime calibration results are
508c2ecf20Sopenharmony_ci * valid.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ci#define CONFIDENCE_OK (3)
538c2ecf20Sopenharmony_ci/* Default idle injection duration, driver adjust sleep time to meet target
548c2ecf20Sopenharmony_ci * idle ratio. Similar to frequency modulation.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define DEFAULT_DURATION_JIFFIES (6)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic unsigned int target_mwait;
598c2ecf20Sopenharmony_cistatic struct dentry *debug_dir;
608c2ecf20Sopenharmony_cistatic bool poll_pkg_cstate_enable;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* user selected target */
638c2ecf20Sopenharmony_cistatic unsigned int set_target_ratio;
648c2ecf20Sopenharmony_cistatic unsigned int current_ratio;
658c2ecf20Sopenharmony_cistatic bool should_skip;
668c2ecf20Sopenharmony_cistatic bool reduce_irq;
678c2ecf20Sopenharmony_cistatic atomic_t idle_wakeup_counter;
688c2ecf20Sopenharmony_cistatic unsigned int control_cpu; /* The cpu assigned to collect stat and update
698c2ecf20Sopenharmony_ci				  * control parameters. default to BSP but BSP
708c2ecf20Sopenharmony_ci				  * can be offlined.
718c2ecf20Sopenharmony_ci				  */
728c2ecf20Sopenharmony_cistatic bool clamping;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct powerclamp_worker_data {
758c2ecf20Sopenharmony_ci	struct kthread_worker *worker;
768c2ecf20Sopenharmony_ci	struct kthread_work balancing_work;
778c2ecf20Sopenharmony_ci	struct kthread_delayed_work idle_injection_work;
788c2ecf20Sopenharmony_ci	unsigned int cpu;
798c2ecf20Sopenharmony_ci	unsigned int count;
808c2ecf20Sopenharmony_ci	unsigned int guard;
818c2ecf20Sopenharmony_ci	unsigned int window_size_now;
828c2ecf20Sopenharmony_ci	unsigned int target_ratio;
838c2ecf20Sopenharmony_ci	unsigned int duration_jiffies;
848c2ecf20Sopenharmony_ci	bool clamping;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct powerclamp_worker_data __percpu *worker_data;
888c2ecf20Sopenharmony_cistatic struct thermal_cooling_device *cooling_dev;
898c2ecf20Sopenharmony_cistatic unsigned long *cpu_clamping_mask;  /* bit map for tracking per cpu
908c2ecf20Sopenharmony_ci					   * clamping kthread worker
918c2ecf20Sopenharmony_ci					   */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic unsigned int duration;
948c2ecf20Sopenharmony_cistatic unsigned int pkg_cstate_ratio_cur;
958c2ecf20Sopenharmony_cistatic unsigned int window_size;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int duration_set(const char *arg, const struct kernel_param *kp)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int ret = 0;
1008c2ecf20Sopenharmony_ci	unsigned long new_duration;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ret = kstrtoul(arg, 10, &new_duration);
1038c2ecf20Sopenharmony_ci	if (ret)
1048c2ecf20Sopenharmony_ci		goto exit;
1058c2ecf20Sopenharmony_ci	if (new_duration > 25 || new_duration < 6) {
1068c2ecf20Sopenharmony_ci		pr_err("Out of recommended range %lu, between 6-25ms\n",
1078c2ecf20Sopenharmony_ci			new_duration);
1088c2ecf20Sopenharmony_ci		ret = -EINVAL;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	duration = clamp(new_duration, 6ul, 25ul);
1128c2ecf20Sopenharmony_ci	smp_mb();
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciexit:
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return ret;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic const struct kernel_param_ops duration_ops = {
1208c2ecf20Sopenharmony_ci	.set = duration_set,
1218c2ecf20Sopenharmony_ci	.get = param_get_int,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cimodule_param_cb(duration, &duration_ops, &duration, 0644);
1268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistruct powerclamp_calibration_data {
1298c2ecf20Sopenharmony_ci	unsigned long confidence;  /* used for calibration, basically a counter
1308c2ecf20Sopenharmony_ci				    * gets incremented each time a clamping
1318c2ecf20Sopenharmony_ci				    * period is completed without extra wakeups
1328c2ecf20Sopenharmony_ci				    * once that counter is reached given level,
1338c2ecf20Sopenharmony_ci				    * compensation is deemed usable.
1348c2ecf20Sopenharmony_ci				    */
1358c2ecf20Sopenharmony_ci	unsigned long steady_comp; /* steady state compensation used when
1368c2ecf20Sopenharmony_ci				    * no extra wakeups occurred.
1378c2ecf20Sopenharmony_ci				    */
1388c2ecf20Sopenharmony_ci	unsigned long dynamic_comp; /* compensate excessive wakeup from idle
1398c2ecf20Sopenharmony_ci				     * mostly from external interrupts.
1408c2ecf20Sopenharmony_ci				     */
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO];
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int window_size_set(const char *arg, const struct kernel_param *kp)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int ret = 0;
1488c2ecf20Sopenharmony_ci	unsigned long new_window_size;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = kstrtoul(arg, 10, &new_window_size);
1518c2ecf20Sopenharmony_ci	if (ret)
1528c2ecf20Sopenharmony_ci		goto exit_win;
1538c2ecf20Sopenharmony_ci	if (new_window_size > 10 || new_window_size < 2) {
1548c2ecf20Sopenharmony_ci		pr_err("Out of recommended window size %lu, between 2-10\n",
1558c2ecf20Sopenharmony_ci			new_window_size);
1568c2ecf20Sopenharmony_ci		ret = -EINVAL;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	window_size = clamp(new_window_size, 2ul, 10ul);
1608c2ecf20Sopenharmony_ci	smp_mb();
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciexit_win:
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return ret;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const struct kernel_param_ops window_size_ops = {
1688c2ecf20Sopenharmony_ci	.set = window_size_set,
1698c2ecf20Sopenharmony_ci	.get = param_get_int,
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cimodule_param_cb(window_size, &window_size_ops, &window_size, 0644);
1738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n"
1748c2ecf20Sopenharmony_ci	"\tpowerclamp controls idle ratio within this window. larger\n"
1758c2ecf20Sopenharmony_ci	"\twindow size results in slower response time but more smooth\n"
1768c2ecf20Sopenharmony_ci	"\tclamping results. default to 2.");
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic void find_target_mwait(void)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	unsigned int eax, ebx, ecx, edx;
1818c2ecf20Sopenharmony_ci	unsigned int highest_cstate = 0;
1828c2ecf20Sopenharmony_ci	unsigned int highest_subcstate = 0;
1838c2ecf20Sopenharmony_ci	int i;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
1868c2ecf20Sopenharmony_ci		return;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
1918c2ecf20Sopenharmony_ci	    !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	edx >>= MWAIT_SUBSTATE_SIZE;
1958c2ecf20Sopenharmony_ci	for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
1968c2ecf20Sopenharmony_ci		if (edx & MWAIT_SUBSTATE_MASK) {
1978c2ecf20Sopenharmony_ci			highest_cstate = i;
1988c2ecf20Sopenharmony_ci			highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
1998c2ecf20Sopenharmony_ci		}
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
2028c2ecf20Sopenharmony_ci		(highest_subcstate - 1);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistruct pkg_cstate_info {
2078c2ecf20Sopenharmony_ci	bool skip;
2088c2ecf20Sopenharmony_ci	int msr_index;
2098c2ecf20Sopenharmony_ci	int cstate_id;
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci#define PKG_CSTATE_INIT(id) {				\
2138c2ecf20Sopenharmony_ci		.msr_index = MSR_PKG_C##id##_RESIDENCY, \
2148c2ecf20Sopenharmony_ci		.cstate_id = id				\
2158c2ecf20Sopenharmony_ci			}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic struct pkg_cstate_info pkg_cstates[] = {
2188c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(2),
2198c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(3),
2208c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(6),
2218c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(7),
2228c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(8),
2238c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(9),
2248c2ecf20Sopenharmony_ci	PKG_CSTATE_INIT(10),
2258c2ecf20Sopenharmony_ci	{NULL},
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic bool has_pkg_state_counter(void)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u64 val;
2318c2ecf20Sopenharmony_ci	struct pkg_cstate_info *info = pkg_cstates;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* check if any one of the counter msrs exists */
2348c2ecf20Sopenharmony_ci	while (info->msr_index) {
2358c2ecf20Sopenharmony_ci		if (!rdmsrl_safe(info->msr_index, &val))
2368c2ecf20Sopenharmony_ci			return true;
2378c2ecf20Sopenharmony_ci		info++;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return false;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic u64 pkg_state_counter(void)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	u64 val;
2468c2ecf20Sopenharmony_ci	u64 count = 0;
2478c2ecf20Sopenharmony_ci	struct pkg_cstate_info *info = pkg_cstates;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	while (info->msr_index) {
2508c2ecf20Sopenharmony_ci		if (!info->skip) {
2518c2ecf20Sopenharmony_ci			if (!rdmsrl_safe(info->msr_index, &val))
2528c2ecf20Sopenharmony_ci				count += val;
2538c2ecf20Sopenharmony_ci			else
2548c2ecf20Sopenharmony_ci				info->skip = true;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci		info++;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return count;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic unsigned int get_compensation(int ratio)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	unsigned int comp = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (!poll_pkg_cstate_enable)
2678c2ecf20Sopenharmony_ci		return 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* we only use compensation if all adjacent ones are good */
2708c2ecf20Sopenharmony_ci	if (ratio == 1 &&
2718c2ecf20Sopenharmony_ci		cal_data[ratio].confidence >= CONFIDENCE_OK &&
2728c2ecf20Sopenharmony_ci		cal_data[ratio + 1].confidence >= CONFIDENCE_OK &&
2738c2ecf20Sopenharmony_ci		cal_data[ratio + 2].confidence >= CONFIDENCE_OK) {
2748c2ecf20Sopenharmony_ci		comp = (cal_data[ratio].steady_comp +
2758c2ecf20Sopenharmony_ci			cal_data[ratio + 1].steady_comp +
2768c2ecf20Sopenharmony_ci			cal_data[ratio + 2].steady_comp) / 3;
2778c2ecf20Sopenharmony_ci	} else if (ratio == MAX_TARGET_RATIO - 1 &&
2788c2ecf20Sopenharmony_ci		cal_data[ratio].confidence >= CONFIDENCE_OK &&
2798c2ecf20Sopenharmony_ci		cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
2808c2ecf20Sopenharmony_ci		cal_data[ratio - 2].confidence >= CONFIDENCE_OK) {
2818c2ecf20Sopenharmony_ci		comp = (cal_data[ratio].steady_comp +
2828c2ecf20Sopenharmony_ci			cal_data[ratio - 1].steady_comp +
2838c2ecf20Sopenharmony_ci			cal_data[ratio - 2].steady_comp) / 3;
2848c2ecf20Sopenharmony_ci	} else if (cal_data[ratio].confidence >= CONFIDENCE_OK &&
2858c2ecf20Sopenharmony_ci		cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
2868c2ecf20Sopenharmony_ci		cal_data[ratio + 1].confidence >= CONFIDENCE_OK) {
2878c2ecf20Sopenharmony_ci		comp = (cal_data[ratio].steady_comp +
2888c2ecf20Sopenharmony_ci			cal_data[ratio - 1].steady_comp +
2898c2ecf20Sopenharmony_ci			cal_data[ratio + 1].steady_comp) / 3;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* REVISIT: simple penalty of double idle injection */
2938c2ecf20Sopenharmony_ci	if (reduce_irq)
2948c2ecf20Sopenharmony_ci		comp = ratio;
2958c2ecf20Sopenharmony_ci	/* do not exceed limit */
2968c2ecf20Sopenharmony_ci	if (comp + ratio >= MAX_TARGET_RATIO)
2978c2ecf20Sopenharmony_ci		comp = MAX_TARGET_RATIO - ratio - 1;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return comp;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void adjust_compensation(int target_ratio, unsigned int win)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int delta;
3058c2ecf20Sopenharmony_ci	struct powerclamp_calibration_data *d = &cal_data[target_ratio];
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/*
3088c2ecf20Sopenharmony_ci	 * adjust compensations if confidence level has not been reached or
3098c2ecf20Sopenharmony_ci	 * there are too many wakeups during the last idle injection period, we
3108c2ecf20Sopenharmony_ci	 * cannot trust the data for compensation.
3118c2ecf20Sopenharmony_ci	 */
3128c2ecf20Sopenharmony_ci	if (d->confidence >= CONFIDENCE_OK ||
3138c2ecf20Sopenharmony_ci		atomic_read(&idle_wakeup_counter) >
3148c2ecf20Sopenharmony_ci		win * num_online_cpus())
3158c2ecf20Sopenharmony_ci		return;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	delta = set_target_ratio - current_ratio;
3188c2ecf20Sopenharmony_ci	/* filter out bad data */
3198c2ecf20Sopenharmony_ci	if (delta >= 0 && delta <= (1+target_ratio/10)) {
3208c2ecf20Sopenharmony_ci		if (d->steady_comp)
3218c2ecf20Sopenharmony_ci			d->steady_comp =
3228c2ecf20Sopenharmony_ci				roundup(delta+d->steady_comp, 2)/2;
3238c2ecf20Sopenharmony_ci		else
3248c2ecf20Sopenharmony_ci			d->steady_comp = delta;
3258c2ecf20Sopenharmony_ci		d->confidence++;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic bool powerclamp_adjust_controls(unsigned int target_ratio,
3308c2ecf20Sopenharmony_ci				unsigned int guard, unsigned int win)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	static u64 msr_last, tsc_last;
3338c2ecf20Sopenharmony_ci	u64 msr_now, tsc_now;
3348c2ecf20Sopenharmony_ci	u64 val64;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* check result for the last window */
3378c2ecf20Sopenharmony_ci	msr_now = pkg_state_counter();
3388c2ecf20Sopenharmony_ci	tsc_now = rdtsc();
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* calculate pkg cstate vs tsc ratio */
3418c2ecf20Sopenharmony_ci	if (!msr_last || !tsc_last)
3428c2ecf20Sopenharmony_ci		current_ratio = 1;
3438c2ecf20Sopenharmony_ci	else if (tsc_now-tsc_last) {
3448c2ecf20Sopenharmony_ci		val64 = 100*(msr_now-msr_last);
3458c2ecf20Sopenharmony_ci		do_div(val64, (tsc_now-tsc_last));
3468c2ecf20Sopenharmony_ci		current_ratio = val64;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* update record */
3508c2ecf20Sopenharmony_ci	msr_last = msr_now;
3518c2ecf20Sopenharmony_ci	tsc_last = tsc_now;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	adjust_compensation(target_ratio, win);
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * too many external interrupts, set flag such
3568c2ecf20Sopenharmony_ci	 * that we can take measure later.
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	reduce_irq = atomic_read(&idle_wakeup_counter) >=
3598c2ecf20Sopenharmony_ci		2 * win * num_online_cpus();
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	atomic_set(&idle_wakeup_counter, 0);
3628c2ecf20Sopenharmony_ci	/* if we are above target+guard, skip */
3638c2ecf20Sopenharmony_ci	return set_target_ratio + guard <= current_ratio;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void clamp_balancing_func(struct kthread_work *work)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct powerclamp_worker_data *w_data;
3698c2ecf20Sopenharmony_ci	int sleeptime;
3708c2ecf20Sopenharmony_ci	unsigned long target_jiffies;
3718c2ecf20Sopenharmony_ci	unsigned int compensated_ratio;
3728c2ecf20Sopenharmony_ci	int interval; /* jiffies to sleep for each attempt */
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	w_data = container_of(work, struct powerclamp_worker_data,
3758c2ecf20Sopenharmony_ci			      balancing_work);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/*
3788c2ecf20Sopenharmony_ci	 * make sure user selected ratio does not take effect until
3798c2ecf20Sopenharmony_ci	 * the next round. adjust target_ratio if user has changed
3808c2ecf20Sopenharmony_ci	 * target such that we can converge quickly.
3818c2ecf20Sopenharmony_ci	 */
3828c2ecf20Sopenharmony_ci	w_data->target_ratio = READ_ONCE(set_target_ratio);
3838c2ecf20Sopenharmony_ci	w_data->guard = 1 + w_data->target_ratio / 20;
3848c2ecf20Sopenharmony_ci	w_data->window_size_now = window_size;
3858c2ecf20Sopenharmony_ci	w_data->duration_jiffies = msecs_to_jiffies(duration);
3868c2ecf20Sopenharmony_ci	w_data->count++;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/*
3898c2ecf20Sopenharmony_ci	 * systems may have different ability to enter package level
3908c2ecf20Sopenharmony_ci	 * c-states, thus we need to compensate the injected idle ratio
3918c2ecf20Sopenharmony_ci	 * to achieve the actual target reported by the HW.
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	compensated_ratio = w_data->target_ratio +
3948c2ecf20Sopenharmony_ci		get_compensation(w_data->target_ratio);
3958c2ecf20Sopenharmony_ci	if (compensated_ratio <= 0)
3968c2ecf20Sopenharmony_ci		compensated_ratio = 1;
3978c2ecf20Sopenharmony_ci	interval = w_data->duration_jiffies * 100 / compensated_ratio;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* align idle time */
4008c2ecf20Sopenharmony_ci	target_jiffies = roundup(jiffies, interval);
4018c2ecf20Sopenharmony_ci	sleeptime = target_jiffies - jiffies;
4028c2ecf20Sopenharmony_ci	if (sleeptime <= 0)
4038c2ecf20Sopenharmony_ci		sleeptime = 1;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (clamping && w_data->clamping && cpu_online(w_data->cpu))
4068c2ecf20Sopenharmony_ci		kthread_queue_delayed_work(w_data->worker,
4078c2ecf20Sopenharmony_ci					   &w_data->idle_injection_work,
4088c2ecf20Sopenharmony_ci					   sleeptime);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void clamp_idle_injection_func(struct kthread_work *work)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct powerclamp_worker_data *w_data;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	w_data = container_of(work, struct powerclamp_worker_data,
4168c2ecf20Sopenharmony_ci			      idle_injection_work.work);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/*
4198c2ecf20Sopenharmony_ci	 * only elected controlling cpu can collect stats and update
4208c2ecf20Sopenharmony_ci	 * control parameters.
4218c2ecf20Sopenharmony_ci	 */
4228c2ecf20Sopenharmony_ci	if (w_data->cpu == control_cpu &&
4238c2ecf20Sopenharmony_ci	    !(w_data->count % w_data->window_size_now)) {
4248c2ecf20Sopenharmony_ci		should_skip =
4258c2ecf20Sopenharmony_ci			powerclamp_adjust_controls(w_data->target_ratio,
4268c2ecf20Sopenharmony_ci						   w_data->guard,
4278c2ecf20Sopenharmony_ci						   w_data->window_size_now);
4288c2ecf20Sopenharmony_ci		smp_mb();
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (should_skip)
4328c2ecf20Sopenharmony_ci		goto balance;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	play_idle(jiffies_to_usecs(w_data->duration_jiffies));
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cibalance:
4378c2ecf20Sopenharmony_ci	if (clamping && w_data->clamping && cpu_online(w_data->cpu))
4388c2ecf20Sopenharmony_ci		kthread_queue_work(w_data->worker, &w_data->balancing_work);
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/*
4428c2ecf20Sopenharmony_ci * 1 HZ polling while clamping is active, useful for userspace
4438c2ecf20Sopenharmony_ci * to monitor actual idle ratio.
4448c2ecf20Sopenharmony_ci */
4458c2ecf20Sopenharmony_cistatic void poll_pkg_cstate(struct work_struct *dummy);
4468c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate);
4478c2ecf20Sopenharmony_cistatic void poll_pkg_cstate(struct work_struct *dummy)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	static u64 msr_last;
4508c2ecf20Sopenharmony_ci	static u64 tsc_last;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	u64 msr_now;
4538c2ecf20Sopenharmony_ci	u64 tsc_now;
4548c2ecf20Sopenharmony_ci	u64 val64;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	msr_now = pkg_state_counter();
4578c2ecf20Sopenharmony_ci	tsc_now = rdtsc();
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* calculate pkg cstate vs tsc ratio */
4608c2ecf20Sopenharmony_ci	if (!msr_last || !tsc_last)
4618c2ecf20Sopenharmony_ci		pkg_cstate_ratio_cur = 1;
4628c2ecf20Sopenharmony_ci	else {
4638c2ecf20Sopenharmony_ci		if (tsc_now - tsc_last) {
4648c2ecf20Sopenharmony_ci			val64 = 100 * (msr_now - msr_last);
4658c2ecf20Sopenharmony_ci			do_div(val64, (tsc_now - tsc_last));
4668c2ecf20Sopenharmony_ci			pkg_cstate_ratio_cur = val64;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* update record */
4718c2ecf20Sopenharmony_ci	msr_last = msr_now;
4728c2ecf20Sopenharmony_ci	tsc_last = tsc_now;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (true == clamping)
4758c2ecf20Sopenharmony_ci		schedule_delayed_work(&poll_pkg_cstate_work, HZ);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void start_power_clamp_worker(unsigned long cpu)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
4818c2ecf20Sopenharmony_ci	struct kthread_worker *worker;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inj/%ld", cpu);
4848c2ecf20Sopenharmony_ci	if (IS_ERR(worker))
4858c2ecf20Sopenharmony_ci		return;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	w_data->worker = worker;
4888c2ecf20Sopenharmony_ci	w_data->count = 0;
4898c2ecf20Sopenharmony_ci	w_data->cpu = cpu;
4908c2ecf20Sopenharmony_ci	w_data->clamping = true;
4918c2ecf20Sopenharmony_ci	set_bit(cpu, cpu_clamping_mask);
4928c2ecf20Sopenharmony_ci	sched_set_fifo(worker->task);
4938c2ecf20Sopenharmony_ci	kthread_init_work(&w_data->balancing_work, clamp_balancing_func);
4948c2ecf20Sopenharmony_ci	kthread_init_delayed_work(&w_data->idle_injection_work,
4958c2ecf20Sopenharmony_ci				  clamp_idle_injection_func);
4968c2ecf20Sopenharmony_ci	kthread_queue_work(w_data->worker, &w_data->balancing_work);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void stop_power_clamp_worker(unsigned long cpu)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (!w_data->worker)
5048c2ecf20Sopenharmony_ci		return;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	w_data->clamping = false;
5078c2ecf20Sopenharmony_ci	/*
5088c2ecf20Sopenharmony_ci	 * Make sure that all works that get queued after this point see
5098c2ecf20Sopenharmony_ci	 * the clamping disabled. The counter part is not needed because
5108c2ecf20Sopenharmony_ci	 * there is an implicit memory barrier when the queued work
5118c2ecf20Sopenharmony_ci	 * is proceed.
5128c2ecf20Sopenharmony_ci	 */
5138c2ecf20Sopenharmony_ci	smp_wmb();
5148c2ecf20Sopenharmony_ci	kthread_cancel_work_sync(&w_data->balancing_work);
5158c2ecf20Sopenharmony_ci	kthread_cancel_delayed_work_sync(&w_data->idle_injection_work);
5168c2ecf20Sopenharmony_ci	/*
5178c2ecf20Sopenharmony_ci	 * The balancing work still might be queued here because
5188c2ecf20Sopenharmony_ci	 * the handling of the "clapming" variable, cancel, and queue
5198c2ecf20Sopenharmony_ci	 * operations are not synchronized via a lock. But it is not
5208c2ecf20Sopenharmony_ci	 * a big deal. The balancing work is fast and destroy kthread
5218c2ecf20Sopenharmony_ci	 * will wait for it.
5228c2ecf20Sopenharmony_ci	 */
5238c2ecf20Sopenharmony_ci	clear_bit(w_data->cpu, cpu_clamping_mask);
5248c2ecf20Sopenharmony_ci	kthread_destroy_worker(w_data->worker);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	w_data->worker = NULL;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic int start_power_clamp(void)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	unsigned long cpu;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
5348c2ecf20Sopenharmony_ci	/* prevent cpu hotplug */
5358c2ecf20Sopenharmony_ci	get_online_cpus();
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* prefer BSP */
5388c2ecf20Sopenharmony_ci	control_cpu = cpumask_first(cpu_online_mask);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	clamping = true;
5418c2ecf20Sopenharmony_ci	if (poll_pkg_cstate_enable)
5428c2ecf20Sopenharmony_ci		schedule_delayed_work(&poll_pkg_cstate_work, 0);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* start one kthread worker per online cpu */
5458c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
5468c2ecf20Sopenharmony_ci		start_power_clamp_worker(cpu);
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci	put_online_cpus();
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void end_power_clamp(void)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	int i;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/*
5588c2ecf20Sopenharmony_ci	 * Block requeuing in all the kthread workers. They will flush and
5598c2ecf20Sopenharmony_ci	 * stop faster.
5608c2ecf20Sopenharmony_ci	 */
5618c2ecf20Sopenharmony_ci	clamping = false;
5628c2ecf20Sopenharmony_ci	if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) {
5638c2ecf20Sopenharmony_ci		for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) {
5648c2ecf20Sopenharmony_ci			pr_debug("clamping worker for cpu %d alive, destroy\n",
5658c2ecf20Sopenharmony_ci				 i);
5668c2ecf20Sopenharmony_ci			stop_power_clamp_worker(i);
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int powerclamp_cpu_online(unsigned int cpu)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	if (clamping == false)
5748c2ecf20Sopenharmony_ci		return 0;
5758c2ecf20Sopenharmony_ci	start_power_clamp_worker(cpu);
5768c2ecf20Sopenharmony_ci	/* prefer BSP as controlling CPU */
5778c2ecf20Sopenharmony_ci	if (cpu == 0) {
5788c2ecf20Sopenharmony_ci		control_cpu = 0;
5798c2ecf20Sopenharmony_ci		smp_mb();
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci	return 0;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic int powerclamp_cpu_predown(unsigned int cpu)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	if (clamping == false)
5878c2ecf20Sopenharmony_ci		return 0;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	stop_power_clamp_worker(cpu);
5908c2ecf20Sopenharmony_ci	if (cpu != control_cpu)
5918c2ecf20Sopenharmony_ci		return 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	control_cpu = cpumask_first(cpu_online_mask);
5948c2ecf20Sopenharmony_ci	if (control_cpu == cpu)
5958c2ecf20Sopenharmony_ci		control_cpu = cpumask_next(cpu, cpu_online_mask);
5968c2ecf20Sopenharmony_ci	smp_mb();
5978c2ecf20Sopenharmony_ci	return 0;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
6018c2ecf20Sopenharmony_ci				 unsigned long *state)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	*state = MAX_TARGET_RATIO;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	return 0;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
6098c2ecf20Sopenharmony_ci				 unsigned long *state)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	if (clamping) {
6128c2ecf20Sopenharmony_ci		if (poll_pkg_cstate_enable)
6138c2ecf20Sopenharmony_ci			*state = pkg_cstate_ratio_cur;
6148c2ecf20Sopenharmony_ci		else
6158c2ecf20Sopenharmony_ci			*state = set_target_ratio;
6168c2ecf20Sopenharmony_ci	} else {
6178c2ecf20Sopenharmony_ci		/* to save power, do not poll idle ratio while not clamping */
6188c2ecf20Sopenharmony_ci		*state = -1; /* indicates invalid state */
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	return 0;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
6258c2ecf20Sopenharmony_ci				 unsigned long new_target_ratio)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	int ret = 0;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	new_target_ratio = clamp(new_target_ratio, 0UL,
6308c2ecf20Sopenharmony_ci				(unsigned long) (MAX_TARGET_RATIO-1));
6318c2ecf20Sopenharmony_ci	if (set_target_ratio == 0 && new_target_ratio > 0) {
6328c2ecf20Sopenharmony_ci		pr_info("Start idle injection to reduce power\n");
6338c2ecf20Sopenharmony_ci		set_target_ratio = new_target_ratio;
6348c2ecf20Sopenharmony_ci		ret = start_power_clamp();
6358c2ecf20Sopenharmony_ci		goto exit_set;
6368c2ecf20Sopenharmony_ci	} else	if (set_target_ratio > 0 && new_target_ratio == 0) {
6378c2ecf20Sopenharmony_ci		pr_info("Stop forced idle injection\n");
6388c2ecf20Sopenharmony_ci		end_power_clamp();
6398c2ecf20Sopenharmony_ci		set_target_ratio = 0;
6408c2ecf20Sopenharmony_ci	} else	/* adjust currently running */ {
6418c2ecf20Sopenharmony_ci		set_target_ratio = new_target_ratio;
6428c2ecf20Sopenharmony_ci		/* make new set_target_ratio visible to other cpus */
6438c2ecf20Sopenharmony_ci		smp_mb();
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ciexit_set:
6478c2ecf20Sopenharmony_ci	return ret;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci/* bind to generic thermal layer as cooling device*/
6518c2ecf20Sopenharmony_cistatic struct thermal_cooling_device_ops powerclamp_cooling_ops = {
6528c2ecf20Sopenharmony_ci	.get_max_state = powerclamp_get_max_state,
6538c2ecf20Sopenharmony_ci	.get_cur_state = powerclamp_get_cur_state,
6548c2ecf20Sopenharmony_ci	.set_cur_state = powerclamp_set_cur_state,
6558c2ecf20Sopenharmony_ci};
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic const struct x86_cpu_id __initconst intel_powerclamp_ids[] = {
6588c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_MWAIT, NULL),
6598c2ecf20Sopenharmony_ci	{}
6608c2ecf20Sopenharmony_ci};
6618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int __init powerclamp_probe(void)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (!x86_match_cpu(intel_powerclamp_ids)) {
6678c2ecf20Sopenharmony_ci		pr_err("CPU does not support MWAIT\n");
6688c2ecf20Sopenharmony_ci		return -ENODEV;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* The goal for idle time alignment is to achieve package cstate. */
6728c2ecf20Sopenharmony_ci	if (!has_pkg_state_counter()) {
6738c2ecf20Sopenharmony_ci		pr_info("No package C-state available\n");
6748c2ecf20Sopenharmony_ci		return -ENODEV;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* find the deepest mwait value */
6788c2ecf20Sopenharmony_ci	find_target_mwait();
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic int powerclamp_debug_show(struct seq_file *m, void *unused)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	int i = 0;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	seq_printf(m, "controlling cpu: %d\n", control_cpu);
6888c2ecf20Sopenharmony_ci	seq_printf(m, "pct confidence steady dynamic (compensation)\n");
6898c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TARGET_RATIO; i++) {
6908c2ecf20Sopenharmony_ci		seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
6918c2ecf20Sopenharmony_ci			i,
6928c2ecf20Sopenharmony_ci			cal_data[i].confidence,
6938c2ecf20Sopenharmony_ci			cal_data[i].steady_comp,
6948c2ecf20Sopenharmony_ci			cal_data[i].dynamic_comp);
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return 0;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(powerclamp_debug);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic inline void powerclamp_create_debug_files(void)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	debug_dir = debugfs_create_dir("intel_powerclamp", NULL);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir, cal_data,
7078c2ecf20Sopenharmony_ci			    &powerclamp_debug_fops);
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic enum cpuhp_state hp_state;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int __init powerclamp_init(void)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	int retval;
7158c2ecf20Sopenharmony_ci	int bitmap_size;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	bitmap_size = BITS_TO_LONGS(num_possible_cpus()) * sizeof(long);
7188c2ecf20Sopenharmony_ci	cpu_clamping_mask = kzalloc(bitmap_size, GFP_KERNEL);
7198c2ecf20Sopenharmony_ci	if (!cpu_clamping_mask)
7208c2ecf20Sopenharmony_ci		return -ENOMEM;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* probe cpu features and ids here */
7238c2ecf20Sopenharmony_ci	retval = powerclamp_probe();
7248c2ecf20Sopenharmony_ci	if (retval)
7258c2ecf20Sopenharmony_ci		goto exit_free;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* set default limit, maybe adjusted during runtime based on feedback */
7288c2ecf20Sopenharmony_ci	window_size = 2;
7298c2ecf20Sopenharmony_ci	retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
7308c2ecf20Sopenharmony_ci					   "thermal/intel_powerclamp:online",
7318c2ecf20Sopenharmony_ci					   powerclamp_cpu_online,
7328c2ecf20Sopenharmony_ci					   powerclamp_cpu_predown);
7338c2ecf20Sopenharmony_ci	if (retval < 0)
7348c2ecf20Sopenharmony_ci		goto exit_free;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	hp_state = retval;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	worker_data = alloc_percpu(struct powerclamp_worker_data);
7398c2ecf20Sopenharmony_ci	if (!worker_data) {
7408c2ecf20Sopenharmony_ci		retval = -ENOMEM;
7418c2ecf20Sopenharmony_ci		goto exit_unregister;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (topology_max_packages() == 1 && topology_max_die_per_package() == 1)
7458c2ecf20Sopenharmony_ci		poll_pkg_cstate_enable = true;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
7488c2ecf20Sopenharmony_ci						&powerclamp_cooling_ops);
7498c2ecf20Sopenharmony_ci	if (IS_ERR(cooling_dev)) {
7508c2ecf20Sopenharmony_ci		retval = -ENODEV;
7518c2ecf20Sopenharmony_ci		goto exit_free_thread;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (!duration)
7558c2ecf20Sopenharmony_ci		duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	powerclamp_create_debug_files();
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	return 0;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciexit_free_thread:
7628c2ecf20Sopenharmony_ci	free_percpu(worker_data);
7638c2ecf20Sopenharmony_ciexit_unregister:
7648c2ecf20Sopenharmony_ci	cpuhp_remove_state_nocalls(hp_state);
7658c2ecf20Sopenharmony_ciexit_free:
7668c2ecf20Sopenharmony_ci	kfree(cpu_clamping_mask);
7678c2ecf20Sopenharmony_ci	return retval;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_cimodule_init(powerclamp_init);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic void __exit powerclamp_exit(void)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	end_power_clamp();
7748c2ecf20Sopenharmony_ci	cpuhp_remove_state_nocalls(hp_state);
7758c2ecf20Sopenharmony_ci	free_percpu(worker_data);
7768c2ecf20Sopenharmony_ci	thermal_cooling_device_unregister(cooling_dev);
7778c2ecf20Sopenharmony_ci	kfree(cpu_clamping_mask);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&poll_pkg_cstate_work);
7808c2ecf20Sopenharmony_ci	debugfs_remove_recursive(debug_dir);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_cimodule_exit(powerclamp_exit);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
7858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
7868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
7878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs");
788