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