i8k.c 19.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
 *
 * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
 *
6
 * Hwmon integration:
7
 * Copyright (C) 2011  Jean Delvare <jdelvare@suse.de>
G
Guenter Roeck 已提交
8
 * Copyright (C) 2013  Guenter Roeck <linux@roeck-us.net>
9
 *
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19 20
 * 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, 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.
 */

21 22
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
23 24 25 26
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
27
#include <linux/seq_file.h>
28
#include <linux/dmi.h>
29
#include <linux/capability.h>
30
#include <linux/mutex.h>
31 32
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
33 34
#include <linux/uaccess.h>
#include <linux/io.h>
G
Guenter Roeck 已提交
35
#include <linux/sched.h>
L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44

#include <linux/i8k.h>

#define I8K_SMM_FN_STATUS	0x0025
#define I8K_SMM_POWER_STATUS	0x0069
#define I8K_SMM_SET_FAN		0x01a3
#define I8K_SMM_GET_FAN		0x00a3
#define I8K_SMM_GET_SPEED	0x02a3
#define I8K_SMM_GET_TEMP	0x10a3
45 46
#define I8K_SMM_GET_DELL_SIG1	0xfea3
#define I8K_SMM_GET_DELL_SIG2	0xffa3
L
Linus Torvalds 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

#define I8K_FAN_MULT		30
#define I8K_MAX_TEMP		127

#define I8K_FN_NONE		0x00
#define I8K_FN_UP		0x01
#define I8K_FN_DOWN		0x02
#define I8K_FN_MUTE		0x04
#define I8K_FN_MASK		0x07
#define I8K_FN_SHIFT		8

#define I8K_POWER_AC		0x05
#define I8K_POWER_BATTERY	0x01

#define I8K_TEMPERATURE_BUG	1

63
static DEFINE_MUTEX(i8k_mutex);
64
static char bios_version[4];
65
static struct device *i8k_hwmon_dev;
66
static u32 i8k_hwmon_flags;
67
static int i8k_fan_mult;
68 69

#define I8K_HWMON_HAVE_TEMP1	(1 << 0)
70 71 72 73 74
#define I8K_HWMON_HAVE_TEMP2	(1 << 1)
#define I8K_HWMON_HAVE_TEMP3	(1 << 2)
#define I8K_HWMON_HAVE_TEMP4	(1 << 3)
#define I8K_HWMON_HAVE_FAN1	(1 << 4)
#define I8K_HWMON_HAVE_FAN2	(1 << 5)
L
Linus Torvalds 已提交
75 76 77 78 79

MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
MODULE_LICENSE("GPL");

80
static bool force;
L
Linus Torvalds 已提交
81 82 83
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force loading without checking for supported models");

84
static bool ignore_dmi;
85 86 87
module_param(ignore_dmi, bool, 0);
MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");

88
static bool restricted;
L
Linus Torvalds 已提交
89 90 91
module_param(restricted, bool, 0);
MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");

92
static bool power_status;
L
Linus Torvalds 已提交
93 94 95
module_param(power_status, bool, 0600);
MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");

96 97 98 99
static int fan_mult = I8K_FAN_MULT;
module_param(fan_mult, int, 0);
MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");

100
static int i8k_open_fs(struct inode *inode, struct file *file);
101
static long i8k_ioctl(struct file *, unsigned int, unsigned long);
L
Linus Torvalds 已提交
102

103
static const struct file_operations i8k_fops = {
104
	.owner		= THIS_MODULE,
105 106 107 108
	.open		= i8k_open_fs,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
109
	.unlocked_ioctl	= i8k_ioctl,
L
Linus Torvalds 已提交
110 111
};

112
struct smm_regs {
113
	unsigned int eax;
114 115 116 117 118
	unsigned int ebx __packed;
	unsigned int ecx __packed;
	unsigned int edx __packed;
	unsigned int esi __packed;
	unsigned int edi __packed;
119
};
L
Linus Torvalds 已提交
120

121
static inline const char *i8k_get_dmi_data(int field)
122
{
123
	const char *dmi_data = dmi_get_system_info(field);
124 125

	return dmi_data && *dmi_data ? dmi_data : "?";
126
}
L
Linus Torvalds 已提交
127 128 129 130

/*
 * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
 */
