18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2009-2010 Intel Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Jesse Barnes <jbarnes@virtuousgeek.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Some Intel Ibex Peak based platforms support so-called "intelligent 118c2ecf20Sopenharmony_ci * power sharing", which allows the CPU and GPU to cooperate to maximize 128c2ecf20Sopenharmony_ci * performance within a given TDP (thermal design point). This driver 138c2ecf20Sopenharmony_ci * performs the coordination between the CPU and GPU, monitors thermal and 148c2ecf20Sopenharmony_ci * power statistics in the platform, and initializes power monitoring 158c2ecf20Sopenharmony_ci * hardware. It also provides a few tunables to control behavior. Its 168c2ecf20Sopenharmony_ci * primary purpose is to safely allow CPU and GPU turbo modes to be enabled 178c2ecf20Sopenharmony_ci * by tracking power and thermal budget; secondarily it can boost turbo 188c2ecf20Sopenharmony_ci * performance by allocating more power or thermal budget to the CPU or GPU 198c2ecf20Sopenharmony_ci * based on available headroom and activity. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The basic algorithm is driven by a 5s moving average of temperature. If 228c2ecf20Sopenharmony_ci * thermal headroom is available, the CPU and/or GPU power clamps may be 238c2ecf20Sopenharmony_ci * adjusted upwards. If we hit the thermal ceiling or a thermal trigger, 248c2ecf20Sopenharmony_ci * we scale back the clamp. Aside from trigger events (when we're critically 258c2ecf20Sopenharmony_ci * close or over our TDP) we don't adjust the clamps more than once every 268c2ecf20Sopenharmony_ci * five seconds. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * The thermal device (device 31, function 6) has a set of registers that 298c2ecf20Sopenharmony_ci * are updated by the ME firmware. The ME should also take the clamp values 308c2ecf20Sopenharmony_ci * written to those registers and write them to the CPU, but we currently 318c2ecf20Sopenharmony_ci * bypass that functionality and write the CPU MSR directly. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * UNSUPPORTED: 348c2ecf20Sopenharmony_ci * - dual MCP configs 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * TODO: 378c2ecf20Sopenharmony_ci * - handle CPU hotplug 388c2ecf20Sopenharmony_ci * - provide turbo enable/disable api 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Related documents: 418c2ecf20Sopenharmony_ci * - CDI 403777, 403778 - Auburndale EDS vol 1 & 2 428c2ecf20Sopenharmony_ci * - CDI 401376 - Ibex Peak EDS 438c2ecf20Sopenharmony_ci * - ref 26037, 26641 - IPS BIOS spec 448c2ecf20Sopenharmony_ci * - ref 26489 - Nehalem BIOS writer's guide 458c2ecf20Sopenharmony_ci * - ref 26921 - Ibex Peak BIOS Specification 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 498c2ecf20Sopenharmony_ci#include <linux/delay.h> 508c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 518c2ecf20Sopenharmony_ci#include <linux/kernel.h> 528c2ecf20Sopenharmony_ci#include <linux/kthread.h> 538c2ecf20Sopenharmony_ci#include <linux/module.h> 548c2ecf20Sopenharmony_ci#include <linux/pci.h> 558c2ecf20Sopenharmony_ci#include <linux/sched.h> 568c2ecf20Sopenharmony_ci#include <linux/sched/loadavg.h> 578c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 588c2ecf20Sopenharmony_ci#include <linux/string.h> 598c2ecf20Sopenharmony_ci#include <linux/tick.h> 608c2ecf20Sopenharmony_ci#include <linux/timer.h> 618c2ecf20Sopenharmony_ci#include <linux/dmi.h> 628c2ecf20Sopenharmony_ci#include <drm/i915_drm.h> 638c2ecf20Sopenharmony_ci#include <asm/msr.h> 648c2ecf20Sopenharmony_ci#include <asm/processor.h> 658c2ecf20Sopenharmony_ci#include "intel_ips.h" 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * Package level MSRs for monitor/control 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define PLATFORM_INFO 0xce 758c2ecf20Sopenharmony_ci#define PLATFORM_TDP (1<<29) 768c2ecf20Sopenharmony_ci#define PLATFORM_RATIO (1<<28) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define IA32_MISC_ENABLE 0x1a0 798c2ecf20Sopenharmony_ci#define IA32_MISC_TURBO_EN (1ULL<<38) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define TURBO_POWER_CURRENT_LIMIT 0x1ac 828c2ecf20Sopenharmony_ci#define TURBO_TDC_OVR_EN (1UL<<31) 838c2ecf20Sopenharmony_ci#define TURBO_TDC_MASK (0x000000007fff0000UL) 848c2ecf20Sopenharmony_ci#define TURBO_TDC_SHIFT (16) 858c2ecf20Sopenharmony_ci#define TURBO_TDP_OVR_EN (1UL<<15) 868c2ecf20Sopenharmony_ci#define TURBO_TDP_MASK (0x0000000000003fffUL) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * Core/thread MSRs for monitoring 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci#define IA32_PERF_CTL 0x199 928c2ecf20Sopenharmony_ci#define IA32_PERF_TURBO_DIS (1ULL<<32) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * Thermal PCI device regs 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci#define THM_CFG_TBAR 0x10 988c2ecf20Sopenharmony_ci#define THM_CFG_TBAR_HI 0x14 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define THM_TSIU 0x00 1018c2ecf20Sopenharmony_ci#define THM_TSE 0x01 1028c2ecf20Sopenharmony_ci#define TSE_EN 0xb8 1038c2ecf20Sopenharmony_ci#define THM_TSS 0x02 1048c2ecf20Sopenharmony_ci#define THM_TSTR 0x03 1058c2ecf20Sopenharmony_ci#define THM_TSTTP 0x04 1068c2ecf20Sopenharmony_ci#define THM_TSCO 0x08 1078c2ecf20Sopenharmony_ci#define THM_TSES 0x0c 1088c2ecf20Sopenharmony_ci#define THM_TSGPEN 0x0d 1098c2ecf20Sopenharmony_ci#define TSGPEN_HOT_LOHI (1<<1) 1108c2ecf20Sopenharmony_ci#define TSGPEN_CRIT_LOHI (1<<2) 1118c2ecf20Sopenharmony_ci#define THM_TSPC 0x0e 1128c2ecf20Sopenharmony_ci#define THM_PPEC 0x10 1138c2ecf20Sopenharmony_ci#define THM_CTA 0x12 1148c2ecf20Sopenharmony_ci#define THM_PTA 0x14 1158c2ecf20Sopenharmony_ci#define PTA_SLOPE_MASK (0xff00) 1168c2ecf20Sopenharmony_ci#define PTA_SLOPE_SHIFT 8 1178c2ecf20Sopenharmony_ci#define PTA_OFFSET_MASK (0x00ff) 1188c2ecf20Sopenharmony_ci#define THM_MGTA 0x16 1198c2ecf20Sopenharmony_ci#define MGTA_SLOPE_MASK (0xff00) 1208c2ecf20Sopenharmony_ci#define MGTA_SLOPE_SHIFT 8 1218c2ecf20Sopenharmony_ci#define MGTA_OFFSET_MASK (0x00ff) 1228c2ecf20Sopenharmony_ci#define THM_TRC 0x1a 1238c2ecf20Sopenharmony_ci#define TRC_CORE2_EN (1<<15) 1248c2ecf20Sopenharmony_ci#define TRC_THM_EN (1<<12) 1258c2ecf20Sopenharmony_ci#define TRC_C6_WAR (1<<8) 1268c2ecf20Sopenharmony_ci#define TRC_CORE1_EN (1<<7) 1278c2ecf20Sopenharmony_ci#define TRC_CORE_PWR (1<<6) 1288c2ecf20Sopenharmony_ci#define TRC_PCH_EN (1<<5) 1298c2ecf20Sopenharmony_ci#define TRC_MCH_EN (1<<4) 1308c2ecf20Sopenharmony_ci#define TRC_DIMM4 (1<<3) 1318c2ecf20Sopenharmony_ci#define TRC_DIMM3 (1<<2) 1328c2ecf20Sopenharmony_ci#define TRC_DIMM2 (1<<1) 1338c2ecf20Sopenharmony_ci#define TRC_DIMM1 (1<<0) 1348c2ecf20Sopenharmony_ci#define THM_TES 0x20 1358c2ecf20Sopenharmony_ci#define THM_TEN 0x21 1368c2ecf20Sopenharmony_ci#define TEN_UPDATE_EN 1 1378c2ecf20Sopenharmony_ci#define THM_PSC 0x24 1388c2ecf20Sopenharmony_ci#define PSC_NTG (1<<0) /* No GFX turbo support */ 1398c2ecf20Sopenharmony_ci#define PSC_NTPC (1<<1) /* No CPU turbo support */ 1408c2ecf20Sopenharmony_ci#define PSC_PP_DEF (0<<2) /* Perf policy up to driver */ 1418c2ecf20Sopenharmony_ci#define PSP_PP_PC (1<<2) /* BIOS prefers CPU perf */ 1428c2ecf20Sopenharmony_ci#define PSP_PP_BAL (2<<2) /* BIOS wants balanced perf */ 1438c2ecf20Sopenharmony_ci#define PSP_PP_GFX (3<<2) /* BIOS prefers GFX perf */ 1448c2ecf20Sopenharmony_ci#define PSP_PBRT (1<<4) /* BIOS run time support */ 1458c2ecf20Sopenharmony_ci#define THM_CTV1 0x30 1468c2ecf20Sopenharmony_ci#define CTV_TEMP_ERROR (1<<15) 1478c2ecf20Sopenharmony_ci#define CTV_TEMP_MASK 0x3f 1488c2ecf20Sopenharmony_ci#define CTV_ 1498c2ecf20Sopenharmony_ci#define THM_CTV2 0x32 1508c2ecf20Sopenharmony_ci#define THM_CEC 0x34 /* undocumented power accumulator in joules */ 1518c2ecf20Sopenharmony_ci#define THM_AE 0x3f 1528c2ecf20Sopenharmony_ci#define THM_HTS 0x50 /* 32 bits */ 1538c2ecf20Sopenharmony_ci#define HTS_PCPL_MASK (0x7fe00000) 1548c2ecf20Sopenharmony_ci#define HTS_PCPL_SHIFT 21 1558c2ecf20Sopenharmony_ci#define HTS_GPL_MASK (0x001ff000) 1568c2ecf20Sopenharmony_ci#define HTS_GPL_SHIFT 12 1578c2ecf20Sopenharmony_ci#define HTS_PP_MASK (0x00000c00) 1588c2ecf20Sopenharmony_ci#define HTS_PP_SHIFT 10 1598c2ecf20Sopenharmony_ci#define HTS_PP_DEF 0 1608c2ecf20Sopenharmony_ci#define HTS_PP_PROC 1 1618c2ecf20Sopenharmony_ci#define HTS_PP_BAL 2 1628c2ecf20Sopenharmony_ci#define HTS_PP_GFX 3 1638c2ecf20Sopenharmony_ci#define HTS_PCTD_DIS (1<<9) 1648c2ecf20Sopenharmony_ci#define HTS_GTD_DIS (1<<8) 1658c2ecf20Sopenharmony_ci#define HTS_PTL_MASK (0x000000fe) 1668c2ecf20Sopenharmony_ci#define HTS_PTL_SHIFT 1 1678c2ecf20Sopenharmony_ci#define HTS_NVV (1<<0) 1688c2ecf20Sopenharmony_ci#define THM_HTSHI 0x54 /* 16 bits */ 1698c2ecf20Sopenharmony_ci#define HTS2_PPL_MASK (0x03ff) 1708c2ecf20Sopenharmony_ci#define HTS2_PRST_MASK (0x3c00) 1718c2ecf20Sopenharmony_ci#define HTS2_PRST_SHIFT 10 1728c2ecf20Sopenharmony_ci#define HTS2_PRST_UNLOADED 0 1738c2ecf20Sopenharmony_ci#define HTS2_PRST_RUNNING 1 1748c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISOP 2 /* turbo disabled due to power */ 1758c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISHT 3 /* turbo disabled due to high temp */ 1768c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISUSR 4 /* user disabled turbo */ 1778c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISPLAT 5 /* platform disabled turbo */ 1788c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISPM 6 /* power management disabled turbo */ 1798c2ecf20Sopenharmony_ci#define HTS2_PRST_TDISERR 7 /* some kind of error disabled turbo */ 1808c2ecf20Sopenharmony_ci#define THM_PTL 0x56 1818c2ecf20Sopenharmony_ci#define THM_MGTV 0x58 1828c2ecf20Sopenharmony_ci#define TV_MASK 0x000000000000ff00 1838c2ecf20Sopenharmony_ci#define TV_SHIFT 8 1848c2ecf20Sopenharmony_ci#define THM_PTV 0x60 1858c2ecf20Sopenharmony_ci#define PTV_MASK 0x00ff 1868c2ecf20Sopenharmony_ci#define THM_MMGPC 0x64 1878c2ecf20Sopenharmony_ci#define THM_MPPC 0x66 1888c2ecf20Sopenharmony_ci#define THM_MPCPC 0x68 1898c2ecf20Sopenharmony_ci#define THM_TSPIEN 0x82 1908c2ecf20Sopenharmony_ci#define TSPIEN_AUX_LOHI (1<<0) 1918c2ecf20Sopenharmony_ci#define TSPIEN_HOT_LOHI (1<<1) 1928c2ecf20Sopenharmony_ci#define TSPIEN_CRIT_LOHI (1<<2) 1938c2ecf20Sopenharmony_ci#define TSPIEN_AUX2_LOHI (1<<3) 1948c2ecf20Sopenharmony_ci#define THM_TSLOCK 0x83 1958c2ecf20Sopenharmony_ci#define THM_ATR 0x84 1968c2ecf20Sopenharmony_ci#define THM_TOF 0x87 1978c2ecf20Sopenharmony_ci#define THM_STS 0x98 1988c2ecf20Sopenharmony_ci#define STS_PCPL_MASK (0x7fe00000) 1998c2ecf20Sopenharmony_ci#define STS_PCPL_SHIFT 21 2008c2ecf20Sopenharmony_ci#define STS_GPL_MASK (0x001ff000) 2018c2ecf20Sopenharmony_ci#define STS_GPL_SHIFT 12 2028c2ecf20Sopenharmony_ci#define STS_PP_MASK (0x00000c00) 2038c2ecf20Sopenharmony_ci#define STS_PP_SHIFT 10 2048c2ecf20Sopenharmony_ci#define STS_PP_DEF 0 2058c2ecf20Sopenharmony_ci#define STS_PP_PROC 1 2068c2ecf20Sopenharmony_ci#define STS_PP_BAL 2 2078c2ecf20Sopenharmony_ci#define STS_PP_GFX 3 2088c2ecf20Sopenharmony_ci#define STS_PCTD_DIS (1<<9) 2098c2ecf20Sopenharmony_ci#define STS_GTD_DIS (1<<8) 2108c2ecf20Sopenharmony_ci#define STS_PTL_MASK (0x000000fe) 2118c2ecf20Sopenharmony_ci#define STS_PTL_SHIFT 1 2128c2ecf20Sopenharmony_ci#define STS_NVV (1<<0) 2138c2ecf20Sopenharmony_ci#define THM_SEC 0x9c 2148c2ecf20Sopenharmony_ci#define SEC_ACK (1<<0) 2158c2ecf20Sopenharmony_ci#define THM_TC3 0xa4 2168c2ecf20Sopenharmony_ci#define THM_TC1 0xa8 2178c2ecf20Sopenharmony_ci#define STS_PPL_MASK (0x0003ff00) 2188c2ecf20Sopenharmony_ci#define STS_PPL_SHIFT 16 2198c2ecf20Sopenharmony_ci#define THM_TC2 0xac 2208c2ecf20Sopenharmony_ci#define THM_DTV 0xb0 2218c2ecf20Sopenharmony_ci#define THM_ITV 0xd8 2228c2ecf20Sopenharmony_ci#define ITV_ME_SEQNO_MASK 0x00ff0000 /* ME should update every ~200ms */ 2238c2ecf20Sopenharmony_ci#define ITV_ME_SEQNO_SHIFT (16) 2248c2ecf20Sopenharmony_ci#define ITV_MCH_TEMP_MASK 0x0000ff00 2258c2ecf20Sopenharmony_ci#define ITV_MCH_TEMP_SHIFT (8) 2268c2ecf20Sopenharmony_ci#define ITV_PCH_TEMP_MASK 0x000000ff 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define thm_readb(off) readb(ips->regmap + (off)) 2298c2ecf20Sopenharmony_ci#define thm_readw(off) readw(ips->regmap + (off)) 2308c2ecf20Sopenharmony_ci#define thm_readl(off) readl(ips->regmap + (off)) 2318c2ecf20Sopenharmony_ci#define thm_readq(off) readq(ips->regmap + (off)) 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define thm_writeb(off, val) writeb((val), ips->regmap + (off)) 2348c2ecf20Sopenharmony_ci#define thm_writew(off, val) writew((val), ips->regmap + (off)) 2358c2ecf20Sopenharmony_ci#define thm_writel(off, val) writel((val), ips->regmap + (off)) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const int IPS_ADJUST_PERIOD = 5000; /* ms */ 2388c2ecf20Sopenharmony_cistatic bool late_i915_load = false; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* For initial average collection */ 2418c2ecf20Sopenharmony_cistatic const int IPS_SAMPLE_PERIOD = 200; /* ms */ 2428c2ecf20Sopenharmony_cistatic const int IPS_SAMPLE_WINDOW = 5000; /* 5s moving window of samples */ 2438c2ecf20Sopenharmony_ci#define IPS_SAMPLE_COUNT (IPS_SAMPLE_WINDOW / IPS_SAMPLE_PERIOD) 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* Per-SKU limits */ 2468c2ecf20Sopenharmony_cistruct ips_mcp_limits { 2478c2ecf20Sopenharmony_ci int mcp_power_limit; /* mW units */ 2488c2ecf20Sopenharmony_ci int core_power_limit; 2498c2ecf20Sopenharmony_ci int mch_power_limit; 2508c2ecf20Sopenharmony_ci int core_temp_limit; /* degrees C */ 2518c2ecf20Sopenharmony_ci int mch_temp_limit; 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Max temps are -10 degrees C to avoid PROCHOT# */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic struct ips_mcp_limits ips_sv_limits = { 2578c2ecf20Sopenharmony_ci .mcp_power_limit = 35000, 2588c2ecf20Sopenharmony_ci .core_power_limit = 29000, 2598c2ecf20Sopenharmony_ci .mch_power_limit = 20000, 2608c2ecf20Sopenharmony_ci .core_temp_limit = 95, 2618c2ecf20Sopenharmony_ci .mch_temp_limit = 90 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic struct ips_mcp_limits ips_lv_limits = { 2658c2ecf20Sopenharmony_ci .mcp_power_limit = 25000, 2668c2ecf20Sopenharmony_ci .core_power_limit = 21000, 2678c2ecf20Sopenharmony_ci .mch_power_limit = 13000, 2688c2ecf20Sopenharmony_ci .core_temp_limit = 95, 2698c2ecf20Sopenharmony_ci .mch_temp_limit = 90 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic struct ips_mcp_limits ips_ulv_limits = { 2738c2ecf20Sopenharmony_ci .mcp_power_limit = 18000, 2748c2ecf20Sopenharmony_ci .core_power_limit = 14000, 2758c2ecf20Sopenharmony_ci .mch_power_limit = 11000, 2768c2ecf20Sopenharmony_ci .core_temp_limit = 95, 2778c2ecf20Sopenharmony_ci .mch_temp_limit = 90 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistruct ips_driver { 2818c2ecf20Sopenharmony_ci struct device *dev; 2828c2ecf20Sopenharmony_ci void __iomem *regmap; 2838c2ecf20Sopenharmony_ci int irq; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci struct task_struct *monitor; 2868c2ecf20Sopenharmony_ci struct task_struct *adjust; 2878c2ecf20Sopenharmony_ci struct dentry *debug_root; 2888c2ecf20Sopenharmony_ci struct timer_list timer; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Average CPU core temps (all averages in .01 degrees C for precision) */ 2918c2ecf20Sopenharmony_ci u16 ctv1_avg_temp; 2928c2ecf20Sopenharmony_ci u16 ctv2_avg_temp; 2938c2ecf20Sopenharmony_ci /* GMCH average */ 2948c2ecf20Sopenharmony_ci u16 mch_avg_temp; 2958c2ecf20Sopenharmony_ci /* Average for the CPU (both cores?) */ 2968c2ecf20Sopenharmony_ci u16 mcp_avg_temp; 2978c2ecf20Sopenharmony_ci /* Average power consumption (in mW) */ 2988c2ecf20Sopenharmony_ci u32 cpu_avg_power; 2998c2ecf20Sopenharmony_ci u32 mch_avg_power; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Offset values */ 3028c2ecf20Sopenharmony_ci u16 cta_val; 3038c2ecf20Sopenharmony_ci u16 pta_val; 3048c2ecf20Sopenharmony_ci u16 mgta_val; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Maximums & prefs, protected by turbo status lock */ 3078c2ecf20Sopenharmony_ci spinlock_t turbo_status_lock; 3088c2ecf20Sopenharmony_ci u16 mcp_temp_limit; 3098c2ecf20Sopenharmony_ci u16 mcp_power_limit; 3108c2ecf20Sopenharmony_ci u16 core_power_limit; 3118c2ecf20Sopenharmony_ci u16 mch_power_limit; 3128c2ecf20Sopenharmony_ci bool cpu_turbo_enabled; 3138c2ecf20Sopenharmony_ci bool __cpu_turbo_on; 3148c2ecf20Sopenharmony_ci bool gpu_turbo_enabled; 3158c2ecf20Sopenharmony_ci bool __gpu_turbo_on; 3168c2ecf20Sopenharmony_ci bool gpu_preferred; 3178c2ecf20Sopenharmony_ci bool poll_turbo_status; 3188c2ecf20Sopenharmony_ci bool second_cpu; 3198c2ecf20Sopenharmony_ci bool turbo_toggle_allowed; 3208c2ecf20Sopenharmony_ci struct ips_mcp_limits *limits; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Optional MCH interfaces for if i915 is in use */ 3238c2ecf20Sopenharmony_ci unsigned long (*read_mch_val)(void); 3248c2ecf20Sopenharmony_ci bool (*gpu_raise)(void); 3258c2ecf20Sopenharmony_ci bool (*gpu_lower)(void); 3268c2ecf20Sopenharmony_ci bool (*gpu_busy)(void); 3278c2ecf20Sopenharmony_ci bool (*gpu_turbo_disable)(void); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* For restoration at unload */ 3308c2ecf20Sopenharmony_ci u64 orig_turbo_limit; 3318c2ecf20Sopenharmony_ci u64 orig_turbo_ratios; 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic bool 3358c2ecf20Sopenharmony_ciips_gpu_turbo_enabled(struct ips_driver *ips); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/** 3388c2ecf20Sopenharmony_ci * ips_cpu_busy - is CPU busy? 3398c2ecf20Sopenharmony_ci * @ips: IPS driver struct 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * Check CPU for load to see whether we should increase its thermal budget. 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * RETURNS: 3448c2ecf20Sopenharmony_ci * True if the CPU could use more power, false otherwise. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic bool ips_cpu_busy(struct ips_driver *ips) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci if ((avenrun[0] >> FSHIFT) > 1) 3498c2ecf20Sopenharmony_ci return true; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return false; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/** 3558c2ecf20Sopenharmony_ci * ips_cpu_raise - raise CPU power clamp 3568c2ecf20Sopenharmony_ci * @ips: IPS driver struct 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * Raise the CPU power clamp by %IPS_CPU_STEP, in accordance with TDP for 3598c2ecf20Sopenharmony_ci * this platform. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * We do this by adjusting the TURBO_POWER_CURRENT_LIMIT MSR upwards (as 3628c2ecf20Sopenharmony_ci * long as we haven't hit the TDP limit for the SKU). 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_cistatic void ips_cpu_raise(struct ips_driver *ips) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci u64 turbo_override; 3678c2ecf20Sopenharmony_ci u16 cur_tdp_limit, new_tdp_limit; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!ips->cpu_turbo_enabled) 3708c2ecf20Sopenharmony_ci return; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci cur_tdp_limit = turbo_override & TURBO_TDP_MASK; 3758c2ecf20Sopenharmony_ci new_tdp_limit = cur_tdp_limit + 8; /* 1W increase */ 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Clamp to SKU TDP limit */ 3788c2ecf20Sopenharmony_ci if (((new_tdp_limit * 10) / 8) > ips->core_power_limit) 3798c2ecf20Sopenharmony_ci new_tdp_limit = cur_tdp_limit; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci thm_writew(THM_MPCPC, (new_tdp_limit * 10) / 8); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN; 3848c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci turbo_override &= ~TURBO_TDP_MASK; 3878c2ecf20Sopenharmony_ci turbo_override |= new_tdp_limit; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * ips_cpu_lower - lower CPU power clamp 3948c2ecf20Sopenharmony_ci * @ips: IPS driver struct 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * Lower CPU power clamp b %IPS_CPU_STEP if possible. 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * We do this by adjusting the TURBO_POWER_CURRENT_LIMIT MSR down, going 3998c2ecf20Sopenharmony_ci * as low as the platform limits will allow (though we could go lower there 4008c2ecf20Sopenharmony_ci * wouldn't be much point). 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic void ips_cpu_lower(struct ips_driver *ips) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci u64 turbo_override; 4058c2ecf20Sopenharmony_ci u16 cur_limit, new_limit; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci cur_limit = turbo_override & TURBO_TDP_MASK; 4108c2ecf20Sopenharmony_ci new_limit = cur_limit - 8; /* 1W decrease */ 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Clamp to SKU TDP limit */ 4138c2ecf20Sopenharmony_ci if (new_limit < (ips->orig_turbo_limit & TURBO_TDP_MASK)) 4148c2ecf20Sopenharmony_ci new_limit = ips->orig_turbo_limit & TURBO_TDP_MASK; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci thm_writew(THM_MPCPC, (new_limit * 10) / 8); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN; 4198c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci turbo_override &= ~TURBO_TDP_MASK; 4228c2ecf20Sopenharmony_ci turbo_override |= new_limit; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * do_enable_cpu_turbo - internal turbo enable function 4298c2ecf20Sopenharmony_ci * @data: unused 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * Internal function for actually updating MSRs. When we enable/disable 4328c2ecf20Sopenharmony_ci * turbo, we need to do it on each CPU; this function is the one called 4338c2ecf20Sopenharmony_ci * by on_each_cpu() when needed. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic void do_enable_cpu_turbo(void *data) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci u64 perf_ctl; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci rdmsrl(IA32_PERF_CTL, perf_ctl); 4408c2ecf20Sopenharmony_ci if (perf_ctl & IA32_PERF_TURBO_DIS) { 4418c2ecf20Sopenharmony_ci perf_ctl &= ~IA32_PERF_TURBO_DIS; 4428c2ecf20Sopenharmony_ci wrmsrl(IA32_PERF_CTL, perf_ctl); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * ips_enable_cpu_turbo - enable turbo mode on all CPUs 4488c2ecf20Sopenharmony_ci * @ips: IPS driver struct 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * Enable turbo mode by clearing the disable bit in IA32_PERF_CTL on 4518c2ecf20Sopenharmony_ci * all logical threads. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic void ips_enable_cpu_turbo(struct ips_driver *ips) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci /* Already on, no need to mess with MSRs */ 4568c2ecf20Sopenharmony_ci if (ips->__cpu_turbo_on) 4578c2ecf20Sopenharmony_ci return; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (ips->turbo_toggle_allowed) 4608c2ecf20Sopenharmony_ci on_each_cpu(do_enable_cpu_turbo, ips, 1); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ips->__cpu_turbo_on = true; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/** 4668c2ecf20Sopenharmony_ci * do_disable_cpu_turbo - internal turbo disable function 4678c2ecf20Sopenharmony_ci * @data: unused 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Internal function for actually updating MSRs. When we enable/disable 4708c2ecf20Sopenharmony_ci * turbo, we need to do it on each CPU; this function is the one called 4718c2ecf20Sopenharmony_ci * by on_each_cpu() when needed. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic void do_disable_cpu_turbo(void *data) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci u64 perf_ctl; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci rdmsrl(IA32_PERF_CTL, perf_ctl); 4788c2ecf20Sopenharmony_ci if (!(perf_ctl & IA32_PERF_TURBO_DIS)) { 4798c2ecf20Sopenharmony_ci perf_ctl |= IA32_PERF_TURBO_DIS; 4808c2ecf20Sopenharmony_ci wrmsrl(IA32_PERF_CTL, perf_ctl); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/** 4858c2ecf20Sopenharmony_ci * ips_disable_cpu_turbo - disable turbo mode on all CPUs 4868c2ecf20Sopenharmony_ci * @ips: IPS driver struct 4878c2ecf20Sopenharmony_ci * 4888c2ecf20Sopenharmony_ci * Disable turbo mode by setting the disable bit in IA32_PERF_CTL on 4898c2ecf20Sopenharmony_ci * all logical threads. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic void ips_disable_cpu_turbo(struct ips_driver *ips) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci /* Already off, leave it */ 4948c2ecf20Sopenharmony_ci if (!ips->__cpu_turbo_on) 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (ips->turbo_toggle_allowed) 4988c2ecf20Sopenharmony_ci on_each_cpu(do_disable_cpu_turbo, ips, 1); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ips->__cpu_turbo_on = false; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * ips_gpu_busy - is GPU busy? 5058c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * Check GPU for load to see whether we should increase its thermal budget. 5088c2ecf20Sopenharmony_ci * We need to call into the i915 driver in this case. 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * RETURNS: 5118c2ecf20Sopenharmony_ci * True if the GPU could use more power, false otherwise. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic bool ips_gpu_busy(struct ips_driver *ips) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci if (!ips_gpu_turbo_enabled(ips)) 5168c2ecf20Sopenharmony_ci return false; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ips->gpu_busy(); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci/** 5228c2ecf20Sopenharmony_ci * ips_gpu_raise - raise GPU power clamp 5238c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5248c2ecf20Sopenharmony_ci * 5258c2ecf20Sopenharmony_ci * Raise the GPU frequency/power if possible. We need to call into the 5268c2ecf20Sopenharmony_ci * i915 driver in this case. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_cistatic void ips_gpu_raise(struct ips_driver *ips) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci if (!ips_gpu_turbo_enabled(ips)) 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (!ips->gpu_raise()) 5348c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = false; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/** 5408c2ecf20Sopenharmony_ci * ips_gpu_lower - lower GPU power clamp 5418c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5428c2ecf20Sopenharmony_ci * 5438c2ecf20Sopenharmony_ci * Lower GPU frequency/power if possible. Need to call i915. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_cistatic void ips_gpu_lower(struct ips_driver *ips) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci if (!ips_gpu_turbo_enabled(ips)) 5488c2ecf20Sopenharmony_ci return; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!ips->gpu_lower()) 5518c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = false; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/** 5578c2ecf20Sopenharmony_ci * ips_enable_gpu_turbo - notify the gfx driver turbo is available 5588c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * Call into the graphics driver indicating that it can safely use 5618c2ecf20Sopenharmony_ci * turbo mode. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_cistatic void ips_enable_gpu_turbo(struct ips_driver *ips) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci if (ips->__gpu_turbo_on) 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci ips->__gpu_turbo_on = true; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/** 5718c2ecf20Sopenharmony_ci * ips_disable_gpu_turbo - notify the gfx driver to disable turbo mode 5728c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * Request that the graphics driver disable turbo mode. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_cistatic void ips_disable_gpu_turbo(struct ips_driver *ips) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci /* Avoid calling i915 if turbo is already disabled */ 5798c2ecf20Sopenharmony_ci if (!ips->__gpu_turbo_on) 5808c2ecf20Sopenharmony_ci return; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (!ips->gpu_turbo_disable()) 5838c2ecf20Sopenharmony_ci dev_err(ips->dev, "failed to disable graphics turbo\n"); 5848c2ecf20Sopenharmony_ci else 5858c2ecf20Sopenharmony_ci ips->__gpu_turbo_on = false; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * mcp_exceeded - check whether we're outside our thermal & power limits 5908c2ecf20Sopenharmony_ci * @ips: IPS driver struct 5918c2ecf20Sopenharmony_ci * 5928c2ecf20Sopenharmony_ci * Check whether the MCP is over its thermal or power budget. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_cistatic bool mcp_exceeded(struct ips_driver *ips) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci unsigned long flags; 5978c2ecf20Sopenharmony_ci bool ret = false; 5988c2ecf20Sopenharmony_ci u32 temp_limit; 5998c2ecf20Sopenharmony_ci u32 avg_power; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci spin_lock_irqsave(&ips->turbo_status_lock, flags); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci temp_limit = ips->mcp_temp_limit * 100; 6048c2ecf20Sopenharmony_ci if (ips->mcp_avg_temp > temp_limit) 6058c2ecf20Sopenharmony_ci ret = true; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci avg_power = ips->cpu_avg_power + ips->mch_avg_power; 6088c2ecf20Sopenharmony_ci if (avg_power > ips->mcp_power_limit) 6098c2ecf20Sopenharmony_ci ret = true; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ips->turbo_status_lock, flags); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/** 6178c2ecf20Sopenharmony_ci * cpu_exceeded - check whether a CPU core is outside its limits 6188c2ecf20Sopenharmony_ci * @ips: IPS driver struct 6198c2ecf20Sopenharmony_ci * @cpu: CPU number to check 6208c2ecf20Sopenharmony_ci * 6218c2ecf20Sopenharmony_ci * Check a given CPU's average temp or power is over its limit. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistatic bool cpu_exceeded(struct ips_driver *ips, int cpu) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci unsigned long flags; 6268c2ecf20Sopenharmony_ci int avg; 6278c2ecf20Sopenharmony_ci bool ret = false; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci spin_lock_irqsave(&ips->turbo_status_lock, flags); 6308c2ecf20Sopenharmony_ci avg = cpu ? ips->ctv2_avg_temp : ips->ctv1_avg_temp; 6318c2ecf20Sopenharmony_ci if (avg > (ips->limits->core_temp_limit * 100)) 6328c2ecf20Sopenharmony_ci ret = true; 6338c2ecf20Sopenharmony_ci if (ips->cpu_avg_power > ips->core_power_limit * 100) 6348c2ecf20Sopenharmony_ci ret = true; 6358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ips->turbo_status_lock, flags); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (ret) 6388c2ecf20Sopenharmony_ci dev_info(ips->dev, "CPU power or thermal limit exceeded\n"); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/** 6448c2ecf20Sopenharmony_ci * mch_exceeded - check whether the GPU is over budget 6458c2ecf20Sopenharmony_ci * @ips: IPS driver struct 6468c2ecf20Sopenharmony_ci * 6478c2ecf20Sopenharmony_ci * Check the MCH temp & power against their maximums. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_cistatic bool mch_exceeded(struct ips_driver *ips) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci unsigned long flags; 6528c2ecf20Sopenharmony_ci bool ret = false; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci spin_lock_irqsave(&ips->turbo_status_lock, flags); 6558c2ecf20Sopenharmony_ci if (ips->mch_avg_temp > (ips->limits->mch_temp_limit * 100)) 6568c2ecf20Sopenharmony_ci ret = true; 6578c2ecf20Sopenharmony_ci if (ips->mch_avg_power > ips->mch_power_limit) 6588c2ecf20Sopenharmony_ci ret = true; 6598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ips->turbo_status_lock, flags); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/** 6658c2ecf20Sopenharmony_ci * verify_limits - verify BIOS provided limits 6668c2ecf20Sopenharmony_ci * @ips: IPS structure 6678c2ecf20Sopenharmony_ci * 6688c2ecf20Sopenharmony_ci * BIOS can optionally provide non-default limits for power and temp. Check 6698c2ecf20Sopenharmony_ci * them here and use the defaults if the BIOS values are not provided or 6708c2ecf20Sopenharmony_ci * are otherwise unusable. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_cistatic void verify_limits(struct ips_driver *ips) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci if (ips->mcp_power_limit < ips->limits->mcp_power_limit || 6758c2ecf20Sopenharmony_ci ips->mcp_power_limit > 35000) 6768c2ecf20Sopenharmony_ci ips->mcp_power_limit = ips->limits->mcp_power_limit; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (ips->mcp_temp_limit < ips->limits->core_temp_limit || 6798c2ecf20Sopenharmony_ci ips->mcp_temp_limit < ips->limits->mch_temp_limit || 6808c2ecf20Sopenharmony_ci ips->mcp_temp_limit > 150) 6818c2ecf20Sopenharmony_ci ips->mcp_temp_limit = min(ips->limits->core_temp_limit, 6828c2ecf20Sopenharmony_ci ips->limits->mch_temp_limit); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/** 6868c2ecf20Sopenharmony_ci * update_turbo_limits - get various limits & settings from regs 6878c2ecf20Sopenharmony_ci * @ips: IPS driver struct 6888c2ecf20Sopenharmony_ci * 6898c2ecf20Sopenharmony_ci * Update the IPS power & temp limits, along with turbo enable flags, 6908c2ecf20Sopenharmony_ci * based on latest register contents. 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci * Used at init time and for runtime BIOS support, which requires polling 6938c2ecf20Sopenharmony_ci * the regs for updates (as a result of AC->DC transition for example). 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * LOCKING: 6968c2ecf20Sopenharmony_ci * Caller must hold turbo_status_lock (outside of init) 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_cistatic void update_turbo_limits(struct ips_driver *ips) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci u32 hts = thm_readl(THM_HTS); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci ips->cpu_turbo_enabled = !(hts & HTS_PCTD_DIS); 7038c2ecf20Sopenharmony_ci /* 7048c2ecf20Sopenharmony_ci * Disable turbo for now, until we can figure out why the power figures 7058c2ecf20Sopenharmony_ci * are wrong 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci ips->cpu_turbo_enabled = false; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (ips->gpu_busy) 7108c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = !(hts & HTS_GTD_DIS); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci ips->core_power_limit = thm_readw(THM_MPCPC); 7138c2ecf20Sopenharmony_ci ips->mch_power_limit = thm_readw(THM_MMGPC); 7148c2ecf20Sopenharmony_ci ips->mcp_temp_limit = thm_readw(THM_PTL); 7158c2ecf20Sopenharmony_ci ips->mcp_power_limit = thm_readw(THM_MPPC); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci verify_limits(ips); 7188c2ecf20Sopenharmony_ci /* Ignore BIOS CPU vs GPU pref */ 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/** 7228c2ecf20Sopenharmony_ci * ips_adjust - adjust power clamp based on thermal state 7238c2ecf20Sopenharmony_ci * @data: ips driver structure 7248c2ecf20Sopenharmony_ci * 7258c2ecf20Sopenharmony_ci * Wake up every 5s or so and check whether we should adjust the power clamp. 7268c2ecf20Sopenharmony_ci * Check CPU and GPU load to determine which needs adjustment. There are 7278c2ecf20Sopenharmony_ci * several things to consider here: 7288c2ecf20Sopenharmony_ci * - do we need to adjust up or down? 7298c2ecf20Sopenharmony_ci * - is CPU busy? 7308c2ecf20Sopenharmony_ci * - is GPU busy? 7318c2ecf20Sopenharmony_ci * - is CPU in turbo? 7328c2ecf20Sopenharmony_ci * - is GPU in turbo? 7338c2ecf20Sopenharmony_ci * - is CPU or GPU preferred? (CPU is default) 7348c2ecf20Sopenharmony_ci * 7358c2ecf20Sopenharmony_ci * So, given the above, we do the following: 7368c2ecf20Sopenharmony_ci * - up (TDP available) 7378c2ecf20Sopenharmony_ci * - CPU not busy, GPU not busy - nothing 7388c2ecf20Sopenharmony_ci * - CPU busy, GPU not busy - adjust CPU up 7398c2ecf20Sopenharmony_ci * - CPU not busy, GPU busy - adjust GPU up 7408c2ecf20Sopenharmony_ci * - CPU busy, GPU busy - adjust preferred unit up, taking headroom from 7418c2ecf20Sopenharmony_ci * non-preferred unit if necessary 7428c2ecf20Sopenharmony_ci * - down (at TDP limit) 7438c2ecf20Sopenharmony_ci * - adjust both CPU and GPU down if possible 7448c2ecf20Sopenharmony_ci * 7458c2ecf20Sopenharmony_ci cpu+ gpu+ cpu+gpu- cpu-gpu+ cpu-gpu- 7468c2ecf20Sopenharmony_cicpu < gpu < cpu+gpu+ cpu+ gpu+ nothing 7478c2ecf20Sopenharmony_cicpu < gpu >= cpu+gpu-(mcp<) cpu+gpu-(mcp<) gpu- gpu- 7488c2ecf20Sopenharmony_cicpu >= gpu < cpu-gpu+(mcp<) cpu- cpu-gpu+(mcp<) cpu- 7498c2ecf20Sopenharmony_cicpu >= gpu >= cpu-gpu- cpu-gpu- cpu-gpu- cpu-gpu- 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistatic int ips_adjust(void *data) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct ips_driver *ips = data; 7558c2ecf20Sopenharmony_ci unsigned long flags; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci dev_dbg(ips->dev, "starting ips-adjust thread\n"); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* 7608c2ecf20Sopenharmony_ci * Adjust CPU and GPU clamps every 5s if needed. Doing it more 7618c2ecf20Sopenharmony_ci * often isn't recommended due to ME interaction. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ci do { 7648c2ecf20Sopenharmony_ci bool cpu_busy = ips_cpu_busy(ips); 7658c2ecf20Sopenharmony_ci bool gpu_busy = ips_gpu_busy(ips); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci spin_lock_irqsave(&ips->turbo_status_lock, flags); 7688c2ecf20Sopenharmony_ci if (ips->poll_turbo_status) 7698c2ecf20Sopenharmony_ci update_turbo_limits(ips); 7708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ips->turbo_status_lock, flags); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* Update turbo status if necessary */ 7738c2ecf20Sopenharmony_ci if (ips->cpu_turbo_enabled) 7748c2ecf20Sopenharmony_ci ips_enable_cpu_turbo(ips); 7758c2ecf20Sopenharmony_ci else 7768c2ecf20Sopenharmony_ci ips_disable_cpu_turbo(ips); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (ips->gpu_turbo_enabled) 7798c2ecf20Sopenharmony_ci ips_enable_gpu_turbo(ips); 7808c2ecf20Sopenharmony_ci else 7818c2ecf20Sopenharmony_ci ips_disable_gpu_turbo(ips); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* We're outside our comfort zone, crank them down */ 7848c2ecf20Sopenharmony_ci if (mcp_exceeded(ips)) { 7858c2ecf20Sopenharmony_ci ips_cpu_lower(ips); 7868c2ecf20Sopenharmony_ci ips_gpu_lower(ips); 7878c2ecf20Sopenharmony_ci goto sleep; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (!cpu_exceeded(ips, 0) && cpu_busy) 7918c2ecf20Sopenharmony_ci ips_cpu_raise(ips); 7928c2ecf20Sopenharmony_ci else 7938c2ecf20Sopenharmony_ci ips_cpu_lower(ips); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!mch_exceeded(ips) && gpu_busy) 7968c2ecf20Sopenharmony_ci ips_gpu_raise(ips); 7978c2ecf20Sopenharmony_ci else 7988c2ecf20Sopenharmony_ci ips_gpu_lower(ips); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cisleep: 8018c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD)); 8028c2ecf20Sopenharmony_ci } while (!kthread_should_stop()); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci dev_dbg(ips->dev, "ips-adjust thread stopped\n"); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return 0; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci/* 8108c2ecf20Sopenharmony_ci * Helpers for reading out temp/power values and calculating their 8118c2ecf20Sopenharmony_ci * averages for the decision making and monitoring functions. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic u16 calc_avg_temp(struct ips_driver *ips, u16 *array) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci u64 total = 0; 8178c2ecf20Sopenharmony_ci int i; 8188c2ecf20Sopenharmony_ci u16 avg; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci for (i = 0; i < IPS_SAMPLE_COUNT; i++) 8218c2ecf20Sopenharmony_ci total += (u64)(array[i] * 100); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci do_div(total, IPS_SAMPLE_COUNT); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci avg = (u16)total; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return avg; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic u16 read_mgtv(struct ips_driver *ips) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci u16 ret; 8338c2ecf20Sopenharmony_ci u64 slope, offset; 8348c2ecf20Sopenharmony_ci u64 val; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci val = thm_readq(THM_MGTV); 8378c2ecf20Sopenharmony_ci val = (val & TV_MASK) >> TV_SHIFT; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci slope = offset = thm_readw(THM_MGTA); 8408c2ecf20Sopenharmony_ci slope = (slope & MGTA_SLOPE_MASK) >> MGTA_SLOPE_SHIFT; 8418c2ecf20Sopenharmony_ci offset = offset & MGTA_OFFSET_MASK; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci ret = ((val * slope + 0x40) >> 7) + offset; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return 0; /* MCH temp reporting buggy */ 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic u16 read_ptv(struct ips_driver *ips) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci u16 val; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci val = thm_readw(THM_PTV) & PTV_MASK; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci return val; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic u16 read_ctv(struct ips_driver *ips, int cpu) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci int reg = cpu ? THM_CTV2 : THM_CTV1; 8608c2ecf20Sopenharmony_ci u16 val; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci val = thm_readw(reg); 8638c2ecf20Sopenharmony_ci if (!(val & CTV_TEMP_ERROR)) 8648c2ecf20Sopenharmony_ci val = (val) >> 6; /* discard fractional component */ 8658c2ecf20Sopenharmony_ci else 8668c2ecf20Sopenharmony_ci val = 0; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return val; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic u32 get_cpu_power(struct ips_driver *ips, u32 *last, int period) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci u32 val; 8748c2ecf20Sopenharmony_ci u32 ret; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* 8778c2ecf20Sopenharmony_ci * CEC is in joules/65535. Take difference over time to 8788c2ecf20Sopenharmony_ci * get watts. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ci val = thm_readl(THM_CEC); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* period is in ms and we want mW */ 8838c2ecf20Sopenharmony_ci ret = (((val - *last) * 1000) / period); 8848c2ecf20Sopenharmony_ci ret = (ret * 1000) / 65535; 8858c2ecf20Sopenharmony_ci *last = val; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic const u16 temp_decay_factor = 2; 8918c2ecf20Sopenharmony_cistatic u16 update_average_temp(u16 avg, u16 val) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci u16 ret; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* Multiply by 100 for extra precision */ 8968c2ecf20Sopenharmony_ci ret = (val * 100 / temp_decay_factor) + 8978c2ecf20Sopenharmony_ci (((temp_decay_factor - 1) * avg) / temp_decay_factor); 8988c2ecf20Sopenharmony_ci return ret; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic const u16 power_decay_factor = 2; 9028c2ecf20Sopenharmony_cistatic u16 update_average_power(u32 avg, u32 val) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci u32 ret; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci ret = (val / power_decay_factor) + 9078c2ecf20Sopenharmony_ci (((power_decay_factor - 1) * avg) / power_decay_factor); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return ret; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic u32 calc_avg_power(struct ips_driver *ips, u32 *array) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci u64 total = 0; 9158c2ecf20Sopenharmony_ci u32 avg; 9168c2ecf20Sopenharmony_ci int i; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci for (i = 0; i < IPS_SAMPLE_COUNT; i++) 9198c2ecf20Sopenharmony_ci total += array[i]; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci do_div(total, IPS_SAMPLE_COUNT); 9228c2ecf20Sopenharmony_ci avg = (u32)total; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return avg; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic void monitor_timeout(struct timer_list *t) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct ips_driver *ips = from_timer(ips, t, timer); 9308c2ecf20Sopenharmony_ci wake_up_process(ips->monitor); 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci/** 9348c2ecf20Sopenharmony_ci * ips_monitor - temp/power monitoring thread 9358c2ecf20Sopenharmony_ci * @data: ips driver structure 9368c2ecf20Sopenharmony_ci * 9378c2ecf20Sopenharmony_ci * This is the main function for the IPS driver. It monitors power and 9388c2ecf20Sopenharmony_ci * tempurature in the MCP and adjusts CPU and GPU power clams accordingly. 9398c2ecf20Sopenharmony_ci * 9408c2ecf20Sopenharmony_ci * We keep a 5s moving average of power consumption and tempurature. Using 9418c2ecf20Sopenharmony_ci * that data, along with CPU vs GPU preference, we adjust the power clamps 9428c2ecf20Sopenharmony_ci * up or down. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_cistatic int ips_monitor(void *data) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct ips_driver *ips = data; 9478c2ecf20Sopenharmony_ci unsigned long seqno_timestamp, expire, last_msecs, last_sample_period; 9488c2ecf20Sopenharmony_ci int i; 9498c2ecf20Sopenharmony_ci u32 *cpu_samples, *mchp_samples, old_cpu_power; 9508c2ecf20Sopenharmony_ci u16 *mcp_samples, *ctv1_samples, *ctv2_samples, *mch_samples; 9518c2ecf20Sopenharmony_ci u8 cur_seqno, last_seqno; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci mcp_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); 9548c2ecf20Sopenharmony_ci ctv1_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); 9558c2ecf20Sopenharmony_ci ctv2_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); 9568c2ecf20Sopenharmony_ci mch_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); 9578c2ecf20Sopenharmony_ci cpu_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u32), GFP_KERNEL); 9588c2ecf20Sopenharmony_ci mchp_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u32), GFP_KERNEL); 9598c2ecf20Sopenharmony_ci if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || 9608c2ecf20Sopenharmony_ci !cpu_samples || !mchp_samples) { 9618c2ecf20Sopenharmony_ci dev_err(ips->dev, 9628c2ecf20Sopenharmony_ci "failed to allocate sample array, ips disabled\n"); 9638c2ecf20Sopenharmony_ci kfree(mcp_samples); 9648c2ecf20Sopenharmony_ci kfree(ctv1_samples); 9658c2ecf20Sopenharmony_ci kfree(ctv2_samples); 9668c2ecf20Sopenharmony_ci kfree(mch_samples); 9678c2ecf20Sopenharmony_ci kfree(cpu_samples); 9688c2ecf20Sopenharmony_ci kfree(mchp_samples); 9698c2ecf20Sopenharmony_ci return -ENOMEM; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci last_seqno = (thm_readl(THM_ITV) & ITV_ME_SEQNO_MASK) >> 9738c2ecf20Sopenharmony_ci ITV_ME_SEQNO_SHIFT; 9748c2ecf20Sopenharmony_ci seqno_timestamp = get_jiffies_64(); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci old_cpu_power = thm_readl(THM_CEC); 9778c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* Collect an initial average */ 9808c2ecf20Sopenharmony_ci for (i = 0; i < IPS_SAMPLE_COUNT; i++) { 9818c2ecf20Sopenharmony_ci u32 mchp, cpu_power; 9828c2ecf20Sopenharmony_ci u16 val; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci mcp_samples[i] = read_ptv(ips); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci val = read_ctv(ips, 0); 9878c2ecf20Sopenharmony_ci ctv1_samples[i] = val; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci val = read_ctv(ips, 1); 9908c2ecf20Sopenharmony_ci ctv2_samples[i] = val; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci val = read_mgtv(ips); 9938c2ecf20Sopenharmony_ci mch_samples[i] = val; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci cpu_power = get_cpu_power(ips, &old_cpu_power, 9968c2ecf20Sopenharmony_ci IPS_SAMPLE_PERIOD); 9978c2ecf20Sopenharmony_ci cpu_samples[i] = cpu_power; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (ips->read_mch_val) { 10008c2ecf20Sopenharmony_ci mchp = ips->read_mch_val(); 10018c2ecf20Sopenharmony_ci mchp_samples[i] = mchp; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); 10058c2ecf20Sopenharmony_ci if (kthread_should_stop()) 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci ips->mcp_avg_temp = calc_avg_temp(ips, mcp_samples); 10108c2ecf20Sopenharmony_ci ips->ctv1_avg_temp = calc_avg_temp(ips, ctv1_samples); 10118c2ecf20Sopenharmony_ci ips->ctv2_avg_temp = calc_avg_temp(ips, ctv2_samples); 10128c2ecf20Sopenharmony_ci ips->mch_avg_temp = calc_avg_temp(ips, mch_samples); 10138c2ecf20Sopenharmony_ci ips->cpu_avg_power = calc_avg_power(ips, cpu_samples); 10148c2ecf20Sopenharmony_ci ips->mch_avg_power = calc_avg_power(ips, mchp_samples); 10158c2ecf20Sopenharmony_ci kfree(mcp_samples); 10168c2ecf20Sopenharmony_ci kfree(ctv1_samples); 10178c2ecf20Sopenharmony_ci kfree(ctv2_samples); 10188c2ecf20Sopenharmony_ci kfree(mch_samples); 10198c2ecf20Sopenharmony_ci kfree(cpu_samples); 10208c2ecf20Sopenharmony_ci kfree(mchp_samples); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* Start the adjustment thread now that we have data */ 10238c2ecf20Sopenharmony_ci wake_up_process(ips->adjust); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* 10268c2ecf20Sopenharmony_ci * Ok, now we have an initial avg. From here on out, we track the 10278c2ecf20Sopenharmony_ci * running avg using a decaying average calculation. This allows 10288c2ecf20Sopenharmony_ci * us to reduce the sample frequency if the CPU and GPU are idle. 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ci old_cpu_power = thm_readl(THM_CEC); 10318c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); 10328c2ecf20Sopenharmony_ci last_sample_period = IPS_SAMPLE_PERIOD; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci timer_setup(&ips->timer, monitor_timeout, TIMER_DEFERRABLE); 10358c2ecf20Sopenharmony_ci do { 10368c2ecf20Sopenharmony_ci u32 cpu_val, mch_val; 10378c2ecf20Sopenharmony_ci u16 val; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* MCP itself */ 10408c2ecf20Sopenharmony_ci val = read_ptv(ips); 10418c2ecf20Sopenharmony_ci ips->mcp_avg_temp = update_average_temp(ips->mcp_avg_temp, val); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Processor 0 */ 10448c2ecf20Sopenharmony_ci val = read_ctv(ips, 0); 10458c2ecf20Sopenharmony_ci ips->ctv1_avg_temp = 10468c2ecf20Sopenharmony_ci update_average_temp(ips->ctv1_avg_temp, val); 10478c2ecf20Sopenharmony_ci /* Power */ 10488c2ecf20Sopenharmony_ci cpu_val = get_cpu_power(ips, &old_cpu_power, 10498c2ecf20Sopenharmony_ci last_sample_period); 10508c2ecf20Sopenharmony_ci ips->cpu_avg_power = 10518c2ecf20Sopenharmony_ci update_average_power(ips->cpu_avg_power, cpu_val); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (ips->second_cpu) { 10548c2ecf20Sopenharmony_ci /* Processor 1 */ 10558c2ecf20Sopenharmony_ci val = read_ctv(ips, 1); 10568c2ecf20Sopenharmony_ci ips->ctv2_avg_temp = 10578c2ecf20Sopenharmony_ci update_average_temp(ips->ctv2_avg_temp, val); 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* MCH */ 10618c2ecf20Sopenharmony_ci val = read_mgtv(ips); 10628c2ecf20Sopenharmony_ci ips->mch_avg_temp = update_average_temp(ips->mch_avg_temp, val); 10638c2ecf20Sopenharmony_ci /* Power */ 10648c2ecf20Sopenharmony_ci if (ips->read_mch_val) { 10658c2ecf20Sopenharmony_ci mch_val = ips->read_mch_val(); 10668c2ecf20Sopenharmony_ci ips->mch_avg_power = 10678c2ecf20Sopenharmony_ci update_average_power(ips->mch_avg_power, 10688c2ecf20Sopenharmony_ci mch_val); 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* 10728c2ecf20Sopenharmony_ci * Make sure ME is updating thermal regs. 10738c2ecf20Sopenharmony_ci * Note: 10748c2ecf20Sopenharmony_ci * If it's been more than a second since the last update, 10758c2ecf20Sopenharmony_ci * the ME is probably hung. 10768c2ecf20Sopenharmony_ci */ 10778c2ecf20Sopenharmony_ci cur_seqno = (thm_readl(THM_ITV) & ITV_ME_SEQNO_MASK) >> 10788c2ecf20Sopenharmony_ci ITV_ME_SEQNO_SHIFT; 10798c2ecf20Sopenharmony_ci if (cur_seqno == last_seqno && 10808c2ecf20Sopenharmony_ci time_after(jiffies, seqno_timestamp + HZ)) { 10818c2ecf20Sopenharmony_ci dev_warn(ips->dev, 10828c2ecf20Sopenharmony_ci "ME failed to update for more than 1s, likely hung\n"); 10838c2ecf20Sopenharmony_ci } else { 10848c2ecf20Sopenharmony_ci seqno_timestamp = get_jiffies_64(); 10858c2ecf20Sopenharmony_ci last_seqno = cur_seqno; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci last_msecs = jiffies_to_msecs(jiffies); 10898c2ecf20Sopenharmony_ci expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 10928c2ecf20Sopenharmony_ci mod_timer(&ips->timer, expire); 10938c2ecf20Sopenharmony_ci schedule(); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Calculate actual sample period for power averaging */ 10968c2ecf20Sopenharmony_ci last_sample_period = jiffies_to_msecs(jiffies) - last_msecs; 10978c2ecf20Sopenharmony_ci if (!last_sample_period) 10988c2ecf20Sopenharmony_ci last_sample_period = 1; 10998c2ecf20Sopenharmony_ci } while (!kthread_should_stop()); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci del_timer_sync(&ips->timer); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci dev_dbg(ips->dev, "ips-monitor thread stopped\n"); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci#if 0 11098c2ecf20Sopenharmony_ci#define THM_DUMPW(reg) \ 11108c2ecf20Sopenharmony_ci { \ 11118c2ecf20Sopenharmony_ci u16 val = thm_readw(reg); \ 11128c2ecf20Sopenharmony_ci dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \ 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci#define THM_DUMPL(reg) \ 11158c2ecf20Sopenharmony_ci { \ 11168c2ecf20Sopenharmony_ci u32 val = thm_readl(reg); \ 11178c2ecf20Sopenharmony_ci dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \ 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci#define THM_DUMPQ(reg) \ 11208c2ecf20Sopenharmony_ci { \ 11218c2ecf20Sopenharmony_ci u64 val = thm_readq(reg); \ 11228c2ecf20Sopenharmony_ci dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \ 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic void dump_thermal_info(struct ips_driver *ips) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci u16 ptl; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci ptl = thm_readw(THM_PTL); 11308c2ecf20Sopenharmony_ci dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci THM_DUMPW(THM_CTA); 11338c2ecf20Sopenharmony_ci THM_DUMPW(THM_TRC); 11348c2ecf20Sopenharmony_ci THM_DUMPW(THM_CTV1); 11358c2ecf20Sopenharmony_ci THM_DUMPL(THM_STS); 11368c2ecf20Sopenharmony_ci THM_DUMPW(THM_PTV); 11378c2ecf20Sopenharmony_ci THM_DUMPQ(THM_MGTV); 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci#endif 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci/** 11428c2ecf20Sopenharmony_ci * ips_irq_handler - handle temperature triggers and other IPS events 11438c2ecf20Sopenharmony_ci * @irq: irq number 11448c2ecf20Sopenharmony_ci * @arg: unused 11458c2ecf20Sopenharmony_ci * 11468c2ecf20Sopenharmony_ci * Handle temperature limit trigger events, generally by lowering the clamps. 11478c2ecf20Sopenharmony_ci * If we're at a critical limit, we clamp back to the lowest possible value 11488c2ecf20Sopenharmony_ci * to prevent emergency shutdown. 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_cistatic irqreturn_t ips_irq_handler(int irq, void *arg) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct ips_driver *ips = arg; 11538c2ecf20Sopenharmony_ci u8 tses = thm_readb(THM_TSES); 11548c2ecf20Sopenharmony_ci u8 tes = thm_readb(THM_TES); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (!tses && !tes) 11578c2ecf20Sopenharmony_ci return IRQ_NONE; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci dev_info(ips->dev, "TSES: 0x%02x\n", tses); 11608c2ecf20Sopenharmony_ci dev_info(ips->dev, "TES: 0x%02x\n", tes); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* STS update from EC? */ 11638c2ecf20Sopenharmony_ci if (tes & 1) { 11648c2ecf20Sopenharmony_ci u32 sts, tc1; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci sts = thm_readl(THM_STS); 11678c2ecf20Sopenharmony_ci tc1 = thm_readl(THM_TC1); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (sts & STS_NVV) { 11708c2ecf20Sopenharmony_ci spin_lock(&ips->turbo_status_lock); 11718c2ecf20Sopenharmony_ci ips->core_power_limit = (sts & STS_PCPL_MASK) >> 11728c2ecf20Sopenharmony_ci STS_PCPL_SHIFT; 11738c2ecf20Sopenharmony_ci ips->mch_power_limit = (sts & STS_GPL_MASK) >> 11748c2ecf20Sopenharmony_ci STS_GPL_SHIFT; 11758c2ecf20Sopenharmony_ci /* ignore EC CPU vs GPU pref */ 11768c2ecf20Sopenharmony_ci ips->cpu_turbo_enabled = !(sts & STS_PCTD_DIS); 11778c2ecf20Sopenharmony_ci /* 11788c2ecf20Sopenharmony_ci * Disable turbo for now, until we can figure 11798c2ecf20Sopenharmony_ci * out why the power figures are wrong 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci ips->cpu_turbo_enabled = false; 11828c2ecf20Sopenharmony_ci if (ips->gpu_busy) 11838c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = !(sts & STS_GTD_DIS); 11848c2ecf20Sopenharmony_ci ips->mcp_temp_limit = (sts & STS_PTL_MASK) >> 11858c2ecf20Sopenharmony_ci STS_PTL_SHIFT; 11868c2ecf20Sopenharmony_ci ips->mcp_power_limit = (tc1 & STS_PPL_MASK) >> 11878c2ecf20Sopenharmony_ci STS_PPL_SHIFT; 11888c2ecf20Sopenharmony_ci verify_limits(ips); 11898c2ecf20Sopenharmony_ci spin_unlock(&ips->turbo_status_lock); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci thm_writeb(THM_SEC, SEC_ACK); 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci thm_writeb(THM_TES, tes); 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* Thermal trip */ 11978c2ecf20Sopenharmony_ci if (tses) { 11988c2ecf20Sopenharmony_ci dev_warn(ips->dev, "thermal trip occurred, tses: 0x%04x\n", 11998c2ecf20Sopenharmony_ci tses); 12008c2ecf20Sopenharmony_ci thm_writeb(THM_TSES, tses); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci#ifndef CONFIG_DEBUG_FS 12078c2ecf20Sopenharmony_cistatic void ips_debugfs_init(struct ips_driver *ips) { return; } 12088c2ecf20Sopenharmony_cistatic void ips_debugfs_cleanup(struct ips_driver *ips) { return; } 12098c2ecf20Sopenharmony_ci#else 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/* Expose current state and limits in debugfs if possible */ 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int cpu_temp_show(struct seq_file *m, void *data) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct ips_driver *ips = m->private; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci seq_printf(m, "%d.%02d\n", ips->ctv1_avg_temp / 100, 12188c2ecf20Sopenharmony_ci ips->ctv1_avg_temp % 100); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return 0; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(cpu_temp); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cistatic int cpu_power_show(struct seq_file *m, void *data) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct ips_driver *ips = m->private; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci seq_printf(m, "%dmW\n", ips->cpu_avg_power); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci return 0; 12318c2ecf20Sopenharmony_ci} 12328c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(cpu_power); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic int cpu_clamp_show(struct seq_file *m, void *data) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci u64 turbo_override; 12378c2ecf20Sopenharmony_ci int tdp, tdc; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci tdp = (int)(turbo_override & TURBO_TDP_MASK); 12428c2ecf20Sopenharmony_ci tdc = (int)((turbo_override & TURBO_TDC_MASK) >> TURBO_TDC_SHIFT); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* Convert to .1W/A units */ 12458c2ecf20Sopenharmony_ci tdp = tdp * 10 / 8; 12468c2ecf20Sopenharmony_ci tdc = tdc * 10 / 8; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci /* Watts Amperes */ 12498c2ecf20Sopenharmony_ci seq_printf(m, "%d.%dW %d.%dA\n", tdp / 10, tdp % 10, 12508c2ecf20Sopenharmony_ci tdc / 10, tdc % 10); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(cpu_clamp); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic int mch_temp_show(struct seq_file *m, void *data) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct ips_driver *ips = m->private; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci seq_printf(m, "%d.%02d\n", ips->mch_avg_temp / 100, 12618c2ecf20Sopenharmony_ci ips->mch_avg_temp % 100); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mch_temp); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic int mch_power_show(struct seq_file *m, void *data) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct ips_driver *ips = m->private; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci seq_printf(m, "%dmW\n", ips->mch_avg_power); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci return 0; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mch_power); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic void ips_debugfs_cleanup(struct ips_driver *ips) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci debugfs_remove_recursive(ips->debug_root); 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic void ips_debugfs_init(struct ips_driver *ips) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci ips->debug_root = debugfs_create_dir("ips", NULL); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci debugfs_create_file("cpu_temp", 0444, ips->debug_root, ips, &cpu_temp_fops); 12878c2ecf20Sopenharmony_ci debugfs_create_file("cpu_power", 0444, ips->debug_root, ips, &cpu_power_fops); 12888c2ecf20Sopenharmony_ci debugfs_create_file("cpu_clamp", 0444, ips->debug_root, ips, &cpu_clamp_fops); 12898c2ecf20Sopenharmony_ci debugfs_create_file("mch_temp", 0444, ips->debug_root, ips, &mch_temp_fops); 12908c2ecf20Sopenharmony_ci debugfs_create_file("mch_power", 0444, ips->debug_root, ips, &mch_power_fops); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci/** 12958c2ecf20Sopenharmony_ci * ips_detect_cpu - detect whether CPU supports IPS 12968c2ecf20Sopenharmony_ci * 12978c2ecf20Sopenharmony_ci * Walk our list and see if we're on a supported CPU. If we find one, 12988c2ecf20Sopenharmony_ci * return the limits for it. 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_cistatic struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci u64 turbo_power, misc_en; 13038c2ecf20Sopenharmony_ci struct ips_mcp_limits *limits = NULL; 13048c2ecf20Sopenharmony_ci u16 tdp; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { 13078c2ecf20Sopenharmony_ci dev_info(ips->dev, "Non-IPS CPU detected.\n"); 13088c2ecf20Sopenharmony_ci return NULL; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci rdmsrl(IA32_MISC_ENABLE, misc_en); 13128c2ecf20Sopenharmony_ci /* 13138c2ecf20Sopenharmony_ci * If the turbo enable bit isn't set, we shouldn't try to enable/disable 13148c2ecf20Sopenharmony_ci * turbo manually or we'll get an illegal MSR access, even though 13158c2ecf20Sopenharmony_ci * turbo will still be available. 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci if (misc_en & IA32_MISC_TURBO_EN) 13188c2ecf20Sopenharmony_ci ips->turbo_toggle_allowed = true; 13198c2ecf20Sopenharmony_ci else 13208c2ecf20Sopenharmony_ci ips->turbo_toggle_allowed = false; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (strstr(boot_cpu_data.x86_model_id, "CPU M")) 13238c2ecf20Sopenharmony_ci limits = &ips_sv_limits; 13248c2ecf20Sopenharmony_ci else if (strstr(boot_cpu_data.x86_model_id, "CPU L")) 13258c2ecf20Sopenharmony_ci limits = &ips_lv_limits; 13268c2ecf20Sopenharmony_ci else if (strstr(boot_cpu_data.x86_model_id, "CPU U")) 13278c2ecf20Sopenharmony_ci limits = &ips_ulv_limits; 13288c2ecf20Sopenharmony_ci else { 13298c2ecf20Sopenharmony_ci dev_info(ips->dev, "No CPUID match found.\n"); 13308c2ecf20Sopenharmony_ci return NULL; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power); 13348c2ecf20Sopenharmony_ci tdp = turbo_power & TURBO_TDP_MASK; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* Sanity check TDP against CPU */ 13378c2ecf20Sopenharmony_ci if (limits->core_power_limit != (tdp / 8) * 1000) { 13388c2ecf20Sopenharmony_ci dev_info(ips->dev, 13398c2ecf20Sopenharmony_ci "CPU TDP doesn't match expected value (found %d, expected %d)\n", 13408c2ecf20Sopenharmony_ci tdp / 8, limits->core_power_limit / 1000); 13418c2ecf20Sopenharmony_ci limits->core_power_limit = (tdp / 8) * 1000; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return limits; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci/** 13488c2ecf20Sopenharmony_ci * ips_get_i915_syms - try to get GPU control methods from i915 driver 13498c2ecf20Sopenharmony_ci * @ips: IPS driver 13508c2ecf20Sopenharmony_ci * 13518c2ecf20Sopenharmony_ci * The i915 driver exports several interfaces to allow the IPS driver to 13528c2ecf20Sopenharmony_ci * monitor and control graphics turbo mode. If we can find them, we can 13538c2ecf20Sopenharmony_ci * enable graphics turbo, otherwise we must disable it to avoid exceeding 13548c2ecf20Sopenharmony_ci * thermal and power limits in the MCP. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic bool ips_get_i915_syms(struct ips_driver *ips) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci ips->read_mch_val = symbol_get(i915_read_mch_val); 13598c2ecf20Sopenharmony_ci if (!ips->read_mch_val) 13608c2ecf20Sopenharmony_ci goto out_err; 13618c2ecf20Sopenharmony_ci ips->gpu_raise = symbol_get(i915_gpu_raise); 13628c2ecf20Sopenharmony_ci if (!ips->gpu_raise) 13638c2ecf20Sopenharmony_ci goto out_put_mch; 13648c2ecf20Sopenharmony_ci ips->gpu_lower = symbol_get(i915_gpu_lower); 13658c2ecf20Sopenharmony_ci if (!ips->gpu_lower) 13668c2ecf20Sopenharmony_ci goto out_put_raise; 13678c2ecf20Sopenharmony_ci ips->gpu_busy = symbol_get(i915_gpu_busy); 13688c2ecf20Sopenharmony_ci if (!ips->gpu_busy) 13698c2ecf20Sopenharmony_ci goto out_put_lower; 13708c2ecf20Sopenharmony_ci ips->gpu_turbo_disable = symbol_get(i915_gpu_turbo_disable); 13718c2ecf20Sopenharmony_ci if (!ips->gpu_turbo_disable) 13728c2ecf20Sopenharmony_ci goto out_put_busy; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci return true; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ciout_put_busy: 13778c2ecf20Sopenharmony_ci symbol_put(i915_gpu_busy); 13788c2ecf20Sopenharmony_ciout_put_lower: 13798c2ecf20Sopenharmony_ci symbol_put(i915_gpu_lower); 13808c2ecf20Sopenharmony_ciout_put_raise: 13818c2ecf20Sopenharmony_ci symbol_put(i915_gpu_raise); 13828c2ecf20Sopenharmony_ciout_put_mch: 13838c2ecf20Sopenharmony_ci symbol_put(i915_read_mch_val); 13848c2ecf20Sopenharmony_ciout_err: 13858c2ecf20Sopenharmony_ci return false; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic bool 13898c2ecf20Sopenharmony_ciips_gpu_turbo_enabled(struct ips_driver *ips) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci if (!ips->gpu_busy && late_i915_load) { 13928c2ecf20Sopenharmony_ci if (ips_get_i915_syms(ips)) { 13938c2ecf20Sopenharmony_ci dev_info(ips->dev, 13948c2ecf20Sopenharmony_ci "i915 driver attached, reenabling gpu turbo\n"); 13958c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci return ips->gpu_turbo_enabled; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_civoid 14038c2ecf20Sopenharmony_ciips_link_to_i915_driver(void) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci /* We can't cleanly get at the various ips_driver structs from 14068c2ecf20Sopenharmony_ci * this caller (the i915 driver), so just set a flag saying 14078c2ecf20Sopenharmony_ci * that it's time to try getting the symbols again. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_ci late_i915_load = true; 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ips_link_to_i915_driver); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic const struct pci_device_id ips_id_table[] = { 14148c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, 14158c2ecf20Sopenharmony_ci { 0, } 14168c2ecf20Sopenharmony_ci}; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ips_id_table); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic int ips_blacklist_callback(const struct dmi_system_id *id) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci pr_info("Blacklisted intel_ips for %s\n", id->ident); 14238c2ecf20Sopenharmony_ci return 1; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic const struct dmi_system_id ips_blacklist[] = { 14278c2ecf20Sopenharmony_ci { 14288c2ecf20Sopenharmony_ci .callback = ips_blacklist_callback, 14298c2ecf20Sopenharmony_ci .ident = "HP ProBook", 14308c2ecf20Sopenharmony_ci .matches = { 14318c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 14328c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook"), 14338c2ecf20Sopenharmony_ci }, 14348c2ecf20Sopenharmony_ci }, 14358c2ecf20Sopenharmony_ci { } /* terminating entry */ 14368c2ecf20Sopenharmony_ci}; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci u64 platform_info; 14418c2ecf20Sopenharmony_ci struct ips_driver *ips; 14428c2ecf20Sopenharmony_ci u32 hts; 14438c2ecf20Sopenharmony_ci int ret = 0; 14448c2ecf20Sopenharmony_ci u16 htshi, trc, trc_required_mask; 14458c2ecf20Sopenharmony_ci u8 tse; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (dmi_check_system(ips_blacklist)) 14488c2ecf20Sopenharmony_ci return -ENODEV; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci ips = devm_kzalloc(&dev->dev, sizeof(*ips), GFP_KERNEL); 14518c2ecf20Sopenharmony_ci if (!ips) 14528c2ecf20Sopenharmony_ci return -ENOMEM; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci spin_lock_init(&ips->turbo_status_lock); 14558c2ecf20Sopenharmony_ci ips->dev = &dev->dev; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci ips->limits = ips_detect_cpu(ips); 14588c2ecf20Sopenharmony_ci if (!ips->limits) { 14598c2ecf20Sopenharmony_ci dev_info(&dev->dev, "IPS not supported on this CPU\n"); 14608c2ecf20Sopenharmony_ci return -ENXIO; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci ret = pcim_enable_device(dev); 14648c2ecf20Sopenharmony_ci if (ret) { 14658c2ecf20Sopenharmony_ci dev_err(&dev->dev, "can't enable PCI device, aborting\n"); 14668c2ecf20Sopenharmony_ci return ret; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci ret = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); 14708c2ecf20Sopenharmony_ci if (ret) { 14718c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to map thermal regs, aborting\n"); 14728c2ecf20Sopenharmony_ci return ret; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci ips->regmap = pcim_iomap_table(dev)[0]; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci pci_set_drvdata(dev, ips); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci tse = thm_readb(THM_TSE); 14798c2ecf20Sopenharmony_ci if (tse != TSE_EN) { 14808c2ecf20Sopenharmony_ci dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse); 14818c2ecf20Sopenharmony_ci return -ENXIO; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci trc = thm_readw(THM_TRC); 14858c2ecf20Sopenharmony_ci trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN; 14868c2ecf20Sopenharmony_ci if ((trc & trc_required_mask) != trc_required_mask) { 14878c2ecf20Sopenharmony_ci dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n"); 14888c2ecf20Sopenharmony_ci return -ENXIO; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (trc & TRC_CORE2_EN) 14928c2ecf20Sopenharmony_ci ips->second_cpu = true; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci update_turbo_limits(ips); 14958c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "max cpu power clamp: %dW\n", 14968c2ecf20Sopenharmony_ci ips->mcp_power_limit / 10); 14978c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "max core power clamp: %dW\n", 14988c2ecf20Sopenharmony_ci ips->core_power_limit / 10); 14998c2ecf20Sopenharmony_ci /* BIOS may update limits at runtime */ 15008c2ecf20Sopenharmony_ci if (thm_readl(THM_PSC) & PSP_PBRT) 15018c2ecf20Sopenharmony_ci ips->poll_turbo_status = true; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci if (!ips_get_i915_syms(ips)) { 15048c2ecf20Sopenharmony_ci dev_info(&dev->dev, "failed to get i915 symbols, graphics turbo disabled until i915 loads\n"); 15058c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = false; 15068c2ecf20Sopenharmony_ci } else { 15078c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "graphics turbo enabled\n"); 15088c2ecf20Sopenharmony_ci ips->gpu_turbo_enabled = true; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci /* 15128c2ecf20Sopenharmony_ci * Check PLATFORM_INFO MSR to make sure this chip is 15138c2ecf20Sopenharmony_ci * turbo capable. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_ci rdmsrl(PLATFORM_INFO, platform_info); 15168c2ecf20Sopenharmony_ci if (!(platform_info & PLATFORM_TDP)) { 15178c2ecf20Sopenharmony_ci dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n"); 15188c2ecf20Sopenharmony_ci return -ENODEV; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* 15228c2ecf20Sopenharmony_ci * IRQ handler for ME interaction 15238c2ecf20Sopenharmony_ci * Note: don't use MSI here as the PCH has bugs. 15248c2ecf20Sopenharmony_ci */ 15258c2ecf20Sopenharmony_ci ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); 15268c2ecf20Sopenharmony_ci if (ret < 0) 15278c2ecf20Sopenharmony_ci return ret; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci ips->irq = pci_irq_vector(dev, 0); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci ret = request_irq(ips->irq, ips_irq_handler, IRQF_SHARED, "ips", ips); 15328c2ecf20Sopenharmony_ci if (ret) { 15338c2ecf20Sopenharmony_ci dev_err(&dev->dev, "request irq failed, aborting\n"); 15348c2ecf20Sopenharmony_ci return ret; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* Enable aux, hot & critical interrupts */ 15388c2ecf20Sopenharmony_ci thm_writeb(THM_TSPIEN, TSPIEN_AUX2_LOHI | TSPIEN_CRIT_LOHI | 15398c2ecf20Sopenharmony_ci TSPIEN_HOT_LOHI | TSPIEN_AUX_LOHI); 15408c2ecf20Sopenharmony_ci thm_writeb(THM_TEN, TEN_UPDATE_EN); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci /* Collect adjustment values */ 15438c2ecf20Sopenharmony_ci ips->cta_val = thm_readw(THM_CTA); 15448c2ecf20Sopenharmony_ci ips->pta_val = thm_readw(THM_PTA); 15458c2ecf20Sopenharmony_ci ips->mgta_val = thm_readw(THM_MGTA); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* Save turbo limits & ratios */ 15488c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci ips_disable_cpu_turbo(ips); 15518c2ecf20Sopenharmony_ci ips->cpu_turbo_enabled = false; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci /* Create thermal adjust thread */ 15548c2ecf20Sopenharmony_ci ips->adjust = kthread_create(ips_adjust, ips, "ips-adjust"); 15558c2ecf20Sopenharmony_ci if (IS_ERR(ips->adjust)) { 15568c2ecf20Sopenharmony_ci dev_err(&dev->dev, 15578c2ecf20Sopenharmony_ci "failed to create thermal adjust thread, aborting\n"); 15588c2ecf20Sopenharmony_ci ret = -ENOMEM; 15598c2ecf20Sopenharmony_ci goto error_free_irq; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* 15648c2ecf20Sopenharmony_ci * Set up the work queue and monitor thread. The monitor thread 15658c2ecf20Sopenharmony_ci * will wake up ips_adjust thread. 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci ips->monitor = kthread_run(ips_monitor, ips, "ips-monitor"); 15688c2ecf20Sopenharmony_ci if (IS_ERR(ips->monitor)) { 15698c2ecf20Sopenharmony_ci dev_err(&dev->dev, 15708c2ecf20Sopenharmony_ci "failed to create thermal monitor thread, aborting\n"); 15718c2ecf20Sopenharmony_ci ret = -ENOMEM; 15728c2ecf20Sopenharmony_ci goto error_thread_cleanup; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci hts = (ips->core_power_limit << HTS_PCPL_SHIFT) | 15768c2ecf20Sopenharmony_ci (ips->mcp_temp_limit << HTS_PTL_SHIFT) | HTS_NVV; 15778c2ecf20Sopenharmony_ci htshi = HTS2_PRST_RUNNING << HTS2_PRST_SHIFT; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci thm_writew(THM_HTSHI, htshi); 15808c2ecf20Sopenharmony_ci thm_writel(THM_HTS, hts); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci ips_debugfs_init(ips); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci dev_info(&dev->dev, "IPS driver initialized, MCP temp limit %d\n", 15858c2ecf20Sopenharmony_ci ips->mcp_temp_limit); 15868c2ecf20Sopenharmony_ci return ret; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cierror_thread_cleanup: 15898c2ecf20Sopenharmony_ci kthread_stop(ips->adjust); 15908c2ecf20Sopenharmony_cierror_free_irq: 15918c2ecf20Sopenharmony_ci free_irq(ips->irq, ips); 15928c2ecf20Sopenharmony_ci pci_free_irq_vectors(dev); 15938c2ecf20Sopenharmony_ci return ret; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic void ips_remove(struct pci_dev *dev) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci struct ips_driver *ips = pci_get_drvdata(dev); 15998c2ecf20Sopenharmony_ci u64 turbo_override; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci ips_debugfs_cleanup(ips); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* Release i915 driver */ 16048c2ecf20Sopenharmony_ci if (ips->read_mch_val) 16058c2ecf20Sopenharmony_ci symbol_put(i915_read_mch_val); 16068c2ecf20Sopenharmony_ci if (ips->gpu_raise) 16078c2ecf20Sopenharmony_ci symbol_put(i915_gpu_raise); 16088c2ecf20Sopenharmony_ci if (ips->gpu_lower) 16098c2ecf20Sopenharmony_ci symbol_put(i915_gpu_lower); 16108c2ecf20Sopenharmony_ci if (ips->gpu_busy) 16118c2ecf20Sopenharmony_ci symbol_put(i915_gpu_busy); 16128c2ecf20Sopenharmony_ci if (ips->gpu_turbo_disable) 16138c2ecf20Sopenharmony_ci symbol_put(i915_gpu_turbo_disable); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 16168c2ecf20Sopenharmony_ci turbo_override &= ~(TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN); 16178c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 16188c2ecf20Sopenharmony_ci wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci free_irq(ips->irq, ips); 16218c2ecf20Sopenharmony_ci pci_free_irq_vectors(dev); 16228c2ecf20Sopenharmony_ci if (ips->adjust) 16238c2ecf20Sopenharmony_ci kthread_stop(ips->adjust); 16248c2ecf20Sopenharmony_ci if (ips->monitor) 16258c2ecf20Sopenharmony_ci kthread_stop(ips->monitor); 16268c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "IPS driver removed\n"); 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_cistatic struct pci_driver ips_pci_driver = { 16308c2ecf20Sopenharmony_ci .name = "intel ips", 16318c2ecf20Sopenharmony_ci .id_table = ips_id_table, 16328c2ecf20Sopenharmony_ci .probe = ips_probe, 16338c2ecf20Sopenharmony_ci .remove = ips_remove, 16348c2ecf20Sopenharmony_ci}; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cimodule_pci_driver(ips_pci_driver); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 16398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jesse Barnes <jbarnes@virtuousgeek.org>"); 16408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intelligent Power Sharing Driver"); 1641