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