thermal.c 31.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 *  acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $)
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This driver fully implements the ACPI thermal policy as described in the
 *  ACPI 2.0 Specification.
 *
 *  TBD: 1. Implement passive cooling hysteresis.
 *       2. Enhance passive cooling (CPU) states/limit interface to support
 *          concepts of 'multiple limiters', upper/lower limits, etc.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
32
#include <linux/dmi.h>
L
Linus Torvalds 已提交
33
#include <linux/init.h>
34
#include <linux/slab.h>
L
Linus Torvalds 已提交
35
#include <linux/types.h>
36
#include <linux/jiffies.h>
L
Linus Torvalds 已提交
37
#include <linux/kmod.h>
38
#include <linux/reboot.h>
39
#include <linux/device.h>
40
#include <linux/thermal.h>
41
#include <linux/acpi.h>
42
#include <linux/workqueue.h>
43
#include <asm/uaccess.h>
L
Linus Torvalds 已提交
44

45 46
#define PREFIX "ACPI: "

L
Linus Torvalds 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59
#define ACPI_THERMAL_CLASS		"thermal_zone"
#define ACPI_THERMAL_DEVICE_NAME	"Thermal Zone"
#define ACPI_THERMAL_NOTIFY_TEMPERATURE	0x80
#define ACPI_THERMAL_NOTIFY_THRESHOLDS	0x81
#define ACPI_THERMAL_NOTIFY_DEVICES	0x82
#define ACPI_THERMAL_NOTIFY_CRITICAL	0xF0
#define ACPI_THERMAL_NOTIFY_HOT		0xF1
#define ACPI_THERMAL_MODE_ACTIVE	0x00

#define ACPI_THERMAL_MAX_ACTIVE	10
#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65

#define _COMPONENT		ACPI_THERMAL_COMPONENT
60
ACPI_MODULE_NAME("thermal");
L
Linus Torvalds 已提交
61

62
MODULE_AUTHOR("Paul Diefenbaugh");
63
MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
L
Linus Torvalds 已提交
64 65
MODULE_LICENSE("GPL");

66 67
static int act;
module_param(act, int, 0644);
68
MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
69

70 71 72 73
static int crt;
module_param(crt, int, 0644);
MODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");

L
Linus Torvalds 已提交
74
static int tzp;
75
module_param(tzp, int, 0444);
76
MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
L
Linus Torvalds 已提交
77

78 79
static int nocrt;
module_param(nocrt, int, 0);
80
MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points.");
81

82 83
static int off;
module_param(off, int, 0);
84
MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
85

86 87
static int psv;
module_param(psv, int, 0644);
88
MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
89

90 91
static struct workqueue_struct *acpi_thermal_pm_queue;

L
Len Brown 已提交
92
static int acpi_thermal_add(struct acpi_device *device);
93
static int acpi_thermal_remove(struct acpi_device *device);
94
static void acpi_thermal_notify(struct acpi_device *device, u32 event);
L
Linus Torvalds 已提交
95