131
static int i8k_smm(struct smm_regs *regs)
L
Linus Torvalds 已提交
132
{
133 134
	int rc;
	int eax = regs->eax;
G
Guenter Roeck 已提交
135 136 137 138 139 140
	cpumask_var_t old_mask;

	/* SMM requires CPU 0 */
	if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
		return -ENOMEM;
	cpumask_copy(old_mask, &current->cpus_allowed);
G
Guenter Roeck 已提交
141 142 143
	rc = set_cpus_allowed_ptr(current, cpumask_of(0));
	if (rc)
		goto out;
G
Guenter Roeck 已提交
144 145 146 147
	if (smp_processor_id() != 0) {
		rc = -EBUSY;
		goto out;
	}
148

149
#if defined(CONFIG_X86_64)
150
	asm volatile("pushq %%rax\n\t"
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
		"movl 0(%%rax),%%edx\n\t"
		"pushq %%rdx\n\t"
		"movl 4(%%rax),%%ebx\n\t"
		"movl 8(%%rax),%%ecx\n\t"
		"movl 12(%%rax),%%edx\n\t"
		"movl 16(%%rax),%%esi\n\t"
		"movl 20(%%rax),%%edi\n\t"
		"popq %%rax\n\t"
		"out %%al,$0xb2\n\t"
		"out %%al,$0x84\n\t"
		"xchgq %%rax,(%%rsp)\n\t"
		"movl %%ebx,4(%%rax)\n\t"
		"movl %%ecx,8(%%rax)\n\t"
		"movl %%edx,12(%%rax)\n\t"
		"movl %%esi,16(%%rax)\n\t"
		"movl %%edi,20(%%rax)\n\t"
		"popq %%rdx\n\t"
		"movl %%edx,0(%%rax)\n\t"
L
Luca Tettamanti 已提交
169 170
		"pushfq\n\t"
		"popq %%rax\n\t"
171
		"andl $1,%%eax\n"
172
		: "=a"(rc)
173 174 175
		:    "a"(regs)
		:    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#else
176
	asm volatile("pushl %%eax\n\t"
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	    "movl 0(%%eax),%%edx\n\t"
	    "push %%edx\n\t"
	    "movl 4(%%eax),%%ebx\n\t"
	    "movl 8(%%eax),%%ecx\n\t"
	    "movl 12(%%eax),%%edx\n\t"
	    "movl 16(%%eax),%%esi\n\t"
	    "movl 20(%%eax),%%edi\n\t"
	    "popl %%eax\n\t"
	    "out %%al,$0xb2\n\t"
	    "out %%al,$0x84\n\t"
	    "xchgl %%eax,(%%esp)\n\t"
	    "movl %%ebx,4(%%eax)\n\t"
	    "movl %%ecx,8(%%eax)\n\t"
	    "movl %%edx,12(%%eax)\n\t"
	    "movl %%esi,16(%%eax)\n\t"
	    "movl %%edi,20(%%eax)\n\t"
	    "popl %%edx\n\t"
	    "movl %%edx,0(%%eax)\n\t"
	    "lahf\n\t"
	    "shrl $8,%%eax\n\t"
J
Jim Bos 已提交
197
	    "andl $1,%%eax\n"
198
	    : "=a"(rc)
199 200
	    :    "a"(regs)
	    :    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
201
#endif
202
	if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
G
Guenter Roeck 已提交
203
		rc = -EINVAL;
204

G
Guenter Roeck 已提交
205 206 207 208
out:
	set_cpus_allowed_ptr(current, old_mask);
	free_cpumask_var(old_mask);
	return rc;
L
Linus Torvalds 已提交
209 210 211 212 213 214 215
}

/*
 * Read the Fn key status.
 */
static int i8k_get_fn_status(void)
{
216
	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
217 218
	int rc;

219 220
	rc = i8k_smm(&regs);
	if (rc < 0)
221 222 223 224 225 226 227 228 229 230 231 232
		return rc;

	switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
	case I8K_FN_UP:
		return I8K_VOL_UP;
	case I8K_FN_DOWN:
		return I8K_VOL_DOWN;
	case I8K_FN_MUTE:
		return I8K_VOL_MUTE;
	default:
		return 0;
	}
L
Linus Torvalds 已提交
233 234 235 236 237 238 239
}

/*
 * Read the power status.
 */
static int i8k_get_power_status(void)
{
240
	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
241 242
	int rc;

243 244
	rc = i8k_smm(&regs);
	if (rc < 0)
245 246
		return rc;

247
	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
L
Linus Torvalds 已提交
248 249 250 251 252 253 254
}

/*
 * Read the fan status.
 */
static int i8k_get_fan_status(int fan)
{
255
	struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
L
Linus Torvalds 已提交
256

257
	regs.ebx = fan & 0xff;
258
	return i8k_smm(&regs) ? : regs.eax & 0xff;
L
Linus Torvalds 已提交
259 260 261 262 263 264 265
}

/*
 * Read the fan speed in RPM.
 */
static int i8k_get_fan_speed(int fan)
{
266
	struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
L
Linus Torvalds 已提交
267

268
	regs.ebx = fan & 0xff;
269
	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
L
Linus Torvalds 已提交
270 271 272 273 274 275 276
}

/*
 * Set the fan speed (off, low, high). Returns the new fan status.
 */
static int i8k_set_fan(int fan, int speed)
{
277
	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
L
Linus Torvalds 已提交
278

279 280
	speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
	regs.ebx = (fan & 0xff) | (speed << 8);
L
Linus Torvalds 已提交
281

282
	return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
L
Linus Torvalds 已提交
283 284 285 286 287
}

/*
 * Read the cpu temperature.
 */
288
static int i8k_get_temp(int sensor)
L
Linus Torvalds 已提交
289
{
290
	struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
291 292
	int rc;
	int temp;
L
Linus Torvalds 已提交
293 294

#ifdef I8K_TEMPERATURE_BUG
295
	static int prev[4];
L
Linus Torvalds 已提交
296
#endif
297
	regs.ebx = sensor & 0xff;
298 299
	rc = i8k_smm(&regs);
	if (rc < 0)
300
		return rc;
301

302
	temp = regs.eax & 0xff;
L
Linus Torvalds 已提交
303 304

#ifdef I8K_TEMPERATURE_BUG
305 306 307 308 309 310 311 312
	/*
	 * Sometimes the temperature sensor returns 0x99, which is out of range.
	 * In this case we return (once) the previous cached value. For example:
	 # 1003655137 00000058 00005a4b
	 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
	 # 1003655139 00000054 00005c52
	 */
	if (temp > I8K_MAX_TEMP) {
313 314
		temp = prev[sensor];
		prev[sensor] = I8K_MAX_TEMP;
315
	} else {
316
		prev[sensor] = temp;
317
	}
L
Linus Torvalds 已提交
318 319
#endif

320
	return temp;
L
Linus Torvalds 已提交
321 322
}

323
static int i8k_get_dell_signature(int req_fn)
L
Linus Torvalds 已提交
324
{
325
	struct smm_regs regs = { .eax = req_fn, };
326
	int rc;
L
Linus Torvalds 已提交
327

328 329
	rc = i8k_smm(&regs);
	if (rc < 0)
330
		return rc;
L
Linus Torvalds 已提交
331

332
	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
L
Linus Torvalds 已提交
333 334
}

335 336
static int
i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
337
{
338
	int val = 0;
339 340 341 342 343 344 345 346 347
	int speed;
	unsigned char buff[16];
	int __user *argp = (int __user *)arg;

	if (!argp)
		return -EINVAL;

	switch (cmd) {
	case I8K_BIOS_VERSION:
348 349
		val = (bios_version[0] << 16) |
				(bios_version[1] << 8) | bios_version[2];
350 351 352 353
		break;

	case I8K_MACHINE_ID:
		memset(buff, 0, 16);
354 355
		strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
			sizeof(buff));
356 357 358 359 360 361 362 363 364 365 366
		break;

	case I8K_FN_STATUS:
		val = i8k_get_fn_status();
		break;

	case I8K_POWER_STATUS:
		val = i8k_get_power_status();
		break;

	case I8K_GET_TEMP:
367
		val = i8k_get_temp(0);
368 369 370
		break;

	case I8K_GET_SPEED:
371
		if (copy_from_user(&val, argp, sizeof(int)))
372
			return -EFAULT;
373

374 375
		val = i8k_get_fan_speed(val);
		break;
L
Linus Torvalds 已提交
376

377
	case I8K_GET_FAN:
378
		if (copy_from_user(&val, argp, sizeof(int)))
379
			return -EFAULT;
380

381 382
		val = i8k_get_fan_status(val);
		break;
L
Linus Torvalds 已提交
383

384
	case I8K_SET_FAN:
385
		if (restricted && !capable(CAP_SYS_ADMIN))
386
			return -EPERM;
387 388

		if (copy_from_user(&val, argp, sizeof(int)))
389
			return -EFAULT;
390 391

		if (copy_from_user(&speed, argp + 1, sizeof(int)))
392
			return -EFAULT;
393

394 395
		val = i8k_set_fan(val, speed);
		break;
L
Linus Torvalds 已提交
396

397 398
	default:
		return -EINVAL;
L
Linus Torvalds 已提交
399 400
	}

401
	if (val < 0)
402
		return val;
L
Linus Torvalds 已提交
403

404 405
	switch (cmd) {
	case I8K_BIOS_VERSION:
406
		if (copy_to_user(argp, &val, 4))
407
			return -EFAULT;
408

409 410
		break;
	case I8K_MACHINE_ID:
411
		if (copy_to_user(argp, buff, 16))
412
			return -EFAULT;
413

414 415
		break;
	default:
416
		if (copy_to_user(argp, &val, sizeof(int)))
417
			return -EFAULT;
418

419
		break;
L
Linus Torvalds 已提交
420 421
	}

422
	return 0;
L
Linus Torvalds 已提交
423 424
}

