blob: 39303bcf09e3500bdb6319049d765f12c0c0f075 [file] [log] [blame]
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +00001/*
2 * x86_pkg_temp_thermal driver
3 * Copyright (c) 2013, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.
16 *
17 */
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/err.h>
23#include <linux/param.h>
24#include <linux/device.h>
25#include <linux/platform_device.h>
26#include <linux/cpu.h>
27#include <linux/smp.h>
28#include <linux/slab.h>
29#include <linux/pm.h>
30#include <linux/thermal.h>
31#include <linux/debugfs.h>
32#include <asm/cpu_device_id.h>
33#include <asm/mce.h>
34
35/*
36* Rate control delay: Idea is to introduce denounce effect
37* This should be long enough to avoid reduce events, when
38* threshold is set to a temperature, which is constantly
39* violated, but at the short enough to take any action.
40* The action can be remove threshold or change it to next
41* interesting setting. Based on experiments, in around
42* every 5 seconds under load will give us a significant
43* temperature change.
44*/
45#define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000
46static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY;
47module_param(notify_delay_ms, int, 0644);
48MODULE_PARM_DESC(notify_delay_ms,
49 "User space notification delay in milli seconds.");
50
51/* Number of trip points in thermal zone. Currently it can't
52* be more than 2. MSR can allow setting and getting notifications
53* for only 2 thresholds. This define enforces this, if there
54* is some wrong values returned by cpuid for number of thresholds.
55*/
56#define MAX_NUMBER_OF_TRIPS 2
Srinivas Pandruvada94e791f2013-07-15 15:38:52 -070057/* Limit number of package temp zones */
58#define MAX_PKG_TEMP_ZONE_IDS 256
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000059
Thomas Gleixner3883a642016-11-22 17:57:08 +000060struct pkg_device {
61 struct list_head list;
62 u16 phys_proc_id;
63 u16 cpu;
64 u32 tj_max;
65 u32 msr_pkg_therm_low;
66 u32 msr_pkg_therm_high;
67 struct thermal_zone_device *tzone;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000068};
69
Javi Merino4abe6022015-03-03 15:30:50 +000070static struct thermal_zone_params pkg_temp_tz_params = {
Jean Delvare79786882014-03-02 15:33:35 +010071 .no_hwmon = true,
72};
73
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000074/* List maintaining number of package instances */
75static LIST_HEAD(phy_dev_list);
76static DEFINE_MUTEX(phy_dev_list_mutex);
77
78/* Interrupt to work function schedule queue */
79static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work);
80
81/* To track if the work is already scheduled on a package */
82static u8 *pkg_work_scheduled;
83
84/* Spin lock to prevent races with pkg_work_scheduled */
85static spinlock_t pkg_work_lock;
86static u16 max_phy_id;
87
88/* Debug counters to show using debugfs */
89static struct dentry *debugfs;
90static unsigned int pkg_interrupt_cnt;
91static unsigned int pkg_work_cnt;
92
93static int pkg_temp_debugfs_init(void)
94{
95 struct dentry *d;
96
97 debugfs = debugfs_create_dir("pkg_temp_thermal", NULL);
98 if (!debugfs)
99 return -ENOENT;
100
101 d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs,
102 (u32 *)&pkg_interrupt_cnt);
103 if (!d)
104 goto err_out;
105
106 d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs,
107 (u32 *)&pkg_work_cnt);
108 if (!d)
109 goto err_out;
110
111 return 0;
112
113err_out:
114 debugfs_remove_recursive(debugfs);
115 return -ENOENT;
116}
117
Thomas Gleixner3883a642016-11-22 17:57:08 +0000118static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000119{
120 u16 phys_proc_id = topology_physical_package_id(cpu);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000121 struct pkg_device *pkgdev;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000122
123 mutex_lock(&phy_dev_list_mutex);
124
Thomas Gleixner3883a642016-11-22 17:57:08 +0000125 list_for_each_entry(pkgdev, &phy_dev_list, list)
126 if (pkgdev->phys_proc_id == phys_proc_id) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000127 mutex_unlock(&phy_dev_list_mutex);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000128 return pkgdev;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000129 }
130
131 mutex_unlock(&phy_dev_list_mutex);
132
133 return NULL;
134}
135
136/*
137* tj-max is is interesting because threshold is set relative to this
138* temperature.
139*/
140static int get_tj_max(int cpu, u32 *tj_max)
141{
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000142 u32 eax, edx, val;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000143 int err;
144
145 err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
146 if (err)
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000147 return err;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000148
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000149 val = (eax >> 16) & 0xff;
150 *tj_max = val * 1000;
151
152 return val ? 0 : -EINVAL;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000153}
154
Sascha Hauer17e83512015-07-24 08:12:54 +0200155static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000156{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000157 struct pkg_device *pkgdev = tzd->devdata;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000158 u32 eax, edx;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000159
Thomas Gleixner3883a642016-11-22 17:57:08 +0000160 rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_STATUS, &eax, &edx);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000161 if (eax & 0x80000000) {
Thomas Gleixner3883a642016-11-22 17:57:08 +0000162 *temp = pkgdev->tj_max - ((eax >> 16) & 0x7f) * 1000;
Sascha Hauer17e83512015-07-24 08:12:54 +0200163 pr_debug("sys_get_curr_temp %d\n", *temp);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000164 return 0;
165 }
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000166 return -EINVAL;
167}
168
169static int sys_get_trip_temp(struct thermal_zone_device *tzd,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000170 int trip, int *temp)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000171{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000172 struct pkg_device *pkgdev = tzd->devdata;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000173 unsigned long thres_reg_value;
Thomas Gleixner3883a642016-11-22 17:57:08 +0000174 u32 mask, shift, eax, edx;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000175 int ret;
176
177 if (trip >= MAX_NUMBER_OF_TRIPS)
178 return -EINVAL;
179
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000180 if (trip) {
181 mask = THERM_MASK_THRESHOLD1;
182 shift = THERM_SHIFT_THRESHOLD1;
183 } else {
184 mask = THERM_MASK_THRESHOLD0;
185 shift = THERM_SHIFT_THRESHOLD0;
186 }
187
Thomas Gleixner3883a642016-11-22 17:57:08 +0000188 ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000189 &eax, &edx);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000190 if (ret < 0)
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000191 return ret;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000192
193 thres_reg_value = (eax & mask) >> shift;
194 if (thres_reg_value)
Thomas Gleixner3883a642016-11-22 17:57:08 +0000195 *temp = pkgdev->tj_max - thres_reg_value * 1000;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000196 else
197 *temp = 0;
Sascha Hauer17e83512015-07-24 08:12:54 +0200198 pr_debug("sys_get_trip_temp %d\n", *temp);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000199
200 return 0;
201}
202
Thomas Gleixner3883a642016-11-22 17:57:08 +0000203static int
204sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000205{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000206 struct pkg_device *pkgdev = tzd->devdata;
207 u32 l, h, mask, shift, intr;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000208 int ret;
209
Thomas Gleixner3883a642016-11-22 17:57:08 +0000210 if (trip >= MAX_NUMBER_OF_TRIPS || temp >= pkgdev->tj_max)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000211 return -EINVAL;
212
Thomas Gleixner3883a642016-11-22 17:57:08 +0000213 ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000214 &l, &h);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000215 if (ret < 0)
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000216 return ret;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000217
218 if (trip) {
219 mask = THERM_MASK_THRESHOLD1;
220 shift = THERM_SHIFT_THRESHOLD1;
221 intr = THERM_INT_THRESHOLD1_ENABLE;
222 } else {
223 mask = THERM_MASK_THRESHOLD0;
224 shift = THERM_SHIFT_THRESHOLD0;
225 intr = THERM_INT_THRESHOLD0_ENABLE;
226 }
227 l &= ~mask;
228 /*
229 * When users space sets a trip temperature == 0, which is indication
230 * that, it is no longer interested in receiving notifications.
231 */
Thomas Gleixner3883a642016-11-22 17:57:08 +0000232 if (!temp) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000233 l &= ~intr;
Thomas Gleixner3883a642016-11-22 17:57:08 +0000234 } else {
235 l |= (pkgdev->tj_max - temp)/1000 << shift;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000236 l |= intr;
237 }
238
Thomas Gleixner3883a642016-11-22 17:57:08 +0000239 return wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000240}
241
Thomas Gleixner3883a642016-11-22 17:57:08 +0000242static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
243 enum thermal_trip_type *type)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000244{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000245 *type = THERMAL_TRIP_PASSIVE;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000246 return 0;
247}
248
249/* Thermal zone callback registry */
250static struct thermal_zone_device_ops tzone_ops = {
251 .get_temp = sys_get_curr_temp,
252 .get_trip_temp = sys_get_trip_temp,
253 .get_trip_type = sys_get_trip_type,
254 .set_trip_temp = sys_set_trip_temp,
255};
256
Thomas Gleixner09a674c2016-11-22 17:57:06 +0000257static bool pkg_thermal_rate_control(void)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000258{
259 return true;
260}
261
262/* Enable threshold interrupt on local package/cpu */
263static inline void enable_pkg_thres_interrupt(void)
264{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000265 u8 thres_0, thres_1;
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000266 u32 l, h;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000267
268 rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
269 /* only enable/disable if it had valid threshold value */
270 thres_0 = (l & THERM_MASK_THRESHOLD0) >> THERM_SHIFT_THRESHOLD0;
271 thres_1 = (l & THERM_MASK_THRESHOLD1) >> THERM_SHIFT_THRESHOLD1;
272 if (thres_0)
273 l |= THERM_INT_THRESHOLD0_ENABLE;
274 if (thres_1)
275 l |= THERM_INT_THRESHOLD1_ENABLE;
276 wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
277}
278
279/* Disable threshold interrupt on local package/cpu */
280static inline void disable_pkg_thres_interrupt(void)
281{
282 u32 l, h;
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000283
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000284 rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000285
286 l &= ~(THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE);
287 wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000288}
289
290static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
291{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000292 int cpu = smp_processor_id();
Thomas Gleixner3883a642016-11-22 17:57:08 +0000293 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000294 int phy_id = topology_physical_package_id(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000295 bool notify = false;
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700296 unsigned long flags;
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000297 u64 msr_val, wr_val;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000298
Thomas Gleixner3883a642016-11-22 17:57:08 +0000299 if (!pkgdev)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000300 return;
301
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700302 spin_lock_irqsave(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000303 ++pkg_work_cnt;
304 if (unlikely(phy_id > max_phy_id)) {
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700305 spin_unlock_irqrestore(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000306 return;
307 }
308 pkg_work_scheduled[phy_id] = 0;
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700309 spin_unlock_irqrestore(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000310
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000311 rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000312 wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
313 if (wr_val != msr_val) {
314 wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000315 notify = true;
316 }
Thomas Gleixner768bd132016-11-22 17:57:04 +0000317
318 enable_pkg_thres_interrupt();
319
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000320 if (notify) {
321 pr_debug("thermal_zone_device_update\n");
Thomas Gleixner3883a642016-11-22 17:57:08 +0000322 thermal_zone_device_update(pkgdev->tzone,
Srinivas Pandruvada0e70f462016-08-26 16:21:16 -0700323 THERMAL_EVENT_UNSPECIFIED);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000324 }
325}
326
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000327static int pkg_thermal_notify(u64 msr_val)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000328{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000329 int cpu = smp_processor_id();
330 int phy_id = topology_physical_package_id(cpu);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000331 unsigned long flags;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000332
333 /*
334 * When a package is in interrupted state, all CPU's in that package
335 * are in the same interrupt state. So scheduling on any one CPU in
336 * the package is enough and simply return for others.
337 */
338 spin_lock_irqsave(&pkg_work_lock, flags);
339 ++pkg_interrupt_cnt;
340 if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) ||
341 pkg_work_scheduled[phy_id]) {
342 disable_pkg_thres_interrupt();
343 spin_unlock_irqrestore(&pkg_work_lock, flags);
344 return -EINVAL;
345 }
346 pkg_work_scheduled[phy_id] = 1;
347 spin_unlock_irqrestore(&pkg_work_lock, flags);
348
349 disable_pkg_thres_interrupt();
350 schedule_delayed_work_on(cpu,
351 &per_cpu(pkg_temp_thermal_threshold_work, cpu),
352 msecs_to_jiffies(notify_delay_ms));
353 return 0;
354}
355
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000356static int pkg_temp_thermal_device_add(unsigned int cpu)
357{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000358 u32 tj_max, eax, ebx, ecx, edx;
359 struct pkg_device *pkgdev;
360 int thres_count, err;
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700361 unsigned long flags;
Thomas Gleixner3883a642016-11-22 17:57:08 +0000362 u8 *temp;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000363
364 cpuid(6, &eax, &ebx, &ecx, &edx);
365 thres_count = ebx & 0x07;
366 if (!thres_count)
367 return -ENODEV;
368
Srinivas Pandruvada94e791f2013-07-15 15:38:52 -0700369 if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS)
370 return -ENODEV;
371
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000372 thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
373
374 err = get_tj_max(cpu, &tj_max);
375 if (err)
376 goto err_ret;
377
378 mutex_lock(&phy_dev_list_mutex);
379
Thomas Gleixner3883a642016-11-22 17:57:08 +0000380 pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL);
381 if (!pkgdev) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000382 err = -ENOMEM;
383 goto err_ret_unlock;
384 }
385
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700386 spin_lock_irqsave(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000387 if (topology_physical_package_id(cpu) > max_phy_id)
388 max_phy_id = topology_physical_package_id(cpu);
Wei Yongjunc7c1b312013-06-18 21:09:12 +0800389 temp = krealloc(pkg_work_scheduled,
390 (max_phy_id+1) * sizeof(u8), GFP_ATOMIC);
391 if (!temp) {
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700392 spin_unlock_irqrestore(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000393 err = -ENOMEM;
394 goto err_ret_free;
395 }
Wei Yongjunc7c1b312013-06-18 21:09:12 +0800396 pkg_work_scheduled = temp;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000397 pkg_work_scheduled[topology_physical_package_id(cpu)] = 0;
Srinivas Pandruvada7bed1b32013-09-24 11:05:16 -0700398 spin_unlock_irqrestore(&pkg_work_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000399
Thomas Gleixner3883a642016-11-22 17:57:08 +0000400 pkgdev->phys_proc_id = topology_physical_package_id(cpu);
401 pkgdev->cpu = cpu;
402 pkgdev->tj_max = tj_max;
403 pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp",
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000404 thres_count,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000405 (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
406 pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
407 if (IS_ERR(pkgdev->tzone)) {
408 err = PTR_ERR(pkgdev->tzone);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000409 goto err_ret_free;
410 }
411 /* Store MSR value for package thermal interrupt, to restore at exit */
412 rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000413 &pkgdev->msr_pkg_therm_low,
414 &pkgdev->msr_pkg_therm_high);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000415
Thomas Gleixner3883a642016-11-22 17:57:08 +0000416 list_add_tail(&pkgdev->list, &phy_dev_list);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000417 pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n",
Thomas Gleixner3883a642016-11-22 17:57:08 +0000418 pkgdev->phys_proc_id, cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000419
420 mutex_unlock(&phy_dev_list_mutex);
421
422 return 0;
423
424err_ret_free:
Thomas Gleixner3883a642016-11-22 17:57:08 +0000425 kfree(pkgdev);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000426err_ret_unlock:
427 mutex_unlock(&phy_dev_list_mutex);
428
429err_ret:
430 return err;
431}
432
433static int pkg_temp_thermal_device_remove(unsigned int cpu)
434{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000435 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000436 int target;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000437
Thomas Gleixner3883a642016-11-22 17:57:08 +0000438 if (!pkgdev)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000439 return -ENODEV;
440
441 mutex_lock(&phy_dev_list_mutex);
Thomas Gleixner21a3d3d2016-11-22 17:57:06 +0000442
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000443 target = cpumask_any_but(topology_core_cpumask(cpu), cpu);
444 /* This might be the last cpu in this package */
445 if (target >= nr_cpu_ids) {
Thomas Gleixner3883a642016-11-22 17:57:08 +0000446 thermal_zone_device_unregister(pkgdev->tzone);
Thomas Gleixner89baa562016-11-22 17:57:05 +0000447 /*
448 * Restore original MSR value for package thermal
449 * interrupt.
450 */
451 wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000452 pkgdev->msr_pkg_therm_low,
453 pkgdev->msr_pkg_therm_high);
454 list_del(&pkgdev->list);
455 kfree(pkgdev);
456 } else if (pkgdev->cpu == cpu) {
457 pkgdev->cpu = target;
Thomas Gleixner89baa562016-11-22 17:57:05 +0000458 }
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000459
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000460 mutex_unlock(&phy_dev_list_mutex);
461
462 return 0;
463}
464
465static int get_core_online(unsigned int cpu)
466{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000467 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000468 struct cpuinfo_x86 *c = &cpu_data(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000469
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000470 /* Paranoia check */
471 if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS))
472 return -ENODEV;
Thomas Gleixner3883a642016-11-22 17:57:08 +0000473
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000474 INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu),
Thomas Gleixner3883a642016-11-22 17:57:08 +0000475 pkg_temp_thermal_threshold_work_fn);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000476
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000477 /* If the package exists, nothing to do */
478 if (pkgdev)
479 return 0;
480 return pkg_temp_thermal_device_add(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000481}
482
483static void put_core_offline(unsigned int cpu)
484{
485 if (!pkg_temp_thermal_device_remove(cpu))
486 cancel_delayed_work_sync(
487 &per_cpu(pkg_temp_thermal_threshold_work, cpu));
488
489 pr_debug("put_core_offline: cpu %d\n", cpu);
490}
491
492static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
493 unsigned long action, void *hcpu)
494{
495 unsigned int cpu = (unsigned long) hcpu;
496
Richard Cochran5af897e2016-03-18 22:26:09 +0100497 switch (action & ~CPU_TASKS_FROZEN) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000498 case CPU_ONLINE:
499 case CPU_DOWN_FAILED:
500 get_core_online(cpu);
501 break;
502 case CPU_DOWN_PREPARE:
503 put_core_offline(cpu);
504 break;
505 }
506 return NOTIFY_OK;
507}
508
509static struct notifier_block pkg_temp_thermal_notifier __refdata = {
510 .notifier_call = pkg_temp_thermal_cpu_callback,
511};
512
513static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = {
Srinivas Pandruvadaf3ed0a12013-07-11 09:50:30 -0700514 { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS },
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000515 {}
516};
517MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids);
518
519static int __init pkg_temp_thermal_init(void)
520{
521 int i;
522
523 if (!x86_match_cpu(pkg_temp_thermal_ids))
524 return -ENODEV;
525
526 spin_lock_init(&pkg_work_lock);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000527
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530528 cpu_notifier_register_begin();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000529 for_each_online_cpu(i)
530 if (get_core_online(i))
531 goto err_ret;
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530532 __register_hotcpu_notifier(&pkg_temp_thermal_notifier);
533 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000534
Thomas Gleixner09a674c2016-11-22 17:57:06 +0000535 platform_thermal_package_notify = pkg_thermal_notify;
536 platform_thermal_package_rate_control = pkg_thermal_rate_control;
537
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000538 /* Don't care if it fails */
539 pkg_temp_debugfs_init();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000540 return 0;
541
542err_ret:
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000543 for_each_online_cpu(i)
544 put_core_offline(i);
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530545 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000546 kfree(pkg_work_scheduled);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000547 return -ENODEV;
548}
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000549module_init(pkg_temp_thermal_init)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000550
551static void __exit pkg_temp_thermal_exit(void)
552{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000553 struct pkg_device *pkgdev, *n;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000554 int i;
555
Thomas Gleixner09a674c2016-11-22 17:57:06 +0000556 platform_thermal_package_notify = NULL;
557 platform_thermal_package_rate_control = NULL;
558
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530559 cpu_notifier_register_begin();
560 __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000561 mutex_lock(&phy_dev_list_mutex);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000562 list_for_each_entry_safe(pkgdev, n, &phy_dev_list, list) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000563 /* Retore old MSR value for package thermal interrupt */
Thomas Gleixner3883a642016-11-22 17:57:08 +0000564 wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
565 pkgdev->msr_pkg_therm_low,
566 pkgdev->msr_pkg_therm_high);
567 thermal_zone_device_unregister(pkgdev->tzone);
568 list_del(&pkgdev->list);
569 kfree(pkgdev);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000570 }
571 mutex_unlock(&phy_dev_list_mutex);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000572 for_each_online_cpu(i)
573 cancel_delayed_work_sync(
574 &per_cpu(pkg_temp_thermal_threshold_work, i));
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530575 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000576
577 kfree(pkg_work_scheduled);
578
579 debugfs_remove_recursive(debugfs);
580}
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000581module_exit(pkg_temp_thermal_exit)
582
583MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
584MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
585MODULE_LICENSE("GPL v2");