96 97 98 99 100 101
static const struct acpi_device_id  thermal_device_ids[] = {
	{ACPI_THERMAL_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, thermal_device_ids);

102
#ifdef CONFIG_PM_SLEEP
103
static int acpi_thermal_suspend(struct device *dev);
104
static int acpi_thermal_resume(struct device *dev);
105
#else
106
#define acpi_thermal_suspend NULL
107
#define acpi_thermal_resume NULL
108
#endif
109
static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
110

L
Linus Torvalds 已提交
111
static struct acpi_driver acpi_thermal_driver = {
L
Len Brown 已提交
112
	.name = "thermal",
L
Len Brown 已提交
113
	.class = ACPI_THERMAL_CLASS,
114
	.ids = thermal_device_ids,
L
Len Brown 已提交
115 116 117
	.ops = {
		.add = acpi_thermal_add,
		.remove = acpi_thermal_remove,
118
		.notify = acpi_thermal_notify,
L
Len Brown 已提交
119
		},
120
	.drv.pm = &acpi_thermal_pm,
L
Linus Torvalds 已提交
121 122 123
};

struct acpi_thermal_state {
L
Len Brown 已提交
124 125 126 127 128 129
	u8 critical:1;
	u8 hot:1;
	u8 passive:1;
	u8 active:1;
	u8 reserved:4;
	int active_index;
L
Linus Torvalds 已提交
130 131 132
};

struct acpi_thermal_state_flags {
L
Len Brown 已提交
133 134 135
	u8 valid:1;
	u8 enabled:1;
	u8 reserved:6;
L
Linus Torvalds 已提交
136 137 138 139
};

struct acpi_thermal_critical {
	struct acpi_thermal_state_flags flags;
L
Len Brown 已提交
140
	unsigned long temperature;
L
Linus Torvalds 已提交
141 142 143 144
};

struct acpi_thermal_hot {
	struct acpi_thermal_state_flags flags;
L
Len Brown 已提交
145
	unsigned long temperature;
L
Linus Torvalds 已提交
146 147 148 149
};

struct acpi_thermal_passive {
	struct acpi_thermal_state_flags flags;
L
Len Brown 已提交
150 151 152 153 154
	unsigned long temperature;
	unsigned long tc1;
	unsigned long tc2;
	unsigned long tsp;
	struct acpi_handle_list devices;
L
Linus Torvalds 已提交
155 156 157 158
};

struct acpi_thermal_active {
	struct acpi_thermal_state_flags flags;
L
Len Brown 已提交
159 160
	unsigned long temperature;
	struct acpi_handle_list devices;
L
Linus Torvalds 已提交
161 162 163 164
};

struct acpi_thermal_trips {
	struct acpi_thermal_critical critical;
L
Len Brown 已提交
165
	struct acpi_thermal_hot hot;
L
Linus Torvalds 已提交
166 167 168 169 170
	struct acpi_thermal_passive passive;
	struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
};

struct acpi_thermal_flags {
L
Len Brown 已提交
171 172 173
	u8 cooling_mode:1;	/* _SCP */
	u8 devices:1;		/* _TZD */
	u8 reserved:6;
L
Linus Torvalds 已提交
174 175 176
};

struct acpi_thermal {
177
	struct acpi_device * device;
L
Len Brown 已提交
178 179 180 181 182
	acpi_bus_id name;
	unsigned long temperature;
	unsigned long last_temperature;
	unsigned long polling_frequency;
	volatile u8 zombie;
L
Linus Torvalds 已提交
183 184 185
	struct acpi_thermal_flags flags;
	struct acpi_thermal_state state;
	struct acpi_thermal_trips trips;
L
Len Brown 已提交
186
	struct acpi_handle_list devices;
187 188
	struct thermal_zone_device *thermal_zone;
	int tz_enabled;
189
	int kelvin_offset;
190
	struct work_struct thermal_check_work;
L
Linus Torvalds 已提交
191 192 193 194 195 196
};

/* --------------------------------------------------------------------------
                             Thermal Zone Management
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
197
static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
L
Linus Torvalds 已提交
198
{
L
Len Brown 已提交
199
	acpi_status status = AE_OK;
200
	unsigned long long tmp;
L
Linus Torvalds 已提交
201 202

	if (!tz)
203
		return -EINVAL;
L
Linus Torvalds 已提交
204 205 206

	tz->last_temperature = tz->temperature;

207
	status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
L
Linus Torvalds 已提交
208
	if (ACPI_FAILURE(status))
209
		return -ENODEV;
L
Linus Torvalds 已提交
210

211
	tz->temperature = tmp;
L
Len Brown 已提交
212 213
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
			  tz->temperature));
L
Linus Torvalds 已提交
214

215
	return 0;
L
Linus Torvalds 已提交
216 217
}

L
Len Brown 已提交
218
static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
L
Linus Torvalds 已提交
219
{
L
Len Brown 已提交
220
	acpi_status status = AE_OK;
221
	unsigned long long tmp;
L
Linus Torvalds 已提交
222 223

	if (!tz)
224
		return -EINVAL;
L
Linus Torvalds 已提交
225

226
	status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp);
L
Linus Torvalds 已提交
227
	if (ACPI_FAILURE(status))
228
		return -ENODEV;
L
Linus Torvalds 已提交
229

230
	tz->polling_frequency = tmp;
L
Len Brown 已提交
231 232
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
			  tz->polling_frequency));
L
Linus Torvalds 已提交
233

234
	return 0;
L
Linus Torvalds 已提交
235 236
}

L
Len Brown 已提交
237
static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
L
Linus Torvalds 已提交
238 239
{
	if (!tz)
240
		return -EINVAL;
L
Linus Torvalds 已提交
241

242
	if (!acpi_has_method(tz->device->handle, "_SCP")) {
L
Linus Torvalds 已提交
243
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
244
		return -ENODEV;
245 246
	} else if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle,
							   "_SCP", mode))) {
247
		return -ENODEV;
248
	}
L
Linus Torvalds 已提交
249

250
	return 0;
L
Linus Torvalds 已提交
251 252
}

253 254 255 256 257
#define ACPI_TRIPS_CRITICAL	0x01
#define ACPI_TRIPS_HOT		0x02
#define ACPI_TRIPS_PASSIVE	0x04
#define ACPI_TRIPS_ACTIVE	0x08
#define ACPI_TRIPS_DEVICES	0x10
L
Linus Torvalds 已提交
258

259 260
#define ACPI_TRIPS_REFRESH_THRESHOLDS	(ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
#define ACPI_TRIPS_REFRESH_DEVICES	ACPI_TRIPS_DEVICES
L
Linus Torvalds 已提交
261

262 263 264
#define ACPI_TRIPS_INIT      (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT |	\
			      ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE |	\
			      ACPI_TRIPS_DEVICES)
L
Linus Torvalds 已提交
265

266 267 268 269 270 271 272 273 274 275 276 277
/*
 * This exception is thrown out in two cases:
 * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
 *   when re-evaluating the AML code.
 * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
 *   We need to re-bind the cooling devices of a thermal zone when this occurs.
 */
#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str)	\
do {	\
	if (flags != ACPI_TRIPS_INIT)	\
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,	\
		"ACPI thermal trip point %s changed\n"	\
278
		"Please send acpidump to linux-acpi@vger.kernel.org", str)); \
279 280 281 282 283
} while (0)

static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
{
	acpi_status status = AE_OK;
284
	unsigned long long tmp;
285 286 287
	struct acpi_handle_list devices;
	int valid = 0;
	int i;
L
Linus Torvalds 已提交
288

289
	/* Critical Shutdown */
290 291
	if (flag & ACPI_TRIPS_CRITICAL) {
		status = acpi_evaluate_integer(tz->device->handle,
292 293
				"_CRT", NULL, &tmp);
		tz->trips.critical.temperature = tmp;
294 295 296
		/*
		 * Treat freezing temperatures as invalid as well; some
		 * BIOSes return really low values and cause reboots at startup.
297
		 * Below zero (Celsius) values clearly aren't right for sure..
298 299
		 * ... so lets discard those as invalid.
		 */
300 301 302 303 304
		if (ACPI_FAILURE(status)) {
			tz->trips.critical.flags.valid = 0;
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					  "No critical threshold\n"));
		} else if (tmp <= 2732) {
305 306
			pr_warn(FW_BUG "Invalid critical threshold (%llu)\n",
				tmp);
307
			tz->trips.critical.flags.valid = 0;
308 309 310
		} else {
			tz->trips.critical.flags.valid = 1;
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
311 312
					  "Found critical threshold [%lu]\n",
					  tz->trips.critical.temperature));
