Blame view

drivers/char/i8k.c 17.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
   *	    See http://www.debian.org/~dz/i8k/ for more information
   *	    and for latest version of this driver.
   *
   * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
   *
949a9d700   Jean Delvare   i8k: Integrate wi...
8
9
10
   * Hwmon integration:
   * Copyright (C) 2011  Jean Delvare <khali@linux-fr.org>
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   * 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.
   */
  
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/init.h>
  #include <linux/proc_fs.h>
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
26
  #include <linux/seq_file.h>
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
27
  #include <linux/dmi.h>
7e80d0d0b   Alexey Dobriyan   i386: sched.h inc...
28
  #include <linux/capability.h>
613655fa3   Arnd Bergmann   drivers: autoconv...
29
  #include <linux/mutex.h>
949a9d700   Jean Delvare   i8k: Integrate wi...
30
31
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
  #include <asm/uaccess.h>
  #include <asm/io.h>
  
  #include <linux/i8k.h>
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
36
  #define I8K_VERSION		"1.14 21/02/2005"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
  
  #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
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
44
45
  #define I8K_SMM_GET_DELL_SIG1	0xfea3
  #define I8K_SMM_GET_DELL_SIG2	0xffa3
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  #define I8K_SMM_BIOS_VERSION	0x00a6
  
  #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
613655fa3   Arnd Bergmann   drivers: autoconv...
62
  static DEFINE_MUTEX(i8k_mutex);
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
63
  static char bios_version[4];
949a9d700   Jean Delvare   i8k: Integrate wi...
64
  static struct device *i8k_hwmon_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
  
  MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
  MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
  MODULE_LICENSE("GPL");
90ab5ee94   Rusty Russell   module_param: mak...
69
  static bool force;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  module_param(force, bool, 0);
  MODULE_PARM_DESC(force, "Force loading without checking for supported models");
90ab5ee94   Rusty Russell   module_param: mak...
72
  static bool ignore_dmi;
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
73
74
  module_param(ignore_dmi, bool, 0);
  MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
90ab5ee94   Rusty Russell   module_param: mak...
75
  static bool restricted;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
  module_param(restricted, bool, 0);
  MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
90ab5ee94   Rusty Russell   module_param: mak...
78
  static bool power_status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  module_param(power_status, bool, 0600);
  MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
4ed99a27d   Jochen Eisinger   i8k: make fan mul...
81
82
83
  static int fan_mult = I8K_FAN_MULT;
  module_param(fan_mult, int, 0);
  MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
84
  static int i8k_open_fs(struct inode *inode, struct file *file);
d79b6f4de   Frederic Weisbecker   procfs: Push down...
85
  static long i8k_ioctl(struct file *, unsigned int, unsigned long);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