425 426 427 428
static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
	long ret;

429
	mutex_lock(&i8k_mutex);
430
	ret = i8k_ioctl_unlocked(fp, cmd, arg);
431
	mutex_unlock(&i8k_mutex);
432 433 434 435

	return ret;
}

L
Linus Torvalds 已提交
436 437 438
/*
 * Print the information for /proc/i8k.
 */
439
static int i8k_proc_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
440
{
441
	int fn_key, cpu_temp, ac_power;
442 443
	int left_fan, right_fan, left_speed, right_speed;

444 445 446 447 448 449
	cpu_temp	= i8k_get_temp(0);			/* 11100 µs */
	left_fan	= i8k_get_fan_status(I8K_FAN_LEFT);	/*   580 µs */
	right_fan	= i8k_get_fan_status(I8K_FAN_RIGHT);	/*   580 µs */
	left_speed	= i8k_get_fan_speed(I8K_FAN_LEFT);	/*   580 µs */
	right_speed	= i8k_get_fan_speed(I8K_FAN_RIGHT);	/*   580 µs */
	fn_key		= i8k_get_fn_status();			/*   750 µs */
450
	if (power_status)
451
		ac_power = i8k_get_power_status();		/* 14700 µs */
452
	else
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
		ac_power = -1;

	/*
	 * Info:
	 *
	 * 1)  Format version (this will change if format changes)
	 * 2)  BIOS version
	 * 3)  BIOS machine ID
	 * 4)  Cpu temperature
	 * 5)  Left fan status
	 * 6)  Right fan status
	 * 7)  Left fan speed
	 * 8)  Right fan speed
	 * 9)  AC power
	 * 10) Fn Key status
	 */
469 470 471
	return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
			  I8K_PROC_FMT,
			  bios_version,
472
			  i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
473 474 475
			  cpu_temp,
			  left_fan, right_fan, left_speed, right_speed,
			  ac_power, fn_key);