313 314 315 316 317
		}
		if (tz->trips.critical.flags.valid == 1) {
			if (crt == -1) {
				tz->trips.critical.flags.valid = 0;
			} else if (crt > 0) {
318
				unsigned long crt_k = CELSIUS_TO_DECI_KELVIN(crt);
319
				/*
320
				 * Allow override critical threshold
321
				 */
322
				if (crt_k > tz->trips.critical.temperature)
323 324
					pr_warn(PREFIX "Critical threshold %d C\n",
						crt);
325
				tz->trips.critical.temperature = crt_k;
326
			}
327 328 329
		}
	}

L
Linus Torvalds 已提交
330
	/* Critical Sleep (optional) */
331
	if (flag & ACPI_TRIPS_HOT) {
332
		status = acpi_evaluate_integer(tz->device->handle,
333
				"_HOT", NULL, &tmp);
334 335 336 337 338
		if (ACPI_FAILURE(status)) {
			tz->trips.hot.flags.valid = 0;
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					"No hot threshold\n"));
		} else {
339
			tz->trips.hot.temperature = tmp;
340 341 342
			tz->trips.hot.flags.valid = 1;
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					"Found hot threshold [%lu]\n",
343
					tz->trips.hot.temperature));
344
		}
345 346
	}

347
	/* Passive (optional) */
348 349
	if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
		(flag == ACPI_TRIPS_INIT)) {
350 351 352 353
		valid = tz->trips.passive.flags.valid;
		if (psv == -1) {
			status = AE_SUPPORT;
		} else if (psv > 0) {
354
			tmp = CELSIUS_TO_DECI_KELVIN(psv);
355 356 357
			status = AE_OK;
		} else {
			status = acpi_evaluate_integer(tz->device->handle,
358
				"_PSV", NULL, &tmp);
359
		}
L
Linus Torvalds 已提交
360 361 362

		if (ACPI_FAILURE(status))
			tz->trips.passive.flags.valid = 0;
363
		else {
364
			tz->trips.passive.temperature = tmp;
365 366 367 368
			tz->trips.passive.flags.valid = 1;
			if (flag == ACPI_TRIPS_INIT) {
				status = acpi_evaluate_integer(
						tz->device->handle, "_TC1",
369
						NULL, &tmp);
370 371
				if (ACPI_FAILURE(status))
					tz->trips.passive.flags.valid = 0;
372 373
				else
					tz->trips.passive.tc1 = tmp;
374 375
				status = acpi_evaluate_integer(
						tz->device->handle, "_TC2",
376
						NULL, &tmp);
377 378
				if (ACPI_FAILURE(status))
					tz->trips.passive.flags.valid = 0;
379 380
				else
					tz->trips.passive.tc2 = tmp;
381 382
				status = acpi_evaluate_integer(
						tz->device->handle, "_TSP",
383
						NULL, &tmp);
384 385
				if (ACPI_FAILURE(status))
					tz->trips.passive.flags.valid = 0;
386 387
				else
					tz->trips.passive.tsp = tmp;
388 389 390 391 392 393 394
			}
		}
	}
	if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
		memset(&devices, 0, sizeof(struct acpi_handle_list));
		status = acpi_evaluate_reference(tz->device->handle, "_PSL",
							NULL, &devices);
395
		if (ACPI_FAILURE(status)) {
396
			pr_warn(PREFIX "Invalid passive threshold\n");
L
Linus Torvalds 已提交
397
			tz->trips.passive.flags.valid = 0;
398
		}
L
Linus Torvalds 已提交
399
		else
400
			tz->trips.passive.flags.valid = 1;
L
Linus Torvalds 已提交
401

402 403 404 405 406 407 408 409 410 411 412
		if (memcmp(&tz->trips.passive.devices, &devices,
				sizeof(struct acpi_handle_list))) {
			memcpy(&tz->trips.passive.devices, &devices,
				sizeof(struct acpi_handle_list));
			ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
		}
	}
	if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
		if (valid != tz->trips.passive.flags.valid)
				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
	}
L
Linus Torvalds 已提交
413

414
	/* Active (optional) */
L
Len Brown 已提交
415 416
	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
		char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
417
		valid = tz->trips.active[i].flags.valid;
L
Linus Torvalds 已提交
418

419
		if (act == -1)
420 421
			break; /* disable all active trip points */

422 423
		if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) &&
			tz->trips.active[i].flags.valid)) {
424
			status = acpi_evaluate_integer(tz->device->handle,
425
							name, NULL, &tmp);
426 427 428 429 430 431 432 433
			if (ACPI_FAILURE(status)) {
				tz->trips.active[i].flags.valid = 0;
				if (i == 0)
					break;
				if (act <= 0)
					break;
				if (i == 1)
					tz->trips.active[0].temperature =
434
						CELSIUS_TO_DECI_KELVIN(act);
435 436 437 438 439 440 441
				else
					/*
					 * Don't allow override higher than
					 * the next higher trip point
					 */
					tz->trips.active[i - 1].temperature =
						(tz->trips.active[i - 2].temperature <
442
						CELSIUS_TO_DECI_KELVIN(act) ?
443
						tz->trips.active[i - 2].temperature :
444
						CELSIUS_TO_DECI_KELVIN(act));
445
				break;
446 447
			} else {
				tz->trips.active[i].temperature = tmp;
448
				tz->trips.active[i].flags.valid = 1;
449
			}
450
		}
