blob: 91f267a4b1b856e96c8fe7aa1800f16436f42edf [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;
Thomas Gleixner64ca7382016-11-22 17:57:12 +000064 bool work_scheduled;
Thomas Gleixner3883a642016-11-22 17:57:08 +000065 u32 tj_max;
66 u32 msr_pkg_therm_low;
67 u32 msr_pkg_therm_high;
68 struct thermal_zone_device *tzone;
Thomas Gleixnerab47bd92016-11-22 17:57:10 +000069 struct cpumask cpumask;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000070};
71
Javi Merino4abe6022015-03-03 15:30:50 +000072static struct thermal_zone_params pkg_temp_tz_params = {
Jean Delvare79786882014-03-02 15:33:35 +010073 .no_hwmon = true,
74};
75
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000076/* List maintaining number of package instances */
77static LIST_HEAD(phy_dev_list);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +000078/* Serializes interrupt notification, work and hotplug */
79static DEFINE_SPINLOCK(pkg_temp_lock);
80/* Protects zone operation in the work function against hotplug removal */
81static DEFINE_MUTEX(thermal_zone_mutex);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000082
83/* Interrupt to work function schedule queue */
84static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work);
85
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +000086/* Debug counters to show using debugfs */
87static struct dentry *debugfs;
88static unsigned int pkg_interrupt_cnt;
89static unsigned int pkg_work_cnt;
90
91static int pkg_temp_debugfs_init(void)
92{
93 struct dentry *d;
94
95 debugfs = debugfs_create_dir("pkg_temp_thermal", NULL);
96 if (!debugfs)
97 return -ENOENT;
98
99 d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs,
100 (u32 *)&pkg_interrupt_cnt);
101 if (!d)
102 goto err_out;
103
104 d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs,
105 (u32 *)&pkg_work_cnt);
106 if (!d)
107 goto err_out;
108
109 return 0;
110
111err_out:
112 debugfs_remove_recursive(debugfs);
113 return -ENOENT;
114}
115
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000116/*
117 * Protection:
118 *
119 * - cpu hotplug: Read serialized by cpu hotplug lock
120 * Write must hold pkg_temp_lock
121 *
122 * - Other callsites: Must hold pkg_temp_lock
123 */
Thomas Gleixner3883a642016-11-22 17:57:08 +0000124static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000125{
126 u16 phys_proc_id = topology_physical_package_id(cpu);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000127 struct pkg_device *pkgdev;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000128
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000129 list_for_each_entry(pkgdev, &phy_dev_list, list) {
130 if (pkgdev->phys_proc_id == phys_proc_id)
Thomas Gleixner3883a642016-11-22 17:57:08 +0000131 return pkgdev;
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000132 }
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000133 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{
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000292 struct thermal_zone_device *tzone = NULL;
Thomas Gleixner64ca7382016-11-22 17:57:12 +0000293 int cpu = smp_processor_id();
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000294 struct pkg_device *pkgdev;
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000295 u64 msr_val, wr_val;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000296
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000297 mutex_lock(&thermal_zone_mutex);
298 spin_lock_irq(&pkg_temp_lock);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000299 ++pkg_work_cnt;
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000300
301 pkgdev = pkg_temp_thermal_get_dev(cpu);
302 if (!pkgdev) {
303 spin_unlock_irq(&pkg_temp_lock);
304 mutex_unlock(&thermal_zone_mutex);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000305 return;
306 }
Thomas Gleixner64ca7382016-11-22 17:57:12 +0000307 pkgdev->work_scheduled = false;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000308
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000309 rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000310 wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
311 if (wr_val != msr_val) {
312 wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000313 tzone = pkgdev->tzone;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000314 }
Thomas Gleixner768bd132016-11-22 17:57:04 +0000315
316 enable_pkg_thres_interrupt();
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000317 spin_unlock_irq(&pkg_temp_lock);
Thomas Gleixner768bd132016-11-22 17:57:04 +0000318
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000319 /*
320 * If tzone is not NULL, then thermal_zone_mutex will prevent the
321 * concurrent removal in the cpu offline callback.
322 */
323 if (tzone)
324 thermal_zone_device_update(tzone, THERMAL_EVENT_UNSPECIFIED);
325
326 mutex_unlock(&thermal_zone_mutex);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000327}
328
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000329static int pkg_thermal_notify(u64 msr_val)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000330{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000331 int cpu = smp_processor_id();
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000332 struct pkg_device *pkgdev;
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000333 unsigned long flags;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000334
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000335 spin_lock_irqsave(&pkg_temp_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000336 ++pkg_interrupt_cnt;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000337
338 disable_pkg_thres_interrupt();
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000339
340 /* Work is per package, so scheduling it once is enough. */
341 pkgdev = pkg_temp_thermal_get_dev(cpu);
Thomas Gleixner64ca7382016-11-22 17:57:12 +0000342 if (pkgdev && !pkgdev->work_scheduled) {
343 pkgdev->work_scheduled = true;
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000344 schedule_delayed_work_on(cpu,
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000345 &per_cpu(pkg_temp_thermal_threshold_work, cpu),
346 msecs_to_jiffies(notify_delay_ms));
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000347 }
348
349 spin_unlock_irqrestore(&pkg_temp_lock, flags);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000350 return 0;
351}
352
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000353static int pkg_temp_thermal_device_add(unsigned int cpu)
354{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000355 u32 tj_max, eax, ebx, ecx, edx;
356 struct pkg_device *pkgdev;
357 int thres_count, err;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000358
359 cpuid(6, &eax, &ebx, &ecx, &edx);
360 thres_count = ebx & 0x07;
361 if (!thres_count)
362 return -ENODEV;
363
Srinivas Pandruvada94e791f2013-07-15 15:38:52 -0700364 if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS)
365 return -ENODEV;
366
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000367 thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
368
369 err = get_tj_max(cpu, &tj_max);
370 if (err)
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000371 return err;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000372
Thomas Gleixner3883a642016-11-22 17:57:08 +0000373 pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000374 if (!pkgdev)
375 return -ENOMEM;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000376
Thomas Gleixner3883a642016-11-22 17:57:08 +0000377 pkgdev->phys_proc_id = topology_physical_package_id(cpu);
378 pkgdev->cpu = cpu;
379 pkgdev->tj_max = tj_max;
380 pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp",
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000381 thres_count,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000382 (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
383 pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
384 if (IS_ERR(pkgdev->tzone)) {
385 err = PTR_ERR(pkgdev->tzone);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000386 kfree(pkgdev);
387 return err;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000388 }
389 /* Store MSR value for package thermal interrupt, to restore at exit */
390 rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000391 &pkgdev->msr_pkg_therm_low,
392 &pkgdev->msr_pkg_therm_high);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000393
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000394 cpumask_set_cpu(cpu, &pkgdev->cpumask);
395 spin_lock_irq(&pkg_temp_lock);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000396 list_add_tail(&pkgdev->list, &phy_dev_list);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000397 spin_unlock_irq(&pkg_temp_lock);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000398 return 0;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000399}
400
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000401static void put_core_offline(unsigned int cpu)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000402{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000403 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000404 bool lastcpu;
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000405 int target;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000406
Thomas Gleixner3883a642016-11-22 17:57:08 +0000407 if (!pkgdev)
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000408 return;
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000409
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000410 target = cpumask_any_but(&pkgdev->cpumask, cpu);
411 cpumask_clear_cpu(cpu, &pkgdev->cpumask);
412 lastcpu = target >= nr_cpu_ids;
413 /*
414 * Remove the sysfs files, if this is the last cpu in the package
415 * before doing further cleanups.
416 */
417 if (lastcpu) {
418 struct thermal_zone_device *tzone = pkgdev->tzone;
Thomas Gleixner21a3d3d2016-11-22 17:57:06 +0000419
Thomas Gleixner89baa562016-11-22 17:57:05 +0000420 /*
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000421 * We must protect against a work function calling
422 * thermal_zone_update, after/while unregister. We null out
423 * the pointer under the zone mutex, so the worker function
424 * won't try to call.
Thomas Gleixner89baa562016-11-22 17:57:05 +0000425 */
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000426 mutex_lock(&thermal_zone_mutex);
427 pkgdev->tzone = NULL;
428 mutex_unlock(&thermal_zone_mutex);
429
430 thermal_zone_device_unregister(tzone);
431 }
432
433 /*
434 * If this is the last CPU in the package, restore the interrupt
435 * MSR and remove the package reference from the array.
436 */
437 if (lastcpu) {
438 /* Protect against work and interrupts */
439 spin_lock_irq(&pkg_temp_lock);
440 list_del(&pkgdev->list);
441 /*
442 * After this point nothing touches the MSR anymore. We
443 * must drop the lock to make the cross cpu call. This goes
444 * away once we move that code to the hotplug state machine.
445 */
446 spin_unlock_irq(&pkg_temp_lock);
Thomas Gleixner89baa562016-11-22 17:57:05 +0000447 wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
Thomas Gleixner3883a642016-11-22 17:57:08 +0000448 pkgdev->msr_pkg_therm_low,
449 pkgdev->msr_pkg_therm_high);
Thomas Gleixner3883a642016-11-22 17:57:08 +0000450 kfree(pkgdev);
Thomas Gleixner89baa562016-11-22 17:57:05 +0000451 }
Thomas Gleixnerb6badbe2016-11-22 17:57:07 +0000452
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000453 /*
454 * Note, this is broken when work was really scheduled on the
455 * outgoing cpu because this will leave the work_scheduled flag set
456 * and the thermal interrupts disabled. Will be fixed in the next
457 * step as there is no way to fix it in a sane way with the per cpu
458 * work nonsense.
459 */
460 cancel_delayed_work_sync(&per_cpu(pkg_temp_thermal_threshold_work, cpu));
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000461}
462
463static int get_core_online(unsigned int cpu)
464{
Thomas Gleixner3883a642016-11-22 17:57:08 +0000465 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000466 struct cpuinfo_x86 *c = &cpu_data(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000467
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000468 /* Paranoia check */
469 if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS))
470 return -ENODEV;
Thomas Gleixner3883a642016-11-22 17:57:08 +0000471
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000472 INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu),
Thomas Gleixner3883a642016-11-22 17:57:08 +0000473 pkg_temp_thermal_threshold_work_fn);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000474
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000475 /* If the package exists, nothing to do */
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000476 if (pkgdev) {
477 cpumask_set_cpu(cpu, &pkgdev->cpumask);
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000478 return 0;
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000479 }
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000480 return pkg_temp_thermal_device_add(cpu);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000481}
482
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000483static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
484 unsigned long action, void *hcpu)
485{
486 unsigned int cpu = (unsigned long) hcpu;
487
Richard Cochran5af897e2016-03-18 22:26:09 +0100488 switch (action & ~CPU_TASKS_FROZEN) {
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000489 case CPU_ONLINE:
490 case CPU_DOWN_FAILED:
491 get_core_online(cpu);
492 break;
493 case CPU_DOWN_PREPARE:
494 put_core_offline(cpu);
495 break;
496 }
497 return NOTIFY_OK;
498}
499
500static struct notifier_block pkg_temp_thermal_notifier __refdata = {
501 .notifier_call = pkg_temp_thermal_cpu_callback,
502};
503
504static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = {
Srinivas Pandruvadaf3ed0a12013-07-11 09:50:30 -0700505 { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS },
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000506 {}
507};
508MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids);
509
510static int __init pkg_temp_thermal_init(void)
511{
512 int i;
513
514 if (!x86_match_cpu(pkg_temp_thermal_ids))
515 return -ENODEV;
516
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530517 cpu_notifier_register_begin();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000518 for_each_online_cpu(i)
519 if (get_core_online(i))
520 goto err_ret;
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530521 __register_hotcpu_notifier(&pkg_temp_thermal_notifier);
522 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000523
Thomas Gleixner09a674c2016-11-22 17:57:06 +0000524 platform_thermal_package_notify = pkg_thermal_notify;
525 platform_thermal_package_rate_control = pkg_thermal_rate_control;
526
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000527 /* Don't care if it fails */
528 pkg_temp_debugfs_init();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000529 return 0;
530
531err_ret:
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000532 for_each_online_cpu(i)
533 put_core_offline(i);
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530534 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000535 return -ENODEV;
536}
Thomas Gleixner8079a4b2016-11-22 17:57:09 +0000537module_init(pkg_temp_thermal_init)
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000538
539static void __exit pkg_temp_thermal_exit(void)
540{
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000541 int i;
542
Thomas Gleixner09a674c2016-11-22 17:57:06 +0000543 platform_thermal_package_notify = NULL;
544 platform_thermal_package_rate_control = NULL;
545
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530546 cpu_notifier_register_begin();
547 __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000548 for_each_online_cpu(i)
Thomas Gleixnerab47bd92016-11-22 17:57:10 +0000549 put_core_offline(i);
Srivatsa S. Bhatcf0485a2014-03-11 02:10:54 +0530550 cpu_notifier_register_done();
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000551
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000552 debugfs_remove_recursive(debugfs);
553}
Srinivas Pandruvadaf1a18a12013-05-17 23:42:02 +0000554module_exit(pkg_temp_thermal_exit)
555
556MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
557MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
558MODULE_LICENSE("GPL v2");