L
Linus Torvalds 已提交
476 477
}

478
static int i8k_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
479
{
480
	return single_open(file, i8k_proc_show, NULL);
L
Linus Torvalds 已提交
481 482
}

483 484 485 486 487 488 489 490 491

/*
 * Hwmon interface
 */

static ssize_t i8k_hwmon_show_temp(struct device *dev,
				   struct device_attribute *devattr,
				   char *buf)
{
492 493
	int index = to_sensor_dev_attr(devattr)->index;
	int temp;
494

495 496 497 498
	temp = i8k_get_temp(index);
	if (temp < 0)
		return temp;
	return sprintf(buf, "%d\n", temp * 1000);
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
}

static ssize_t i8k_hwmon_show_fan(struct device *dev,
				  struct device_attribute *devattr,
				  char *buf)
{
	int index = to_sensor_dev_attr(devattr)->index;
	int fan_speed;

	fan_speed = i8k_get_fan_speed(index);
	if (fan_speed < 0)
		return fan_speed;
	return sprintf(buf, "%d\n", fan_speed);
}

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
static ssize_t i8k_hwmon_show_pwm(struct device *dev,
				  struct device_attribute *devattr,
				  char *buf)
{
	int index = to_sensor_dev_attr(devattr)->index;
	int status;

	status = i8k_get_fan_status(index);
	if (status < 0)
		return -EIO;
	return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255));
}