L
Linus Torvalds 已提交
451 452

		name[2] = 'L';
453 454 455 456
		if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
			memset(&devices, 0, sizeof(struct acpi_handle_list));
			status = acpi_evaluate_reference(tz->device->handle,
						name, NULL, &devices);
457
			if (ACPI_FAILURE(status)) {
458 459
				pr_warn(PREFIX "Invalid active%d threshold\n",
					i);
460
				tz->trips.active[i].flags.valid = 0;
461
			}
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
			else
				tz->trips.active[i].flags.valid = 1;

			if (memcmp(&tz->trips.active[i].devices, &devices,
					sizeof(struct acpi_handle_list))) {
				memcpy(&tz->trips.active[i].devices, &devices,
					sizeof(struct acpi_handle_list));
				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
			}
		}
		if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
			if (valid != tz->trips.active[i].flags.valid)
				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");

		if (!tz->trips.active[i].flags.valid)
			break;
	}

480 481 482
	if ((flag & ACPI_TRIPS_DEVICES)
	    && acpi_has_method(tz->device->handle, "_TZD")) {
		memset(&devices, 0, sizeof(devices));
483 484
		status = acpi_evaluate_reference(tz->device->handle, "_TZD",
						NULL, &devices);
485 486 487
		if (ACPI_SUCCESS(status)
		    && memcmp(&tz->devices, &devices, sizeof(devices))) {
			tz->devices = devices;
488 489
			ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
		}
L
Linus Torvalds 已提交
490 491
	}

492
	return 0;
L
Linus Torvalds 已提交
493 494
}

495
static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
L
Linus Torvalds 已提交
496
{
497 498 499 500 501 502 503 504 505 506 507 508 509
	int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);

	if (ret)
		return ret;

	valid = tz->trips.critical.flags.valid |
		tz->trips.hot.flags.valid |
		tz->trips.passive.flags.valid;

	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
		valid |= tz->trips.active[i].flags.valid;

	if (!valid) {
510
		pr_warn(FW_BUG "No valid trip found\n");
511 512 513
		return -ENODEV;
	}
	return 0;
L
Linus Torvalds 已提交
514 515
}

L
Len Brown 已提交
516
static void acpi_thermal_check(void *data)
L
Linus Torvalds 已提交
517
{
518
	struct acpi_thermal *tz = data;
L
Linus Torvalds 已提交
519

520
	if (!tz->tz_enabled)
521
		return;
522

523
	thermal_zone_device_update(tz->thermal_zone);
L
Linus Torvalds 已提交
524 525
}

526
/* sys I/F for generic thermal sysfs support */
527

528
static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
529 530
{
	struct acpi_thermal *tz = thermal->devdata;
Z
Zhang, Rui 已提交
531
	int result;
532 533 534 535

	if (!tz)
		return -EINVAL;

Z
Zhang, Rui 已提交
536 537 538 539
	result = acpi_thermal_get_temperature(tz);
	if (result)
		return result;

540 541
	*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature,
							tz->kelvin_offset);
542
	return 0;
543 544 545
}

static int thermal_get_mode(struct thermal_zone_device *thermal,
546
				enum thermal_device_mode *mode)
547 548 549 550 551 552
{
	struct acpi_thermal *tz = thermal->devdata;

	if (!tz)
		return -EINVAL;

553 554 555 556
	*mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
		THERMAL_DEVICE_DISABLED;

	return 0;
557 558 559
}

static int thermal_set_mode(struct thermal_zone_device *thermal,
560
				enum thermal_device_mode mode)
561 562 563 564 565 566 567 568 569 570
{
	struct acpi_thermal *tz = thermal->devdata;
	int enable;

	if (!tz)
		return -EINVAL;

	/*
	 * enable/disable thermal management from ACPI thermal driver
	 */
571
	if (mode == THERMAL_DEVICE_ENABLED)
572
		enable = 1;
573
	else if (mode == THERMAL_DEVICE_DISABLED) {
574
		enable = 0;
575 576
		pr_warn("thermal zone will be disabled\n");
	} else
577 578 579 580 581
		return -EINVAL;

	if (enable != tz->tz_enabled) {
		tz->tz_enabled = enable;
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Z
Zhang Rui 已提交
582 583
			"%s kernel ACPI thermal control\n",
			tz->tz_enabled ? "Enable" : "Disable"));
584 585 586 587 588 589
		acpi_thermal_check(tz);
	}
	return 0;
}

static int thermal_get_trip_type(struct thermal_zone_device *thermal,
590
				 int trip, enum thermal_trip_type *type)
591 592 593 594 595 596 597 598
{
	struct acpi_thermal *tz = thermal->devdata;
	int i;

	if (!tz || trip < 0)
		return -EINVAL;

	if (tz->trips.critical.flags.valid) {
599 600 601 602
		if (!trip) {
			*type = THERMAL_TRIP_CRITICAL;
			return 0;
		}
603 604 605 606
		trip--;
	}

	if (tz->trips.hot.flags.valid) {
607 608 609 610
		if (!trip) {
			*type = THERMAL_TRIP_HOT;
			return 0;
		}
611 612 613 614
		trip--;
	}

	if (tz->trips.passive.flags.valid) {
615 616 617 618
		if (!trip) {
			*type = THERMAL_TRIP_PASSIVE;
			return 0;
		}
619 620 621 622 623
		trip--;
	}

	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
		tz->trips.active[i].flags.valid; i++) {
624 625 626 627
		if (!trip) {
			*type = THERMAL_TRIP_ACTIVE;
			return 0;
		}
628 629 630 631 632 633 634
		trip--;
	}

	return -EINVAL;
}

