11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	    See http://www.debian.org/~dz/i8k/ for more information
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	    and for latest version of this driver.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Hwmon integration:
9949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Copyright (C) 2011  Jean Delvare <khali@linux-fr.org>
10949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License as published by the
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Free Software Foundation; either version 2, or (at your option) any
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * later version.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * General Public License for more details.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/proc_fs.h>
26352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov#include <linux/seq_file.h>
27e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov#include <linux/dmi.h>
287e80d0d0b64f5c00b0ac7e623d96189309c298caAlexey Dobriyan#include <linux/capability.h>
29613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann#include <linux/mutex.h>
30949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare#include <linux/hwmon.h>
31949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare#include <linux/hwmon-sysfs.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/i8k.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov#define I8K_VERSION		"1.14 21/02/2005"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_FN_STATUS	0x0025
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_POWER_STATUS	0x0069
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_SET_FAN		0x01a3
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_FAN		0x00a3
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_SPEED	0x02a3
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_TEMP	0x10a3
457e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov#define I8K_SMM_GET_DELL_SIG1	0xfea3
467e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov#define I8K_SMM_GET_DELL_SIG2	0xffa3
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_BIOS_VERSION	0x00a6
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FAN_MULT		30
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_MAX_TEMP		127
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_NONE		0x00
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_UP		0x01
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_DOWN		0x02
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_MUTE		0x04
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_MASK		0x07
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_SHIFT		8
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_POWER_AC		0x05
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_POWER_BATTERY	0x01
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_TEMPERATURE_BUG	1
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
64613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmannstatic DEFINE_MUTEX(i8k_mutex);
65e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovstatic char bios_version[4];
66949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic struct device *i8k_hwmon_dev;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7290ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool force;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(force, bool, 0);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(force, "Force loading without checking for supported models");
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool ignore_dmi;
77e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovmodule_param(ignore_dmi, bool, 0);
78e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry TorokhovMODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
79e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov
8090ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool restricted;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(restricted, bool, 0);
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool power_status;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(power_status, bool, 0600);
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
884ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisingerstatic int fan_mult = I8K_FAN_MULT;
894ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisingermodule_param(fan_mult, int, 0);
904ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen EisingerMODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
914ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisinger
92352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_open_fs(struct inode *inode, struct file *file);
93d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic long i8k_ioctl(struct file *, unsigned int, unsigned long);
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9562322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations i8k_fops = {
961b50221738108c438d5f25c7a043fb89e9e27044Denis V. Lunev	.owner		= THIS_MODULE,
97352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	.open		= i8k_open_fs,
98352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	.read		= seq_read,
99352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	.llseek		= seq_lseek,
100352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	.release	= single_release,
101d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker	.unlocked_ioctl	= i8k_ioctl,
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1048378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstruct smm_regs {
105dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int eax;
106dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int ebx __attribute__ ((packed));
107dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int ecx __attribute__ ((packed));
108dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int edx __attribute__ ((packed));
109dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int esi __attribute__ ((packed));
110dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned int edi __attribute__ ((packed));
1118378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov};
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131855256c497ecfefc730df6032243f26855ce52cJeff Garzikstatic inline const char *i8k_get_dmi_data(int field)
114e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov{
1151855256c497ecfefc730df6032243f26855ce52cJeff Garzik	const char *dmi_data = dmi_get_system_info(field);
1164f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov
1174f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov	return dmi_data && *dmi_data ? dmi_data : "?";
118e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1238378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic int i8k_smm(struct smm_regs *regs)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
125dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int rc;
126dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int eax = regs->eax;
127dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
128fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#if defined(CONFIG_X86_64)
12922d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos	asm volatile("pushq %%rax\n\t"
130fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 0(%%rax),%%edx\n\t"
131fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"pushq %%rdx\n\t"
132fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 4(%%rax),%%ebx\n\t"
133fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 8(%%rax),%%ecx\n\t"
134fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 12(%%rax),%%edx\n\t"
135fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 16(%%rax),%%esi\n\t"
136fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl 20(%%rax),%%edi\n\t"
137fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"popq %%rax\n\t"
138fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"out %%al,$0xb2\n\t"
139fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"out %%al,$0x84\n\t"
140fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"xchgq %%rax,(%%rsp)\n\t"
141fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%ebx,4(%%rax)\n\t"
142fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%ecx,8(%%rax)\n\t"
143fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%edx,12(%%rax)\n\t"
144fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%esi,16(%%rax)\n\t"
145fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%edi,20(%%rax)\n\t"
146fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"popq %%rdx\n\t"
147fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"movl %%edx,0(%%rax)\n\t"
148bc1f419c76a2d6450413ce4349f4e4a07be011d5Luca Tettamanti		"pushfq\n\t"
149bc1f419c76a2d6450413ce4349f4e4a07be011d5Luca Tettamanti		"popq %%rax\n\t"
150fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		"andl $1,%%eax\n"
15122d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos		:"=a"(rc)
152fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		:    "a"(regs)
153fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith		:    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
154fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#else
15522d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos	asm volatile("pushl %%eax\n\t"
156dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 0(%%eax),%%edx\n\t"
157dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "push %%edx\n\t"
158dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 4(%%eax),%%ebx\n\t"
159dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 8(%%eax),%%ecx\n\t"
160dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 12(%%eax),%%edx\n\t"
161dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 16(%%eax),%%esi\n\t"
162dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl 20(%%eax),%%edi\n\t"
163dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "popl %%eax\n\t"
164dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "out %%al,$0xb2\n\t"
165dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "out %%al,$0x84\n\t"
166dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "xchgl %%eax,(%%esp)\n\t"
167dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%ebx,4(%%eax)\n\t"
168dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%ecx,8(%%eax)\n\t"
169dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%edx,12(%%eax)\n\t"
170dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%esi,16(%%eax)\n\t"
171dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%edi,20(%%eax)\n\t"
172dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "popl %%edx\n\t"
173dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "movl %%edx,0(%%eax)\n\t"
174dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "lahf\n\t"
175dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    "shrl $8,%%eax\n\t"
1766b4e81db2552bad04100e7d5ddeed7e848f53b48Jim Bos	    "andl $1,%%eax\n"
17722d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos	    :"=a"(rc)
178dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    :    "a"(regs)
179dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	    :    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
180fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#endif
1818378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
182dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return -EINVAL;
183dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
184dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	return 0;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the bios version. Return the version as an integer corresponding
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to the ascii value, for example "A17" is returned as 0x00413137.
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_bios_version(void)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1938378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, };
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1958378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	return i8k_smm(&regs) ? : regs.eax;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the Fn key status.
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fn_status(void)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2038378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
204dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int rc;
205dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
2068378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if ((rc = i8k_smm(&regs)) < 0)
207dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return rc;
208dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
209dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
210dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_FN_UP:
211dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return I8K_VOL_UP;
212dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_FN_DOWN:
213dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return I8K_VOL_DOWN;
214dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_FN_MUTE:
215dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return I8K_VOL_MUTE;
216dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	default:
217dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return 0;
218dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	}
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the power status.
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_power_status(void)
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2268378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
227dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int rc;
228dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
2298378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if ((rc = i8k_smm(&regs)) < 0)
230dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return rc;
231dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
2328378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the fan status.
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fan_status(int fan)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2408378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
242dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	regs.ebx = fan & 0xff;
2438378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	return i8k_smm(&regs) ? : regs.eax & 0xff;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the fan speed in RPM.
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fan_speed(int fan)
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2518378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
253dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	regs.ebx = fan & 0xff;
2544ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisinger	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * fan_mult;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Set the fan speed (off, low, high). Returns the new fan status.
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_set_fan(int fan, int speed)
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2628378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
264dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
265dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	regs.ebx = (fan & 0xff) | (speed << 8);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2678378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the cpu temperature.
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2737e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhovstatic int i8k_get_temp(int sensor)
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2758378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
276dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int rc;
277dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int temp;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef I8K_TEMPERATURE_BUG
2808378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	static int prev;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2827e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	regs.ebx = sensor & 0xff;
2838378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if ((rc = i8k_smm(&regs)) < 0)
284dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return rc;
2858378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
286dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	temp = regs.eax & 0xff;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef I8K_TEMPERATURE_BUG
289dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	/*
290dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * Sometimes the temperature sensor returns 0x99, which is out of range.
291dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * In this case we return (once) the previous cached value. For example:
292dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 # 1003655137 00000058 00005a4b
293dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
294dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 # 1003655139 00000054 00005c52
295dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 */
296dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	if (temp > I8K_MAX_TEMP) {
297dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		temp = prev;
298dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		prev = I8K_MAX_TEMP;
299dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	} else {
300dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		prev = temp;
301dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	}
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
304dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	return temp;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3077e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhovstatic int i8k_get_dell_signature(int req_fn)
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3097e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	struct smm_regs regs = { .eax = req_fn, };
310dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int rc;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3128378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if ((rc = i8k_smm(&regs)) < 0)
313dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return rc;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3158378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
318d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic int
319d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckeri8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
321e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	int val = 0;
322dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int speed;
323dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	unsigned char buff[16];
324dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int __user *argp = (int __user *)arg;
325dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
326dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	if (!argp)
327dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return -EINVAL;
328dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
329dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	switch (cmd) {
330dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_BIOS_VERSION:
331dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_get_bios_version();
332dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
333dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
334dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_MACHINE_ID:
335dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		memset(buff, 0, 16);
336e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff));
337dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
338dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
339dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_FN_STATUS:
340dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_get_fn_status();
341dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
342dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
343dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_POWER_STATUS:
344dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_get_power_status();
345dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
346dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
347dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_GET_TEMP:
3487e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		val = i8k_get_temp(0);
349dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
350dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
351dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_GET_SPEED:
3528378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_from_user(&val, argp, sizeof(int)))
353dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3548378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
355dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_get_fan_speed(val);
356dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
358dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_GET_FAN:
3598378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_from_user(&val, argp, sizeof(int)))
360dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3618378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
362dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_get_fan_status(val);
363dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
365dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_SET_FAN:
3668378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (restricted && !capable(CAP_SYS_ADMIN))
367dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EPERM;
3688378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
3698378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_from_user(&val, argp, sizeof(int)))
370dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3718378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
3728378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_from_user(&speed, argp + 1, sizeof(int)))
373dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3748378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
375dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		val = i8k_set_fan(val, speed);
376dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
378dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	default:
379dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return -EINVAL;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3828378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if (val < 0)
383dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return val;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
385dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	switch (cmd) {
386dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_BIOS_VERSION:
3878378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_to_user(argp, &val, 4))
388dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3898378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
390dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
391dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	case I8K_MACHINE_ID:
3928378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_to_user(argp, buff, 16))
393dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3948378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
395dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
396dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	default:
3978378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov		if (copy_to_user(argp, &val, sizeof(int)))
398dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov			return -EFAULT;
3998378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov
400dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		break;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
403dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	return 0;
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
406d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
407d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker{
408d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker	long ret;
409d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker
410613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann	mutex_lock(&i8k_mutex);
411d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker	ret = i8k_ioctl_unlocked(fp, cmd, arg);
412613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann	mutex_unlock(&i8k_mutex);
413d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker
414d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker	return ret;
415d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker}
416d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Print the information for /proc/i8k.
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
420352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_proc_show(struct seq_file *seq, void *offset)
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
422352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	int fn_key, cpu_temp, ac_power;
423dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int left_fan, right_fan, left_speed, right_speed;
424dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
42596de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	cpu_temp	= i8k_get_temp(0);			/* 11100 µs */
42696de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	left_fan	= i8k_get_fan_status(I8K_FAN_LEFT);	/*   580 µs */
42796de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	right_fan	= i8k_get_fan_status(I8K_FAN_RIGHT);	/*   580 µs */
42896de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	left_speed	= i8k_get_fan_speed(I8K_FAN_LEFT);	/*   580 µs */
42996de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	right_speed	= i8k_get_fan_speed(I8K_FAN_RIGHT);	/*   580 µs */
43096de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt	fn_key		= i8k_get_fn_status();			/*   750 µs */
4318378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	if (power_status)
43296de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt		ac_power = i8k_get_power_status();		/* 14700 µs */
4338378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov	else
434dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		ac_power = -1;
435dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
436dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	/*
437dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * Info:
438dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 *
439dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 1)  Format version (this will change if format changes)
440dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 2)  BIOS version
441dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 3)  BIOS machine ID
442dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 4)  Cpu temperature
443dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 5)  Left fan status
444dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 6)  Right fan status
445dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 7)  Left fan speed
446dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 8)  Right fan speed
447dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 9)  AC power
448dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * 10) Fn Key status
449dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 */
450352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
451352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov			  I8K_PROC_FMT,
452352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov			  bios_version,
4534f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov			  i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
454352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov			  cpu_temp,
455352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov			  left_fan, right_fan, left_speed, right_speed,
456352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov			  ac_power, fn_key);
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
459352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_open_fs(struct inode *inode, struct file *file)
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
461352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	return single_open(file, i8k_proc_show, NULL);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
464949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
465949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare/*
466949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Hwmon interface
467949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare */
468949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
469949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_temp(struct device *dev,
470949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				   struct device_attribute *devattr,
471949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				   char *buf)
472949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
473949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int cpu_temp;
474949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
475949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	cpu_temp = i8k_get_temp(0);
476949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (cpu_temp < 0)
477949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		return cpu_temp;
478949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return sprintf(buf, "%d\n", cpu_temp * 1000);
479949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
480949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
481949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_fan(struct device *dev,
482949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				  struct device_attribute *devattr,
483949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				  char *buf)
484949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
485949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int index = to_sensor_dev_attr(devattr)->index;
486949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int fan_speed;
487949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
488949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	fan_speed = i8k_get_fan_speed(index);
489949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (fan_speed < 0)
490949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		return fan_speed;
491949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return sprintf(buf, "%d\n", fan_speed);
492949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
493949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
494949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_label(struct device *dev,
495949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				    struct device_attribute *devattr,
496949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				    char *buf)
497949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
498949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	static const char *labels[4] = {
499949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		"i8k",
500949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		"CPU",
501949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		"Left Fan",
502949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		"Right Fan",
503949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	};
504949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int index = to_sensor_dev_attr(devattr)->index;
505949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
506949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return sprintf(buf, "%s\n", labels[index]);
507949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
508949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
509949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
510949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
511949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			  I8K_FAN_LEFT);
512949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
513949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			  I8K_FAN_RIGHT);
514949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
515949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
516949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
517949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
518949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
519949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic void i8k_hwmon_remove_files(struct device *dev)
520949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
521949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &dev_attr_temp1_input);
522949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
523949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
524949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
525949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
526949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
527949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
528949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
529949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
530949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic int __init i8k_init_hwmon(void)
531949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
532949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int err;
533949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
534949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	i8k_hwmon_dev = hwmon_device_register(NULL);
535949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (IS_ERR(i8k_hwmon_dev)) {
536949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = PTR_ERR(i8k_hwmon_dev);
537949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		i8k_hwmon_dev = NULL;
538949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
539949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		return err;
540949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	}
541949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
542949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	/* Required name attribute */
543949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	err = device_create_file(i8k_hwmon_dev,
544949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare				 &sensor_dev_attr_name.dev_attr);
545949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (err)
546949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		goto exit_unregister;
547949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
548949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	/* CPU temperature attributes, if temperature reading is OK */
549949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	err = i8k_get_temp(0);
550949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (err < 0) {
551949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		dev_dbg(i8k_hwmon_dev,
552949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			"Not creating temperature attributes (%d)\n", err);
553949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	} else {
554949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
555949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
556949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
557949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev,
558949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare					 &sensor_dev_attr_temp1_label.dev_attr);
559949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
560949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
561949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	}
562949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
563949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	/* Left fan attributes, if left fan is present */
564949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	err = i8k_get_fan_status(I8K_FAN_LEFT);
565949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (err < 0) {
566949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		dev_dbg(i8k_hwmon_dev,
567949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			"Not creating %s fan attributes (%d)\n", "left", err);
568949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	} else {
569949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev,
570949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare					 &sensor_dev_attr_fan1_input.dev_attr);
571949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
572949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
573949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev,
574949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare					 &sensor_dev_attr_fan1_label.dev_attr);
575949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
576949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
577949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	}
578949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
579949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	/* Right fan attributes, if right fan is present */
580949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	err = i8k_get_fan_status(I8K_FAN_RIGHT);
581949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (err < 0) {
582949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		dev_dbg(i8k_hwmon_dev,
583949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			"Not creating %s fan attributes (%d)\n", "right", err);
584949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	} else {
585949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev,
586949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare					 &sensor_dev_attr_fan2_input.dev_attr);
587949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
588949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
589949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		err = device_create_file(i8k_hwmon_dev,
590949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare					 &sensor_dev_attr_fan2_label.dev_attr);
591949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		if (err)
592949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare			goto exit_remove_files;
593949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	}
594949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
595949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return 0;
596949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
597949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_remove_files:
598949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	i8k_hwmon_remove_files(i8k_hwmon_dev);
599949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_unregister:
600949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	hwmon_device_unregister(i8k_hwmon_dev);
601949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return err;
602949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
603949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
604949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic void __exit i8k_exit_hwmon(void)
605949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{
606949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	i8k_hwmon_remove_files(i8k_hwmon_dev);
607949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	hwmon_device_unregister(i8k_hwmon_dev);
608949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare}
609949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
610e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovstatic struct dmi_system_id __initdata i8k_dmi_table[] = {
611e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	{
612e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		.ident = "Dell Inspiron",
613e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		.matches = {
614e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
615e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
616e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		},
617e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	},
618e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	{
619e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		.ident = "Dell Latitude",
620e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		.matches = {
621e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
622e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
623e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		},
624e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	},
6257e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	{
6267e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		.ident = "Dell Inspiron 2",
6277e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		.matches = {
6287e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
6297e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
6307e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		},
6317e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	},
6327e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	{
6337e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		.ident = "Dell Latitude 2",
6347e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		.matches = {
6357e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
6367e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
6377e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov		},
6387e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	},
639a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne	{	/* UK Inspiron 6400  */
640a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne		.ident = "Dell Inspiron 3",
641a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne		.matches = {
642a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
643a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne			DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
644a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne		},
645a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne	},
64648103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson	{
64748103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson		.ident = "Dell Inspiron 3",
64848103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson		.matches = {
64948103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
65048103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson			DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
65148103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson		},
65248103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson	},
6537ab21a8692094872298df172f54d55cba72fd308Andy Spencer	{
6547ab21a8692094872298df172f54d55cba72fd308Andy Spencer		.ident = "Dell Precision",
6557ab21a8692094872298df172f54d55cba72fd308Andy Spencer		.matches = {
6567ab21a8692094872298df172f54d55cba72fd308Andy Spencer			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
6577ab21a8692094872298df172f54d55cba72fd308Andy Spencer			DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
6587ab21a8692094872298df172f54d55cba72fd308Andy Spencer		},
6597ab21a8692094872298df172f54d55cba72fd308Andy Spencer	},
660bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz	{
661bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz		.ident = "Dell Vostro",
662bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz		.matches = {
663bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
664bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
665bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz		},
666bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz	},
667bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz        { }
668e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov};
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Probe for the presence of a supported laptop.
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init i8k_probe(void)
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
675dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	char buff[4];
676dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	int version;
677dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
679dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * Get DMI information
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
681e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	if (!dmi_check_system(i8k_dmi_table)) {
682e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		if (!ignore_dmi && !force)
683e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			return -ENODEV;
684e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov
685e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		printk(KERN_INFO "i8k: not running on a supported Dell system.\n");
686dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
687e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			i8k_get_dmi_data(DMI_SYS_VENDOR),
688e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			i8k_get_dmi_data(DMI_PRODUCT_NAME),
689e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			i8k_get_dmi_data(DMI_BIOS_VERSION));
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
691dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
692e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version));
693e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
695dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * Get SMM Dell signature
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6977e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
6987e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
699e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		printk(KERN_ERR "i8k: unable to get SMM Dell signature\n");
700e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		if (!force)
701e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			return -ENODEV;
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
704dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	/*
705dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 * Get SMM BIOS version.
706dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	 */
707dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	version = i8k_get_bios_version();
708dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	if (version <= 0) {
709e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n");
710dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	} else {
711dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		buff[0] = (version >> 16) & 0xff;
712dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		buff[1] = (version >> 8) & 0xff;
713dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		buff[2] = (version) & 0xff;
714dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		buff[3] = '\0';
715dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		/*
716dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		 * If DMI BIOS version is unknown use SMM BIOS version.
717dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		 */
718e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		if (!dmi_get_system_info(DMI_BIOS_VERSION))
719e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			strlcpy(bios_version, buff, sizeof(bios_version));
720e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov
721dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		/*
722dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		 * Check if the two versions match.
723dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		 */
724e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov		if (strncmp(buff, bios_version, sizeof(bios_version)) != 0)
725e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov			printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n",
726e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov				buff, bios_version);
727dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	}
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
729dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	return 0;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7328378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic int __init i8k_init(void)
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
734dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	struct proc_dir_entry *proc_i8k;
735949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	int err;
736dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
737dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	/* Are we running on an supported laptop? */
738e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov	if (i8k_probe())
739dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return -ENODEV;
740dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
741dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	/* Register the proc entry */
7421b50221738108c438d5f25c7a043fb89e9e27044Denis V. Lunev	proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops);
743352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov	if (!proc_i8k)
744dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov		return -ENOENT;
745352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov
746949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	err = i8k_init_hwmon();
747949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	if (err)
748949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare		goto exit_remove_proc;
749949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
750dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	printk(KERN_INFO
751dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	       "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
752dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	       I8K_VERSION);
753dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov
754dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	return 0;
755949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare
756949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_remove_proc:
757949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	remove_proc_entry("i8k", NULL);
758949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	return err;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7618378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic void __exit i8k_exit(void)
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
763949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare	i8k_exit_hwmon();
764dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov	remove_proc_entry("i8k", NULL);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7678378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovmodule_init(i8k_init);
7688378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovmodule_exit(i8k_exit);
769