static ssize_t i8k_hwmon_set_pwm(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf, size_t count)
{
	int index = to_sensor_dev_attr(attr)->index;
	unsigned long val;
	int err;

	err = kstrtoul(buf, 10, &val);
	if (err)
		return err;
	val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2);

	mutex_lock(&i8k_mutex);
	err = i8k_set_fan(index, val);
	mutex_unlock(&i8k_mutex);

	return err < 0 ? -EIO : count;
}

547 548 549 550
static ssize_t i8k_hwmon_show_label(struct device *dev,
				    struct device_attribute *devattr,
				    char *buf)
{
551
	static const char *labels[3] = {
552 553 554 555 556 557 558 559 560
		"CPU",
		"Left Fan",
		"Right Fan",
	};
	int index = to_sensor_dev_attr(devattr)->index;

	return sprintf(buf, "%s\n", labels[index]);
}

561 562 563 564
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3);
565 566
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
			  I8K_FAN_LEFT);
567 568
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
			  i8k_hwmon_set_pwm, I8K_FAN_LEFT);
569 570
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
			  I8K_FAN_RIGHT);
571 572
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
			  i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
573 574 575 576 577
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);

static struct attribute *i8k_attrs[] = {
578
	&sensor_dev_attr_temp1_input.dev_attr.attr,	/* 0 */
579
	&sensor_dev_attr_temp1_label.dev_attr.attr,	/* 1 */
580 581 582 583
	&sensor_dev_attr_temp2_input.dev_attr.attr,	/* 2 */
	&sensor_dev_attr_temp3_input.dev_attr.attr,	/* 3 */
	&sensor_dev_attr_temp4_input.dev_attr.attr,	/* 4 */
	&sensor_dev_attr_fan1_input.dev_attr.attr,	/* 5 */
584 585 586 587 588
	&sensor_dev_attr_pwm1.dev_attr.attr,		/* 6 */
	&sensor_dev_attr_fan1_label.dev_attr.attr,	/* 7 */
	&sensor_dev_attr_fan2_input.dev_attr.attr,	/* 8 */
	&sensor_dev_attr_pwm2.dev_attr.attr,		/* 9 */
	&sensor_dev_attr_fan2_label.dev_attr.attr,	/* 10 */
589 590
	NULL
};
591

592 593
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
			      int index)
594
{
595 596 597
	if ((index == 0 || index == 1) &&
	    !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
		return 0;
598 599 600 601 602 603
	if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
		return 0;
	if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
		return 0;
	if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
		return 0;
604
	if (index >= 5 && index <= 7 &&
605 606
	    !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
		return 0;
607
	if (index >= 8 && index <= 10 &&
608 609 610 611
	    !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
		return 0;

	return attr->mode;
612 613
}

614 615 616 617 618 619
static const struct attribute_group i8k_group = {
	.attrs = i8k_attrs,
	.is_visible = i8k_is_visible,
};
__ATTRIBUTE_GROUPS(i8k);

620 621 622 623
static int __init i8k_init_hwmon(void)
{
	int err;

624
	i8k_hwmon_flags = 0;
625 626 627

	/* CPU temperature attributes, if temperature reading is OK */
	err = i8k_get_temp(0);
628 629
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
630 631 632 633 634 635 636 637 638 639
	/* check for additional temperature sensors */
	err = i8k_get_temp(1);
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
	err = i8k_get_temp(2);
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
	err = i8k_get_temp(3);
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
640 641 642

	/* Left fan attributes, if left fan is present */
	err = i8k_get_fan_status(I8K_FAN_LEFT);
643 644
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
645 646 647

	/* Right fan attributes, if right fan is present */
	err = i8k_get_fan_status(I8K_FAN_RIGHT);
648 649
	if (err >= 0)
		i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
650

651 652 653 654 655 656 657 658
	i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "i8k", NULL,
							  i8k_groups);
	if (IS_ERR(i8k_hwmon_dev)) {
		err = PTR_ERR(i8k_hwmon_dev);
		i8k_hwmon_dev = NULL;
		pr_err("hwmon registration failed (%d)\n", err);
		return err;
	}
659 660 661
	return 0;
}

662
static struct dmi_system_id i8k_dmi_table[] __initdata = {
663 664 665 666 667 668 669 670 671 672 673 674 675 676
	{
		.ident = "Dell Inspiron",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
		},
	},
	{
		.ident = "Dell Latitude",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
		},
	},
677 678 679 680 681 682 683 684 685 686 687 688 689 690
	{
		.ident = "Dell Inspiron 2",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
		},
	},
	{
		.ident = "Dell Latitude 2",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
		},
	},