static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
635
				 int trip, int *temp)
636 637 638 639 640 641 642 643
{
	struct acpi_thermal *tz = thermal->devdata;
	int i;

	if (!tz || trip < 0)
		return -EINVAL;

	if (tz->trips.critical.flags.valid) {
644
		if (!trip) {
645
			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
646 647
				tz->trips.critical.temperature,
				tz->kelvin_offset);
648 649
			return 0;
		}
650 651 652 653
		trip--;
	}

	if (tz->trips.hot.flags.valid) {
654
		if (!trip) {
655
			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
656 657
				tz->trips.hot.temperature,
				tz->kelvin_offset);
658 659
			return 0;
		}
660 661 662 663
		trip--;
	}

	if (tz->trips.passive.flags.valid) {
664
		if (!trip) {
665
			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
666 667
				tz->trips.passive.temperature,
				tz->kelvin_offset);
668 669
			return 0;
		}
670 671 672 673 674
		trip--;
	}

	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
		tz->trips.active[i].flags.valid; i++) {
675
		if (!trip) {
676
			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
677 678
				tz->trips.active[i].temperature,
				tz->kelvin_offset);
679 680
			return 0;
		}
681 682 683 684 685 686
		trip--;
	}

	return -EINVAL;
}

687
static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
688 689
				int *temperature)
{
690 691 692
	struct acpi_thermal *tz = thermal->devdata;

	if (tz->trips.critical.flags.valid) {
693
		*temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
694 695
				tz->trips.critical.temperature,
				tz->kelvin_offset);
696 697 698 699 700
		return 0;
	} else
		return -EINVAL;
}

701 702 703 704 705 706 707 708 709 710
static int thermal_get_trend(struct thermal_zone_device *thermal,
				int trip, enum thermal_trend *trend)
{
	struct acpi_thermal *tz = thermal->devdata;
	enum thermal_trip_type type;
	int i;

	if (thermal_get_trip_type(thermal, trip, &type))
		return -EINVAL;

711
	if (type == THERMAL_TRIP_ACTIVE) {
712 713
		int trip_temp;
		int temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
714
					tz->temperature, tz->kelvin_offset);
715 716 717 718 719 720 721 722 723 724
		if (thermal_get_trip_temp(thermal, trip, &trip_temp))
			return -EINVAL;

		if (temp > trip_temp) {
			*trend = THERMAL_TREND_RAISING;
			return 0;
		} else {
			/* Fall back on default trend */
			return -EINVAL;
		}
725
	}
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

	/*
	 * tz->temperature has already been updated by generic thermal layer,
	 * before this callback being invoked
	 */
	i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature))
		+ (tz->trips.passive.tc2
		* (tz->temperature - tz->trips.passive.temperature));

	if (i > 0)
		*trend = THERMAL_TREND_RAISING;
	else if (i < 0)
		*trend = THERMAL_TREND_DROPPING;
	else
		*trend = THERMAL_TREND_STABLE;
	return 0;
}


745 746 747 748 749 750 751 752 753 754 755 756 757 758
static int thermal_notify(struct thermal_zone_device *thermal, int trip,
			   enum thermal_trip_type trip_type)
{
	u8 type = 0;
	struct acpi_thermal *tz = thermal->devdata;

	if (trip_type == THERMAL_TRIP_CRITICAL)
		type = ACPI_THERMAL_NOTIFY_CRITICAL;
	else if (trip_type == THERMAL_TRIP_HOT)
		type = ACPI_THERMAL_NOTIFY_HOT;
	else
		return 0;

	acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
759
					dev_name(&tz->device->dev), type, 1);
760 761 762 763 764 765 766

	if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
		return 1;

	return 0;
}

767 768
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
					struct thermal_cooling_device *cdev,
Z
Zhang Rui 已提交
769
					bool bind)