62322d255   Arjan van de Ven   [PATCH] make more...
87
  static const struct file_operations i8k_fops = {
1b5022173   Denis V. Lunev   drivers: use non-...
88
  	.owner		= THIS_MODULE,
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
89
90
91
92
  	.open		= i8k_open_fs,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
d79b6f4de   Frederic Weisbecker   procfs: Push down...
93
  	.unlocked_ioctl	= i8k_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  };
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
95
  struct smm_regs {
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
96
97
98
99
100
101
  	unsigned int eax;
  	unsigned int ebx __attribute__ ((packed));
  	unsigned int ecx __attribute__ ((packed));
  	unsigned int edx __attribute__ ((packed));
  	unsigned int esi __attribute__ ((packed));
  	unsigned int edi __attribute__ ((packed));
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
102
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103

1855256c4   Jeff Garzik   drivers/firmware:...
104
  static inline const char *i8k_get_dmi_data(int field)
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
105
  {
1855256c4   Jeff Garzik   drivers/firmware:...
106
  	const char *dmi_data = dmi_get_system_info(field);
4f005551a   Dmitry Torokhov   [PATCH] I8K: fix ...
107
108
  
  	return dmi_data && *dmi_data ? dmi_data : "?";
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
109
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
  
  /*
   * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
   */
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
114
  static int i8k_smm(struct smm_regs *regs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  {
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
116
117
  	int rc;
  	int eax = regs->eax;
fe04f22fd   Bradley Smith   I8K: allow i8k dr...
118
  #if defined(CONFIG_X86_64)
22d3243de   Jim Bos   Fix gcc 4.5.1 mis...
119
120
  	asm volatile("pushq %%rax
  \t"
fe04f22fd   Bradley Smith   I8K: allow i8k dr...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  		"movl 0(%%rax),%%edx
  \t"
  		"pushq %%rdx
  \t"
  		"movl 4(%%rax),%%ebx
  \t"
  		"movl 8(%%rax),%%ecx
  \t"
  		"movl 12(%%rax),%%edx
  \t"
  		"movl 16(%%rax),%%esi
  \t"
  		"movl 20(%%rax),%%edi
  \t"
  		"popq %%rax
  \t"
  		"out %%al,$0xb2
  \t"
  		"out %%al,$0x84
  \t"
  		"xchgq %%rax,(%%rsp)
  \t"
  		"movl %%ebx,4(%%rax)
  \t"
  		"movl %%ecx,8(%%rax)
  \t"
  		"movl %%edx,12(%%rax)
  \t"
  		"movl %%esi,16(%%rax)
  \t"
  		"movl %%edi,20(%%rax)
  \t"
  		"popq %%rdx
  \t"
  		"movl %%edx,0(%%rax)
  \t"
bc1f419c7   Luca Tettamanti   i8k: Avoid lahf i...
157
158
159
160
  		"pushfq
  \t"
  		"popq %%rax
  \t"
fe04f22fd   Bradley Smith   I8K: allow i8k dr...
161
162
  		"andl $1,%%eax
  "
22d3243de   Jim Bos   Fix gcc 4.5.1 mis...
163
  		:"=a"(rc)
fe04f22fd   Bradley Smith   I8K: allow i8k dr...
164
165
166
  		:    "a"(regs)
  		:    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
  #else
22d3243de   Jim Bos   Fix gcc 4.5.1 mis...
167
168
  	asm volatile("pushl %%eax
  \t"
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  	    "movl 0(%%eax),%%edx
  \t"
  	    "push %%edx
  \t"
  	    "movl 4(%%eax),%%ebx
  \t"
  	    "movl 8(%%eax),%%ecx
  \t"
  	    "movl 12(%%eax),%%edx
  \t"
  	    "movl 16(%%eax),%%esi
  \t"
  	    "movl 20(%%eax),%%edi
  \t"
  	    "popl %%eax
  \t"
  	    "out %%al,$0xb2
  \t"
  	    "out %%al,$0x84
  \t"
  	    "xchgl %%eax,(%%esp)
  \t"
  	    "movl %%ebx,4(%%eax)
  \t"
  	    "movl %%ecx,8(%%eax)
  \t"
  	    "movl %%edx,12(%%eax)
  \t"
  	    "movl %%esi,16(%%eax)
  \t"
  	    "movl %%edi,20(%%eax)
  \t"
  	    "popl %%edx
  \t"
  	    "movl %%edx,0(%%eax)
  \t"
  	    "lahf
  \t"
  	    "shrl $8,%%eax
  \t"
6b4e81db2   Jim Bos   i8k: Tell gcc tha...
209
210
  	    "andl $1,%%eax
  "
22d3243de   Jim Bos   Fix gcc 4.5.1 mis...
211
  	    :"=a"(rc)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
212
213
  	    :    "a"(regs)
  	    :    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
fe04f22fd   Bradley Smith   I8K: allow i8k dr...
214
  #endif
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
215
  	if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
216
  		return -EINVAL;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
217
218
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
224
225
226
  }
  
  /*
   * Read the bios version. Return the version as an integer corresponding
   * to the ascii value, for example "A17" is returned as 0x00413137.
   */
  static int i8k_get_bios_version(void)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
227
  	struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
229
  	return i8k_smm(&regs) ? : regs.eax;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
   * Read the Fn key status.
   */
  static int i8k_get_fn_status(void)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
237
  	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
238
  	int rc;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
239
  	if ((rc = i8k_smm(&regs)) < 0)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
240
  		return rc;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
241
242
243
244
245
246
247
248
249
250
251
  
  	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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
  }
  
  /*
   * Read the power status.
   */
  static int i8k_get_power_status(void)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
259
  	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
260
  	int rc;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
261
  	if ((rc = i8k_smm(&regs)) < 0)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
262
  		return rc;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
263

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
264
  	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
  }
  
  /*
   * Read the fan status.
   */
  static int i8k_get_fan_status(int fan)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
272
  	struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
274
  	regs.ebx = fan & 0xff;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
275
  	return i8k_smm(&regs) ? : regs.eax & 0xff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
281
282
  }
  
  /*
   * Read the fan speed in RPM.
   */
  static int i8k_get_fan_speed(int fan)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
283
  	struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
285
  	regs.ebx = fan & 0xff;
4ed99a27d   Jochen Eisinger   i8k: make fan mul...
286
  	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * fan_mult;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
  }
  
  /*
   * Set the fan speed (off, low, high). Returns the new fan status.
   */
  static int i8k_set_fan(int fan, int speed)
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
294
  	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
296
  	speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
297
  	regs.ebx = (fan & 0xff) | (speed << 8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
299
  	return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
  }
  
  /*
   * Read the cpu temperature.
   */
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
305
  static int i8k_get_temp(int sensor)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  {
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
307
  	struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
308
309
  	int rc;
  	int temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
  
  #ifdef I8K_TEMPERATURE_BUG
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
312
  	static int prev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  #endif
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
314
  	regs.ebx = sensor & 0xff;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
315
  	if ((rc = i8k_smm(&regs)) < 0)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
316
  		return rc;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
317

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
318
  	temp = regs.eax & 0xff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
  
  #ifdef I8K_TEMPERATURE_BUG
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
321
322
323
324
325
326
327
328
329
330
331
332
333
  	/*
  	 * 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) {
  		temp = prev;
  		prev = I8K_MAX_TEMP;
  	} else {
  		prev = temp;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
  #endif
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
335
  	return temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  }
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
337
  static int i8k_get_dell_signature(int req_fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  {
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
339
  	struct smm_regs regs = { .eax = req_fn, };
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
340
  	int rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
342
  	if ((rc = i8k_smm(&regs)) < 0)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
343
  		return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
345
  	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  }
d79b6f4de   Frederic Weisbecker   procfs: Push down...
347
348
  static int
  i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  {
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
350
  	int val = 0;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  	int speed;
  	unsigned char buff[16];
  	int __user *argp = (int __user *)arg;
  
  	if (!argp)
  		return -EINVAL;
  
  	switch (cmd) {
  	case I8K_BIOS_VERSION:
  		val = i8k_get_bios_version();
  		break;
  
  	case I8K_MACHINE_ID:
  		memset(buff, 0, 16);
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
365
  		strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff));
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
366
367
368
369
370
371
372
373
374
375
376
  		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:
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
377
  		val = i8k_get_temp(0);
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
378
379
380
  		break;
  
  	case I8K_GET_SPEED:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
381
  		if (copy_from_user(&val, argp, sizeof(int)))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
382
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
383

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
384
385
  		val = i8k_get_fan_speed(val);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
387
  	case I8K_GET_FAN:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
388
  		if (copy_from_user(&val, argp, sizeof(int)))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
389
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
390

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
391
392
  		val = i8k_get_fan_status(val);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
394
  	case I8K_SET_FAN:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
395
  		if (restricted && !capable(CAP_SYS_ADMIN))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
396
  			return -EPERM;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
397
398
  
  		if (copy_from_user(&val, argp, sizeof(int)))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
399
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
400
401
  
  		if (copy_from_user(&speed, argp + 1, sizeof(int)))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
402
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
403

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
404
405
  		val = i8k_set_fan(val, speed);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
407
408
  	default:
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
411
  	if (val < 0)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
412
  		return val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
414
415
  	switch (cmd) {
  	case I8K_BIOS_VERSION:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
416
  		if (copy_to_user(argp, &val, 4))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
417
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
418

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
419
420
  		break;
  	case I8K_MACHINE_ID:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
421
  		if (copy_to_user(argp, buff, 16))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
422
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
423

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
424
425
  		break;
  	default:
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
426
  		if (copy_to_user(argp, &val, sizeof(int)))
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
427
  			return -EFAULT;
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
428

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
429
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
432
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  }
d79b6f4de   Frederic Weisbecker   procfs: Push down...
434
435
436
  static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
  {
  	long ret;
613655fa3   Arnd Bergmann   drivers: autoconv...
437
  	mutex_lock(&i8k_mutex);
d79b6f4de   Frederic Weisbecker   procfs: Push down...
438
  	ret = i8k_ioctl_unlocked(fp, cmd, arg);
613655fa3   Arnd Bergmann   drivers: autoconv...
439
  	mutex_unlock(&i8k_mutex);
d79b6f4de   Frederic Weisbecker   procfs: Push down...
440
441
442
  
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
  /*
   * Print the information for /proc/i8k.
   */
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
446
  static int i8k_proc_show(struct seq_file *seq, void *offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  {
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
448
  	int fn_key, cpu_temp, ac_power;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
449
  	int left_fan, right_fan, left_speed, right_speed;
96de0e252   Jan Engelhardt   Convert files to ...
450
451
452
453
454
455
  	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 */
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
456
  	if (power_status)
96de0e252   Jan Engelhardt   Convert files to ...
457
  		ac_power = i8k_get_power_status();		/* 14700 µs */
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
458
  	else
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
459
  		ac_power = -1;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  
  	/*
  	 * 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
  	 */
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
475
476
477
478
  	return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d
  ",
  			  I8K_PROC_FMT,
  			  bios_version,
4f005551a   Dmitry Torokhov   [PATCH] I8K: fix ...
479
  			  i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
480
481
482
  			  cpu_temp,
  			  left_fan, right_fan, left_speed, right_speed,
  			  ac_power, fn_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  }
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
484
  static int i8k_open_fs(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  {
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
486
  	return single_open(file, i8k_proc_show, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  }
949a9d700   Jean Delvare   i8k: Integrate wi...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
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
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  
  /*
   * Hwmon interface
   */
  
  static ssize_t i8k_hwmon_show_temp(struct device *dev,
  				   struct device_attribute *devattr,
  				   char *buf)
  {
  	int cpu_temp;
  
  	cpu_temp = i8k_get_temp(0);
  	if (cpu_temp < 0)
  		return cpu_temp;
  	return sprintf(buf, "%d
  ", cpu_temp * 1000);
  }
  
  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
  ", fan_speed);
  }
  
  static ssize_t i8k_hwmon_show_label(struct device *dev,
  				    struct device_attribute *devattr,
  				    char *buf)
  {
  	static const char *labels[4] = {
  		"i8k",
  		"CPU",
  		"Left Fan",
  		"Right Fan",
  	};
  	int index = to_sensor_dev_attr(devattr)->index;
  
  	return sprintf(buf, "%s
  ", labels[index]);
  }
  
  static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
  static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
  			  I8K_FAN_LEFT);
  static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
  			  I8K_FAN_RIGHT);
  static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
  static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
  static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
  static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
  
  static void i8k_hwmon_remove_files(struct device *dev)
  {
  	device_remove_file(dev, &dev_attr_temp1_input);
  	device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
  	device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
  	device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
  	device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
  	device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
  	device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
  }
  
  static int __init i8k_init_hwmon(void)
  {
  	int err;
  
  	i8k_hwmon_dev = hwmon_device_register(NULL);
  	if (IS_ERR(i8k_hwmon_dev)) {
  		err = PTR_ERR(i8k_hwmon_dev);
  		i8k_hwmon_dev = NULL;
  		printk(KERN_ERR "i8k: hwmon registration failed (%d)
  ", err);
  		return err;
  	}
  
  	/* Required name attribute */
  	err = device_create_file(i8k_hwmon_dev,
  				 &sensor_dev_attr_name.dev_attr);
  	if (err)
  		goto exit_unregister;
  
  	/* CPU temperature attributes, if temperature reading is OK */
  	err = i8k_get_temp(0);
  	if (err < 0) {
  		dev_dbg(i8k_hwmon_dev,
  			"Not creating temperature attributes (%d)
  ", err);
  	} else {
  		err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
  		if (err)
  			goto exit_remove_files;
  		err = device_create_file(i8k_hwmon_dev,
  					 &sensor_dev_attr_temp1_label.dev_attr);
  		if (err)
  			goto exit_remove_files;
  	}
  
  	/* Left fan attributes, if left fan is present */
  	err = i8k_get_fan_status(I8K_FAN_LEFT);
  	if (err < 0) {
  		dev_dbg(i8k_hwmon_dev,
  			"Not creating %s fan attributes (%d)
  ", "left", err);
  	} else {
  		err = device_create_file(i8k_hwmon_dev,
  					 &sensor_dev_attr_fan1_input.dev_attr);
  		if (err)
  			goto exit_remove_files;
  		err = device_create_file(i8k_hwmon_dev,
  					 &sensor_dev_attr_fan1_label.dev_attr);
  		if (err)
  			goto exit_remove_files;
  	}
  
  	/* Right fan attributes, if right fan is present */
  	err = i8k_get_fan_status(I8K_FAN_RIGHT);
  	if (err < 0) {
  		dev_dbg(i8k_hwmon_dev,
  			"Not creating %s fan attributes (%d)
  ", "right", err);
  	} else {
  		err = device_create_file(i8k_hwmon_dev,
  					 &sensor_dev_attr_fan2_input.dev_attr);
  		if (err)
  			goto exit_remove_files;
  		err = device_create_file(i8k_hwmon_dev,
  					 &sensor_dev_attr_fan2_label.dev_attr);
  		if (err)
  			goto exit_remove_files;
  	}
  
  	return 0;
  
   exit_remove_files:
  	i8k_hwmon_remove_files(i8k_hwmon_dev);
   exit_unregister:
  	hwmon_device_unregister(i8k_hwmon_dev);
  	return err;
  }
  
  static void __exit i8k_exit_hwmon(void)
  {
  	i8k_hwmon_remove_files(i8k_hwmon_dev);
  	hwmon_device_unregister(i8k_hwmon_dev);
  }
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  static struct dmi_system_id __initdata i8k_dmi_table[] = {
  	{
  		.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"),
  		},
  	},
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
  	{
  		.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"),
  		},
  	},
a9000d037   Nick Warne   ik8: add Dell UK ...
669
670
671
672
673
674
675
  	{	/* UK Inspiron 6400  */
  		.ident = "Dell Inspiron 3",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  			DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
  		},
  	},
48103c527   Frank Sorenson   i8k: Inspiron E17...
676
677
678
679
680
681
682
  	{
  		.ident = "Dell Inspiron 3",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  			DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
  		},
  	},
7ab21a869   Andy Spencer   i8k: Enable i8k o...
683
684
685
686
687
688
689
  	{
  		.ident = "Dell Precision",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  			DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
  		},
  	},
bef2a508b   Federico Heinz   i8k: Add Dell Vos...
690
691
692
693
694
695
696
697
  	{
  		.ident = "Dell Vostro",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
  		},
  	},
          { }
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
698
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
700
701
702
703
704
  
  /*
   * Probe for the presence of a supported laptop.
   */
  static int __init i8k_probe(void)
  {
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
705
706
  	char buff[4];
  	int version;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
707

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
  	/*
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
709
  	 * Get DMI information
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  	 */
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
711
712
713
714
715
716
  	if (!dmi_check_system(i8k_dmi_table)) {
  		if (!ignore_dmi && !force)
  			return -ENODEV;
  
  		printk(KERN_INFO "i8k: not running on a supported Dell system.
  ");
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
717
718
  		printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s
  ",
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
719
720
721
  			i8k_get_dmi_data(DMI_SYS_VENDOR),
  			i8k_get_dmi_data(DMI_PRODUCT_NAME),
  			i8k_get_dmi_data(DMI_BIOS_VERSION));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  	}
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
723

e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
724
  	strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
  	/*
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
726
  	 * Get SMM Dell signature
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
  	 */
7e0fa31db   Dmitry Torokhov   [PATCH] I8K: add ...
728
729
  	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
  	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
730
731
732
733
  		printk(KERN_ERR "i8k: unable to get SMM Dell signature
  ");
  		if (!force)
  			return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
736
737
738
739
740
  	/*
  	 * Get SMM BIOS version.
  	 */
  	version = i8k_get_bios_version();
  	if (version <= 0) {
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
741
742
  		printk(KERN_WARNING "i8k: unable to get SMM BIOS version
  ");
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
743
  	} else {
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
744
745
746
747
748
749
750
  		buff[0] = (version >> 16) & 0xff;
  		buff[1] = (version >> 8) & 0xff;
  		buff[2] = (version) & 0xff;
  		buff[3] = '\0';
  		/*
  		 * If DMI BIOS version is unknown use SMM BIOS version.
  		 */
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
751
752
  		if (!dmi_get_system_info(DMI_BIOS_VERSION))
  			strlcpy(bios_version, buff, sizeof(bios_version));
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
753
754
755
  		/*
  		 * Check if the two versions match.
  		 */
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
756
757
758
759
  		if (strncmp(buff, bios_version, sizeof(bios_version)) != 0)
  			printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s
  ",
  				buff, bios_version);
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
760
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761

dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
762
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  }
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
764
  static int __init i8k_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
  {
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
766
  	struct proc_dir_entry *proc_i8k;
949a9d700   Jean Delvare   i8k: Integrate wi...
767
  	int err;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
768
769
  
  	/* Are we running on an supported laptop? */
e70c9d5e6   Dmitry Torokhov   [PATCH] I8K: use ...
770
  	if (i8k_probe())
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
771
  		return -ENODEV;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
772
773
  
  	/* Register the proc entry */
1b5022173   Denis V. Lunev   drivers: use non-...
774
  	proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops);
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
775
  	if (!proc_i8k)
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
776
  		return -ENOENT;
352f8f8bf   Dmitry Torokhov   [PATCH] I8K: conv...
777

949a9d700   Jean Delvare   i8k: Integrate wi...
778
779
780
  	err = i8k_init_hwmon();
  	if (err)
  		goto exit_remove_proc;
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
781
782
783
784
785
786
  	printk(KERN_INFO
  	       "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)
  ",
  	       I8K_VERSION);
  
  	return 0;
949a9d700   Jean Delvare   i8k: Integrate wi...
787
788
789
790
  
   exit_remove_proc:
  	remove_proc_entry("i8k", NULL);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  }
8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
792
  static void __exit i8k_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
  {
949a9d700   Jean Delvare   i8k: Integrate wi...
794
  	i8k_exit_hwmon();
dec63ec32   Dmitry Torokhov   [PATCH] I8K: pass...
795
  	remove_proc_entry("i8k", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797

8378b9240   Dmitry Torokhov   [PATCH] I8K: init...
798
799
  module_init(i8k_init);
  module_exit(i8k_exit);