691 692 693 694 695 696 697
	{	/* UK Inspiron 6400  */
		.ident = "Dell Inspiron 3",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
		},
	},
F
Frank Sorenson 已提交
698 699 700 701 702 703 704
	{
		.ident = "Dell Inspiron 3",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
		},
	},
705 706 707 708 709 710 711
	{
		.ident = "Dell Precision",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
		},
	},
F
Federico Heinz 已提交
712 713 714 715 716 717 718
	{
		.ident = "Dell Vostro",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
		},
	},
A
Alan Cox 已提交
719 720 721 722 723 724 725
	{
		.ident = "Dell XPS421",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
		},
	},
726 727 728 729 730 731
	{
		.ident = "Dell Studio",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
		},
732
		.driver_data = (void *)1,	/* fan multiplier override */
733
	},
G
Guenter Roeck 已提交
734 735 736 737 738 739
	{
		.ident = "Dell XPS M140",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
		},
740
		.driver_data = (void *)1,	/* fan multiplier override */
G
Guenter Roeck 已提交
741
	},
742
	{ }
743
};
L
Linus Torvalds 已提交
744 745 746 747 748 749

/*
 * Probe for the presence of a supported laptop.
 */
static int __init i8k_probe(void)
{
750
	const struct dmi_system_id *id;
751

L
Linus Torvalds 已提交
752
	/*
753
	 * Get DMI information
L
Linus Torvalds 已提交
754
	 */
755 756 757 758
	if (!dmi_check_system(i8k_dmi_table)) {
		if (!ignore_dmi && !force)
			return -ENODEV;

759 760
		pr_info("not running on a supported Dell system.\n");
		pr_info("vendor=%s, model=%s, version=%s\n",
761 762 763
			i8k_get_dmi_data(DMI_SYS_VENDOR),
			i8k_get_dmi_data(DMI_PRODUCT_NAME),
			i8k_get_dmi_data(DMI_BIOS_VERSION));
L
Linus Torvalds 已提交
764
	}
765

766 767
	strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
		sizeof(bios_version));
768

L
Linus Torvalds 已提交
769
	/*
770
	 * Get SMM Dell signature
L
Linus Torvalds 已提交
771
	 */
772 773
	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
774
		pr_err("unable to get SMM Dell signature\n");
775 776
		if (!force)
			return -ENODEV;
L
Linus Torvalds 已提交
777 778
	}

779 780 781 782 783
	i8k_fan_mult = fan_mult;
	id = dmi_first_match(i8k_dmi_table);
	if (id && fan_mult == I8K_FAN_MULT && id->driver_data)
		i8k_fan_mult = (unsigned long)id->driver_data;

784
	return 0;
L
Linus Torvalds 已提交
785 786
}

787
static int __init i8k_init(void)
L
Linus Torvalds 已提交
788
{
789
	struct proc_dir_entry *proc_i8k;
790
	int err;
791 792

	/* Are we running on an supported laptop? */
793
	if (i8k_probe())
794 795 796
		return -ENODEV;

	/* Register the proc entry */
797
	proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops);
798
	if (!proc_i8k)
799
		return -ENOENT;
800

801 802 803 804
	err = i8k_init_hwmon();
	if (err)
		goto exit_remove_proc;

805
	return 0;
806 807 808 809

 exit_remove_proc:
	remove_proc_entry("i8k", NULL);
	return err;
L
Linus Torvalds 已提交
810 811
}

812
static void __exit i8k_exit(void)
L
Linus Torvalds 已提交
813
{
814
	hwmon_device_unregister(i8k_hwmon_dev);
815
	remove_proc_entry("i8k", NULL);
L
Linus Torvalds 已提交
816 817
}

818 819
module_init(i8k_init);
module_exit(i8k_exit);