770 771 772
{
	struct acpi_device *device = cdev->devdata;
	struct acpi_thermal *tz = thermal->devdata;
Z
Zhang Rui 已提交
773 774 775
	struct acpi_device *dev;
	acpi_status status;
	acpi_handle handle;
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
	int i;
	int j;
	int trip = -1;
	int result = 0;

	if (tz->trips.critical.flags.valid)
		trip++;

	if (tz->trips.hot.flags.valid)
		trip++;

	if (tz->trips.passive.flags.valid) {
		trip++;
		for (i = 0; i < tz->trips.passive.devices.count;
		    i++) {
Z
Zhang Rui 已提交
791 792
			handle = tz->trips.passive.devices.handles[i];
			status = acpi_bus_get_device(handle, &dev);
Z
Zhang Rui 已提交
793 794 795 796 797 798
			if (ACPI_FAILURE(status) || dev != device)
				continue;
			if (bind)
				result =
					thermal_zone_bind_cooling_device
					(thermal, trip, cdev,
799 800
					 THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
					 THERMAL_WEIGHT_DEFAULT);
Z
Zhang Rui 已提交
801 802 803 804 805 806
			else
				result =
					thermal_zone_unbind_cooling_device
					(thermal, trip, cdev);
			if (result)
				goto failed;
807 808 809 810 811 812 813 814 815 816
		}
	}

	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
		if (!tz->trips.active[i].flags.valid)
			break;
		trip++;
		for (j = 0;
		    j < tz->trips.active[i].devices.count;
		    j++) {
Z
Zhang Rui 已提交
817 818
			handle = tz->trips.active[i].devices.handles[j];
			status = acpi_bus_get_device(handle, &dev);
Z
Zhang Rui 已提交
819 820 821 822 823
			if (ACPI_FAILURE(status) || dev != device)
				continue;
			if (bind)
				result = thermal_zone_bind_cooling_device
					(thermal, trip, cdev,
824 825
					 THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
					 THERMAL_WEIGHT_DEFAULT);
Z
Zhang Rui 已提交
826 827 828 829 830
			else
				result = thermal_zone_unbind_cooling_device
					(thermal, trip, cdev);
			if (result)
				goto failed;
831 832 833 834
		}
	}

	for (i = 0; i < tz->devices.count; i++) {
Z
Zhang Rui 已提交
835 836 837
		handle = tz->devices.handles[i];
		status = acpi_bus_get_device(handle, &dev);
		if (ACPI_SUCCESS(status) && (dev == device)) {
Z
Zhang Rui 已提交
838 839
			if (bind)
				result = thermal_zone_bind_cooling_device
840 841
						(thermal, THERMAL_TRIPS_NONE,
						 cdev, THERMAL_NO_LIMIT,
842 843
						 THERMAL_NO_LIMIT,
						 THERMAL_WEIGHT_DEFAULT);
Z
Zhang Rui 已提交
844 845
			else
				result = thermal_zone_unbind_cooling_device
846 847
						(thermal, THERMAL_TRIPS_NONE,
						 cdev);
Z
Zhang Rui 已提交
848 849 850
			if (result)
				goto failed;
		}
851 852 853 854 855 856 857 858 859 860
	}

failed:
	return result;
}

static int
acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
					struct thermal_cooling_device *cdev)
{
Z
Zhang Rui 已提交
861
	return acpi_thermal_cooling_device_cb(thermal, cdev, true);
862 863 864 865 866 867
}

static int
acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
					struct thermal_cooling_device *cdev)
{
Z
Zhang Rui 已提交
868
	return acpi_thermal_cooling_device_cb(thermal, cdev, false);
869 870
}

871
static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
872 873 874 875 876 877 878
	.bind = acpi_thermal_bind_cooling_device,
	.unbind	= acpi_thermal_unbind_cooling_device,
	.get_temp = thermal_get_temp,
	.get_mode = thermal_get_mode,
	.set_mode = thermal_set_mode,
	.get_trip_type = thermal_get_trip_type,
	.get_trip_temp = thermal_get_trip_temp,
879
	.get_crit_temp = thermal_get_crit_temp,
880
	.get_trend = thermal_get_trend,
881
	.notify = thermal_notify,
882 883 884 885 886 887
};

static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
{
	int trips = 0;
	int result;
Z
Zhang Rui 已提交
888
	acpi_status status;
889 890 891 892 893 894 895 896 897 898 899 900 901
	int i;

	if (tz->trips.critical.flags.valid)
		trips++;

	if (tz->trips.hot.flags.valid)
		trips++;

	if (tz->trips.passive.flags.valid)
		trips++;

	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
			tz->trips.active[i].flags.valid; i++, trips++);
902 903 904

	if (tz->trips.passive.flags.valid)
		tz->thermal_zone =
905
			thermal_zone_device_register("acpitz", trips, 0, tz,
906
						&acpi_thermal_zone_ops, NULL,
907 908 909 910
						     tz->trips.passive.tsp*100,
						     tz->polling_frequency*100);
	else
		tz->thermal_zone =
911
			thermal_zone_device_register("acpitz", trips, 0, tz,
912 913
						&acpi_thermal_zone_ops, NULL,
						0, tz->polling_frequency*100);
K
Krzysztof Helt 已提交
914
	if (IS_ERR(tz->thermal_zone))
915 916 917 918 919 920 921 922 923 924 925 926
		return -ENODEV;

	result = sysfs_create_link(&tz->device->dev.kobj,
				   &tz->thermal_zone->device.kobj, "thermal_zone");
	if (result)
		return result;

	result = sysfs_create_link(&tz->thermal_zone->device.kobj,
				   &tz->device->dev.kobj, "device");
	if (result)
		return result;

927 928 929
	status =  acpi_bus_attach_private_data(tz->device->handle,
					       tz->thermal_zone);
	if (ACPI_FAILURE(status))
Z
Zhang Rui 已提交
930 931
		return -ENODEV;

932 933
	tz->tz_enabled = 1;

934 935
	dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
		 tz->thermal_zone->id);
936 937 938 939 940 941 942 943 944
	return 0;
}

static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
{
	sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
	sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
	thermal_zone_device_unregister(tz->thermal_zone);
	tz->thermal_zone = NULL;
945
	acpi_bus_detach_private_data(tz->device->handle);
946 947 948
}


L
Linus Torvalds 已提交
949 950 951 952
/* --------------------------------------------------------------------------
                                 Driver Interface
   -------------------------------------------------------------------------- */

953
static void acpi_thermal_notify(struct acpi_device *device, u32 event)
L
Linus Torvalds 已提交
954
{
955
	struct acpi_thermal *tz = acpi_driver_data(device);
L
Linus Torvalds 已提交
956 957 958


	if (!tz)
959
		return;
L
Linus Torvalds 已提交
960 961 962 963 964 965

	switch (event) {
	case ACPI_THERMAL_NOTIFY_TEMPERATURE:
		acpi_thermal_check(tz);
		break;
	case ACPI_THERMAL_NOTIFY_THRESHOLDS:
966
		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
L
Linus Torvalds 已提交
967
		acpi_thermal_check(tz);
968
		acpi_bus_generate_netlink_event(device->pnp.device_class,
969
						  dev_name(&device->dev), event, 0);
L
Linus Torvalds 已提交
970 971
		break;
	case ACPI_THERMAL_NOTIFY_DEVICES:
972 973
		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
		acpi_thermal_check(tz);
974
		acpi_bus_generate_netlink_event(device->pnp.device_class,
975
						  dev_name(&device->dev), event, 0);
L
Linus Torvalds 已提交
976 977 978
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
979
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
980 981 982 983
		break;
	}
}

984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
/*
 * On some platforms, the AML code has dependency about
 * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx.
 * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after
 *    /_CRT/_HOT/_PSV/_ACx, or else system will be power off.
 * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0
 *    if _TMP has never been evaluated.
 *
 * As this dependency is totally transparent to OS, evaluate
 * all of them once, in the order of _CRT/_HOT/_PSV/_ACx,
 * _TMP, before they are actually used.
 */
static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz)
{
	acpi_handle handle = tz->device->handle;
	unsigned long long value;
	int i;

	acpi_evaluate_integer(handle, "_CRT", NULL, &value);
	acpi_evaluate_integer(handle, "_HOT", NULL, &value);
	acpi_evaluate_integer(handle, "_PSV", NULL, &value);
	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
		char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
		acpi_status status;

		status = acpi_evaluate_integer(handle, name, NULL, &value);
		if (status == AE_NOT_FOUND)
			break;
	}
	acpi_evaluate_integer(handle, "_TMP", NULL, &value);
}

L
Len Brown 已提交
1016
static int acpi_thermal_get_info(struct acpi_thermal *tz)
L
Linus Torvalds 已提交
1017
{
L
Len Brown 已提交
1018
	int result = 0;
L
Linus Torvalds 已提交
1019 1020 1021


	if (!tz)
1022
		return -EINVAL;
L
Linus Torvalds 已提交
1023

1024 1025
	acpi_thermal_aml_dependency_fix(tz);

1026 1027
	/* Get trip points [_CRT, _PSV, etc.] (required) */
	result = acpi_thermal_get_trip_points(tz);
L
Linus Torvalds 已提交
1028
	if (result)
1029
		return result;
L
Linus Torvalds 已提交
1030

1031 1032
	/* Get temperature [_TMP] (required) */
	result = acpi_thermal_get_temperature(tz);
L
Linus Torvalds 已提交
1033
	if (result)
1034
		return result;
L
Linus Torvalds 已提交
1035 1036 1037

	/* Set the cooling mode [_SCP] to active cooling (default) */
	result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
L
Len Brown 已提交
1038
	if (!result)
L
Linus Torvalds 已提交
1039 1040 1041 1042 1043 1044 1045 1046
		tz->flags.cooling_mode = 1;

	/* Get default polling frequency [_TZP] (optional) */
	if (tzp)
		tz->polling_frequency = tzp;
	else
		acpi_thermal_get_polling_frequency(tz);

1047
	return 0;
L
Linus Torvalds 已提交
1048 1049
}

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
/*
 * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI
 * handles temperature values with a single decimal place. As a consequence,
 * some implementations use an offset of 273.1 and others use an offset of
 * 273.2. Try to find out which one is being used, to present the most
 * accurate and visually appealing number.
 *
 * The heuristic below should work for all ACPI thermal zones which have a
 * critical trip point with a value being a multiple of 0.5 degree Celsius.
 */
static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
{
	if (tz->trips.critical.flags.valid &&
	    (tz->trips.critical.temperature % 5) == 1)
		tz->kelvin_offset = 2731;
	else
		tz->kelvin_offset = 2732;
}

1069 1070 1071 1072 1073 1074 1075
static void acpi_thermal_check_fn(struct work_struct *work)
{
	struct acpi_thermal *tz = container_of(work, struct acpi_thermal,
					       thermal_check_work);
	acpi_thermal_check(tz);
}

L
Len Brown 已提交
1076
static int acpi_thermal_add(struct acpi_device *device)
L
Linus Torvalds 已提交
1077
{
L
Len Brown 已提交
1078 1079
	int result = 0;
	struct acpi_thermal *tz = NULL;
L
Linus Torvalds 已提交
1080 1081 1082


	if (!device)
1083
		return -EINVAL;
L
Linus Torvalds 已提交
1084

1085
	tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
L
Linus Torvalds 已提交
1086
	if (!tz)
1087
		return -ENOMEM;
L
Linus Torvalds 已提交
1088

1089
	tz->device = device;
L
Linus Torvalds 已提交
1090 1091 1092
	strcpy(tz->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
1093
	device->driver_data = tz;
1094

L
Linus Torvalds 已提交
1095 1096
	result = acpi_thermal_get_info(tz);
	if (result)
1097 1098
		goto free_memory;

1099 1100
	acpi_thermal_guess_offset(tz);

1101 1102 1103
	result = acpi_thermal_register_thermal_zone(tz);
	if (result)
		goto free_memory;
L
Linus Torvalds 已提交
1104

1105 1106
	INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn);

1107
	pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device),
1108
		acpi_device_bid(device), DECI_KELVIN_TO_CELSIUS(tz->temperature));
1109
	goto end;
L
Linus Torvalds 已提交
1110

1111 1112 1113
free_memory:
	kfree(tz);
end:
1114
	return result;
L
Linus Torvalds 已提交
1115 1116
}

1117
static int acpi_thermal_remove(struct acpi_device *device)
L
Linus Torvalds 已提交
1118
{
L
Len Brown 已提交
1119
	struct acpi_thermal *tz = NULL;
L
Linus Torvalds 已提交
1120 1121

	if (!device || !acpi_driver_data(device))
1122
		return -EINVAL;
L
Linus Torvalds 已提交
1123

1124
	flush_workqueue(acpi_thermal_pm_queue);
1125
	tz = acpi_driver_data(device);
L
Linus Torvalds 已提交
1126

1127
	acpi_thermal_unregister_thermal_zone(tz);
L
Linus Torvalds 已提交
1128
	kfree(tz);
1129
	return 0;
L
Linus Torvalds 已提交
1130 1131
}

1132
#ifdef CONFIG_PM_SLEEP
1133 1134 1135 1136 1137 1138 1139
static int acpi_thermal_suspend(struct device *dev)
{
	/* Make sure the previously queued thermal check work has been done */
	flush_workqueue(acpi_thermal_pm_queue);
	return 0;
}

1140
static int acpi_thermal_resume(struct device *dev)
1141
{
1142
	struct acpi_thermal *tz;
1143 1144
	int i, j, power_state, result;

1145
	if (!dev)
1146
		return -EINVAL;
1147

1148 1149 1150
	tz = acpi_driver_data(to_acpi_device(dev));
	if (!tz)
		return -EINVAL;
1151

1152
	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
1153 1154 1155 1156 1157 1158
		if (!(&tz->trips.active[i]))
			break;
		if (!tz->trips.active[i].flags.valid)
			break;
		tz->trips.active[i].flags.enabled = 1;
		for (j = 0; j < tz->trips.active[i].devices.count; j++) {
1159 1160 1161
			result = acpi_bus_update_power(
					tz->trips.active[i].devices.handles[j],
					&power_state);
1162 1163 1164 1165
			if (result || (power_state != ACPI_STATE_D0)) {
				tz->trips.active[i].flags.enabled = 0;
				break;
			}
1166
		}
1167
		tz->state.active |= tz->trips.active[i].flags.enabled;
1168 1169
	}

1170
	queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
1171 1172 1173

	return AE_OK;
}
1174
#endif
1175

1176
static int thermal_act(const struct dmi_system_id *d) {
1177 1178

	if (act == 0) {
1179 1180
		pr_notice(PREFIX "%s detected: "
			  "disabling all active thermal trip points\n", d->ident);
1181 1182 1183 1184
		act = -1;
	}
	return 0;
}
1185
static int thermal_nocrt(const struct dmi_system_id *d) {
1186

1187 1188
	pr_notice(PREFIX "%s detected: "
		  "disabling all critical thermal trip point actions.\n", d->ident);
1189 1190 1191
	nocrt = 1;
	return 0;
}
1192
static int thermal_tzp(const struct dmi_system_id *d) {
1193 1194

	if (tzp == 0) {
1195 1196
		pr_notice(PREFIX "%s detected: "
			  "enabling thermal zone polling\n", d->ident);
1197 1198 1199 1200
		tzp = 300;	/* 300 dS = 30 Seconds */
	}
	return 0;
}
1201
static int thermal_psv(const struct dmi_system_id *d) {
1202 1203

	if (psv == 0) {
1204 1205
		pr_notice(PREFIX "%s detected: "
			  "disabling all passive thermal trip points\n", d->ident);
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
		psv = -1;
	}
	return 0;
}

static struct dmi_system_id thermal_dmi_table[] __initdata = {
	/*
	 * Award BIOS on this AOpen makes thermal control almost worthless.
	 * http://bugzilla.kernel.org/show_bug.cgi?id=8842
	 */
	{
	 .callback = thermal_act,
	 .ident = "AOpen i915GMm-HFS",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
		},
	},
	{
	 .callback = thermal_psv,
	 .ident = "AOpen i915GMm-HFS",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
		},
	},
	{
	 .callback = thermal_tzp,
	 .ident = "AOpen i915GMm-HFS",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
		},
	},
1240 1241 1242 1243 1244 1245 1246 1247
	{
	 .callback = thermal_nocrt,
	 .ident = "Gigabyte GA-7ZX",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
		DMI_MATCH(DMI_BOARD_NAME, "7ZX"),
		},
	},
1248 1249 1250
	{}
};

L
Len Brown 已提交
1251
static int __init acpi_thermal_init(void)
L
Linus Torvalds 已提交
1252
{
L
Len Brown 已提交
1253
	int result = 0;
L
Linus Torvalds 已提交
1254

1255 1256
	dmi_check_system(thermal_dmi_table);

1257
	if (off) {
1258
		pr_notice(PREFIX "thermal control disabled\n");
1259 1260
		return -ENODEV;
	}
1261

1262 1263 1264 1265
	acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm");
	if (!acpi_thermal_pm_queue)
		return -ENODEV;

L
Linus Torvalds 已提交
1266
	result = acpi_bus_register_driver(&acpi_thermal_driver);
1267 1268
	if (result < 0) {
		destroy_workqueue(acpi_thermal_pm_queue);
1269
		return -ENODEV;
1270
	}
L
Linus Torvalds 已提交
1271

1272
	return 0;
L
Linus Torvalds 已提交
1273 1274
}

L
Len Brown 已提交
1275
static void __exit acpi_thermal_exit(void)
L
Linus Torvalds 已提交
1276 1277
{
	acpi_bus_unregister_driver(&acpi_thermal_driver);
1278
	destroy_workqueue(acpi_thermal_pm_queue);
L
Linus Torvalds 已提交
1279

1280
	return;
L
Linus Torvalds 已提交
1281 1282 1283 1284
}

module_init(acpi_thermal_init);
module_exit(acpi_thermal_exit);