blob: 9f04283beaeef4741d94f79b88f2459ccd9e4c03 [file] [log] [blame]
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001/*
2 * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature
3 * sensors, fan control, keyboard backlight control) used in Intel-based Apple
4 * computers.
5 *
6 * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
7 *
8 * Based on hdaps.c driver:
9 * Copyright (C) 2005 Robert Love <rml@novell.com>
10 * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
11 *
12 * Fan control based on smcFanControl:
13 * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
14 *
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License v2 as published by the
17 * Free Software Foundation.
18 *
19 * This program is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 * more details.
23 *
24 * You should have received a copy of the GNU General Public License along with
25 * this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29#include <linux/delay.h>
30#include <linux/platform_device.h>
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040031#include <linux/input-polldev.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070032#include <linux/kernel.h>
33#include <linux/module.h>
34#include <linux/timer.h>
35#include <linux/dmi.h>
36#include <linux/mutex.h>
37#include <linux/hwmon-sysfs.h>
38#include <asm/io.h>
39#include <linux/leds.h>
40#include <linux/hwmon.h>
41#include <linux/workqueue.h>
42
43/* data port used by Apple SMC */
44#define APPLESMC_DATA_PORT 0x300
45/* command/status port used by Apple SMC */
46#define APPLESMC_CMD_PORT 0x304
47
48#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
49
50#define APPLESMC_MAX_DATA_LENGTH 32
51
Henrik Rydberg8c9398d2008-10-18 20:27:43 -070052#define APPLESMC_MIN_WAIT 0x0040
53#define APPLESMC_MAX_WAIT 0x8000
54
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070055#define APPLESMC_STATUS_MASK 0x0f
56#define APPLESMC_READ_CMD 0x10
57#define APPLESMC_WRITE_CMD 0x11
58#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
59#define APPLESMC_GET_KEY_TYPE_CMD 0x13
60
61#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */
62
Henrik Rydberg8bd1a122008-10-18 20:27:39 -070063#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */
64#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040065#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070066
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040067#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070068
69#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */
70#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */
71#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */
72#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */
73
74#define FANS_COUNT "FNum" /* r-o ui8 */
75#define FANS_MANUAL "FS! " /* r-w ui16 */
76#define FAN_ACTUAL_SPEED "F0Ac" /* r-o fpe2 (2 bytes) */
77#define FAN_MIN_SPEED "F0Mn" /* r-o fpe2 (2 bytes) */
78#define FAN_MAX_SPEED "F0Mx" /* r-o fpe2 (2 bytes) */
79#define FAN_SAFE_SPEED "F0Sf" /* r-o fpe2 (2 bytes) */
80#define FAN_TARGET_SPEED "F0Tg" /* r-w fpe2 (2 bytes) */
81#define FAN_POSITION "F0ID" /* r-o char[16] */
82
83/*
84 * Temperature sensors keys (sp78 - 2 bytes).
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070085 */
René Rebe8de57702007-10-16 14:19:20 -070086static const char* temperature_sensors_sets[][36] = {
Martin Szulecki1bed24b2007-07-09 11:41:36 -070087/* Set 0: Macbook Pro */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070088 { "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H",
89 "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -080090/* Set 1: Macbook2 set */
91 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "TTF0", "Th0H",
92 "Th0S", "Th1H", NULL },
93/* Set 2: Macbook set */
Martin Szulecki1bed24b2007-07-09 11:41:36 -070094 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "Th0H", "Th0S",
95 "Th1H", "Ts0P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -080096/* Set 3: Macmini set */
René Rebe8de57702007-10-16 14:19:20 -070097 { "TC0D", "TC0P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -080098/* Set 4: Mac Pro (2 x Quad-Core) */
René Rebe8de57702007-10-16 14:19:20 -070099 { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P",
100 "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "THTG", "TH0P",
101 "TH1P", "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S",
102 "TM1P", "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P",
103 "TM9S", "TN0H", "TS0C", NULL },
Roberto De Ioris9f86f282008-08-15 00:40:30 -0700104/* Set 5: iMac */
105 { "TC0D", "TA0P", "TG0P", "TG0D", "TG0H", "TH0P", "Tm0P", "TO0P",
106 "Tp0C", NULL },
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -0700107/* Set 6: Macbook3 set */
108 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H",
109 "Th0S", "Th1H", NULL },
Henrik Rydbergf5274c92008-10-18 20:27:40 -0700110/* Set 7: Macbook Air */
111 { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP",
112 "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL },
Henrik Rydbergd7549902008-10-18 20:27:41 -0700113/* Set 8: Macbook Pro 4,1 (Penryn) */
114 { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H",
115 "Th1H", "Th2H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -0700116/* Set 9: Macbook Pro 3,1 (Santa Rosa) */
117 { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P",
118 "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -0800119/* Set 10: iMac 5,1 */
120 { "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL },
Henrik Rydberg181209a2008-11-06 12:53:20 -0800121/* Set 11: Macbook 5,1 */
122 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P",
123 "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL },
Henrik Rydberga6660322008-11-06 12:53:21 -0800124/* Set 12: Macbook Pro 5,1 */
125 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
126 "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0",
127 "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700128};
129
130/* List of keys used to read/write fan speeds */
131static const char* fan_speed_keys[] = {
132 FAN_ACTUAL_SPEED,
133 FAN_MIN_SPEED,
134 FAN_MAX_SPEED,
135 FAN_SAFE_SPEED,
136 FAN_TARGET_SPEED
137};
138
139#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
140#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
141
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400142#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700143#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
144#define APPLESMC_INPUT_FLAT 4
145
146#define SENSOR_X 0
147#define SENSOR_Y 1
148#define SENSOR_Z 2
149
150/* Structure to be passed to DMI_MATCH function */
151struct dmi_match_data {
152/* Indicates whether this computer has an accelerometer. */
153 int accelerometer;
154/* Indicates whether this computer has light sensors and keyboard backlight. */
155 int light;
156/* Indicates which temperature sensors set to use. */
157 int temperature_set;
158};
159
160static const int debug;
161static struct platform_device *pdev;
162static s16 rest_x;
163static s16 rest_y;
Tony Jones1beeffe2007-08-20 13:46:20 -0700164static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400165static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700166
167/* Indicates whether this computer has an accelerometer. */
168static unsigned int applesmc_accelerometer;
169
170/* Indicates whether this computer has light sensors and keyboard backlight. */
171static unsigned int applesmc_light;
172
173/* Indicates which temperature sensors set to use. */
174static unsigned int applesmc_temperature_set;
175
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400176static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700177
178/*
179 * Last index written to key_at_index sysfs file, and value to use for all other
180 * key_at_index_* sysfs files.
181 */
182static unsigned int key_at_index;
183
184static struct workqueue_struct *applesmc_led_wq;
185
186/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700187 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700188 * (masked with 0x0f), returning zero if the value is obtained. Callers must
189 * hold applesmc_lock.
190 */
191static int __wait_status(u8 val)
192{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700193 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700194
195 val = val & APPLESMC_STATUS_MASK;
196
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700197 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
198 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700199 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
200 if (debug)
201 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700202 "Waited %d us for status %x\n",
203 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700204 return 0;
205 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700206 }
207
208 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
209 val, inb(APPLESMC_CMD_PORT));
210
211 return -EIO;
212}
213
214/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700215 * special treatment of command port - on newer macbooks, it seems necessary
216 * to resend the command byte before polling the status again. Callers must
217 * hold applesmc_lock.
218 */
219static int send_command(u8 cmd)
220{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700221 int us;
222 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700223 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700224 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700225 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
226 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700227 }
228 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
229 cmd, inb(APPLESMC_CMD_PORT));
230 return -EIO;
231}
232
233/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700234 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
235 * Returns zero on success or a negative error on failure. Callers must
236 * hold applesmc_lock.
237 */
238static int applesmc_read_key(const char* key, u8* buffer, u8 len)
239{
240 int i;
241
242 if (len > APPLESMC_MAX_DATA_LENGTH) {
243 printk(KERN_ERR "applesmc_read_key: cannot read more than "
244 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
245 return -EINVAL;
246 }
247
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700248 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700249 return -EIO;
250
251 for (i = 0; i < 4; i++) {
252 outb(key[i], APPLESMC_DATA_PORT);
253 if (__wait_status(0x04))
254 return -EIO;
255 }
256 if (debug)
257 printk(KERN_DEBUG "<%s", key);
258
259 outb(len, APPLESMC_DATA_PORT);
260 if (debug)
261 printk(KERN_DEBUG ">%x", len);
262
263 for (i = 0; i < len; i++) {
264 if (__wait_status(0x05))
265 return -EIO;
266 buffer[i] = inb(APPLESMC_DATA_PORT);
267 if (debug)
268 printk(KERN_DEBUG "<%x", buffer[i]);
269 }
270 if (debug)
271 printk(KERN_DEBUG "\n");
272
273 return 0;
274}
275
276/*
277 * applesmc_write_key - writes len bytes from buffer to a given key.
278 * Returns zero on success or a negative error on failure. Callers must
279 * hold applesmc_lock.
280 */
281static int applesmc_write_key(const char* key, u8* buffer, u8 len)
282{
283 int i;
284
285 if (len > APPLESMC_MAX_DATA_LENGTH) {
286 printk(KERN_ERR "applesmc_write_key: cannot write more than "
287 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
288 return -EINVAL;
289 }
290
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700291 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700292 return -EIO;
293
294 for (i = 0; i < 4; i++) {
295 outb(key[i], APPLESMC_DATA_PORT);
296 if (__wait_status(0x04))
297 return -EIO;
298 }
299
300 outb(len, APPLESMC_DATA_PORT);
301
302 for (i = 0; i < len; i++) {
303 if (__wait_status(0x04))
304 return -EIO;
305 outb(buffer[i], APPLESMC_DATA_PORT);
306 }
307
308 return 0;
309}
310
311/*
312 * applesmc_get_key_at_index - get key at index, and put the result in key
313 * (char[6]). Returns zero on success or a negative error on failure. Callers
314 * must hold applesmc_lock.
315 */
316static int applesmc_get_key_at_index(int index, char* key)
317{
318 int i;
319 u8 readkey[4];
320 readkey[0] = index >> 24;
321 readkey[1] = index >> 16;
322 readkey[2] = index >> 8;
323 readkey[3] = index;
324
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700325 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700326 return -EIO;
327
328 for (i = 0; i < 4; i++) {
329 outb(readkey[i], APPLESMC_DATA_PORT);
330 if (__wait_status(0x04))
331 return -EIO;
332 }
333
334 outb(4, APPLESMC_DATA_PORT);
335
336 for (i = 0; i < 4; i++) {
337 if (__wait_status(0x05))
338 return -EIO;
339 key[i] = inb(APPLESMC_DATA_PORT);
340 }
341 key[4] = 0;
342
343 return 0;
344}
345
346/*
347 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
348 * Returns zero on success or a negative error on failure. Callers must
349 * hold applesmc_lock.
350 */
351static int applesmc_get_key_type(char* key, char* type)
352{
353 int i;
354
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700355 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700356 return -EIO;
357
358 for (i = 0; i < 4; i++) {
359 outb(key[i], APPLESMC_DATA_PORT);
360 if (__wait_status(0x04))
361 return -EIO;
362 }
363
Henrik Rydberg05224092008-10-18 20:27:35 -0700364 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700365
366 for (i = 0; i < 6; i++) {
367 if (__wait_status(0x05))
368 return -EIO;
369 type[i] = inb(APPLESMC_DATA_PORT);
370 }
371 type[5] = 0;
372
373 return 0;
374}
375
376/*
377 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
378 * hold applesmc_lock.
379 */
380static int applesmc_read_motion_sensor(int index, s16* value)
381{
382 u8 buffer[2];
383 int ret;
384
385 switch (index) {
386 case SENSOR_X:
387 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
388 break;
389 case SENSOR_Y:
390 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
391 break;
392 case SENSOR_Z:
393 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
394 break;
395 default:
396 ret = -EINVAL;
397 }
398
399 *value = ((s16)buffer[0] << 8) | buffer[1];
400
401 return ret;
402}
403
404/*
405 * applesmc_device_init - initialize the accelerometer. Returns zero on success
406 * and negative error code on failure. Can sleep.
407 */
408static int applesmc_device_init(void)
409{
410 int total, ret = -ENXIO;
411 u8 buffer[2];
412
413 if (!applesmc_accelerometer)
414 return 0;
415
416 mutex_lock(&applesmc_lock);
417
418 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
419 if (debug)
420 printk(KERN_DEBUG "applesmc try %d\n", total);
421 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
422 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
423 if (total == INIT_TIMEOUT_MSECS) {
424 printk(KERN_DEBUG "applesmc: device has"
425 " already been initialized"
426 " (0x%02x, 0x%02x).\n",
427 buffer[0], buffer[1]);
428 } else {
429 printk(KERN_DEBUG "applesmc: device"
430 " successfully initialized"
431 " (0x%02x, 0x%02x).\n",
432 buffer[0], buffer[1]);
433 }
434 ret = 0;
435 goto out;
436 }
437 buffer[0] = 0xe0;
438 buffer[1] = 0x00;
439 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
440 msleep(INIT_WAIT_MSECS);
441 }
442
443 printk(KERN_WARNING "applesmc: failed to init the device\n");
444
445out:
446 mutex_unlock(&applesmc_lock);
447 return ret;
448}
449
450/*
451 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
452 * applesmc_lock.
453 */
454static int applesmc_get_fan_count(void)
455{
456 int ret;
457 u8 buffer[1];
458
459 mutex_lock(&applesmc_lock);
460
461 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
462
463 mutex_unlock(&applesmc_lock);
464 if (ret)
465 return ret;
466 else
467 return buffer[0];
468}
469
470/* Device model stuff */
471static int applesmc_probe(struct platform_device *dev)
472{
473 int ret;
474
475 ret = applesmc_device_init();
476 if (ret)
477 return ret;
478
479 printk(KERN_INFO "applesmc: device successfully initialized.\n");
480 return 0;
481}
482
483static int applesmc_resume(struct platform_device *dev)
484{
485 return applesmc_device_init();
486}
487
488static struct platform_driver applesmc_driver = {
489 .probe = applesmc_probe,
490 .resume = applesmc_resume,
491 .driver = {
492 .name = "applesmc",
493 .owner = THIS_MODULE,
494 },
495};
496
497/*
498 * applesmc_calibrate - Set our "resting" values. Callers must
499 * hold applesmc_lock.
500 */
501static void applesmc_calibrate(void)
502{
503 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
504 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
505 rest_x = -rest_x;
506}
507
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400508static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700509{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400510 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700511 s16 x, y;
512
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400513 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700514
515 if (applesmc_read_motion_sensor(SENSOR_X, &x))
516 goto out;
517 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
518 goto out;
519
520 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400521 input_report_abs(idev, ABS_X, x - rest_x);
522 input_report_abs(idev, ABS_Y, y - rest_y);
523 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700524
525out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700526 mutex_unlock(&applesmc_lock);
527}
528
529/* Sysfs Files */
530
Nicolas Boichatfa744192007-05-23 13:58:13 -0700531static ssize_t applesmc_name_show(struct device *dev,
532 struct device_attribute *attr, char *buf)
533{
534 return snprintf(buf, PAGE_SIZE, "applesmc\n");
535}
536
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700537static ssize_t applesmc_position_show(struct device *dev,
538 struct device_attribute *attr, char *buf)
539{
540 int ret;
541 s16 x, y, z;
542
543 mutex_lock(&applesmc_lock);
544
545 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
546 if (ret)
547 goto out;
548 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
549 if (ret)
550 goto out;
551 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
552 if (ret)
553 goto out;
554
555out:
556 mutex_unlock(&applesmc_lock);
557 if (ret)
558 return ret;
559 else
560 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
561}
562
563static ssize_t applesmc_light_show(struct device *dev,
564 struct device_attribute *attr, char *sysfsbuf)
565{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700566 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700567 int ret;
568 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700569 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700570
571 mutex_lock(&applesmc_lock);
572
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700573 if (!data_length) {
574 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
575 if (ret)
576 goto out;
577 data_length = clamp_val(query[0], 0, 10);
578 printk(KERN_INFO "applesmc: light sensor data length set to "
579 "%d\n", data_length);
580 }
581
582 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700583 left = buffer[2];
584 if (ret)
585 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700586 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700587 right = buffer[2];
588
589out:
590 mutex_unlock(&applesmc_lock);
591 if (ret)
592 return ret;
593 else
594 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
595}
596
597/* Displays degree Celsius * 1000 */
598static ssize_t applesmc_show_temperature(struct device *dev,
599 struct device_attribute *devattr, char *sysfsbuf)
600{
601 int ret;
602 u8 buffer[2];
603 unsigned int temp;
604 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
605 const char* key =
606 temperature_sensors_sets[applesmc_temperature_set][attr->index];
607
608 mutex_lock(&applesmc_lock);
609
610 ret = applesmc_read_key(key, buffer, 2);
611 temp = buffer[0]*1000;
612 temp += (buffer[1] >> 6) * 250;
613
614 mutex_unlock(&applesmc_lock);
615
616 if (ret)
617 return ret;
618 else
619 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
620}
621
622static ssize_t applesmc_show_fan_speed(struct device *dev,
623 struct device_attribute *attr, char *sysfsbuf)
624{
625 int ret;
626 unsigned int speed = 0;
627 char newkey[5];
628 u8 buffer[2];
629 struct sensor_device_attribute_2 *sensor_attr =
630 to_sensor_dev_attr_2(attr);
631
632 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
633 newkey[1] = '0' + sensor_attr->index;
634 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
635 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
636 newkey[4] = 0;
637
638 mutex_lock(&applesmc_lock);
639
640 ret = applesmc_read_key(newkey, buffer, 2);
641 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
642
643 mutex_unlock(&applesmc_lock);
644 if (ret)
645 return ret;
646 else
647 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
648}
649
650static ssize_t applesmc_store_fan_speed(struct device *dev,
651 struct device_attribute *attr,
652 const char *sysfsbuf, size_t count)
653{
654 int ret;
655 u32 speed;
656 char newkey[5];
657 u8 buffer[2];
658 struct sensor_device_attribute_2 *sensor_attr =
659 to_sensor_dev_attr_2(attr);
660
661 speed = simple_strtoul(sysfsbuf, NULL, 10);
662
663 if (speed > 0x4000) /* Bigger than a 14-bit value */
664 return -EINVAL;
665
666 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
667 newkey[1] = '0' + sensor_attr->index;
668 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
669 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
670 newkey[4] = 0;
671
672 mutex_lock(&applesmc_lock);
673
674 buffer[0] = (speed >> 6) & 0xff;
675 buffer[1] = (speed << 2) & 0xff;
676 ret = applesmc_write_key(newkey, buffer, 2);
677
678 mutex_unlock(&applesmc_lock);
679 if (ret)
680 return ret;
681 else
682 return count;
683}
684
685static ssize_t applesmc_show_fan_manual(struct device *dev,
686 struct device_attribute *devattr, char *sysfsbuf)
687{
688 int ret;
689 u16 manual = 0;
690 u8 buffer[2];
691 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
692
693 mutex_lock(&applesmc_lock);
694
695 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
696 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
697
698 mutex_unlock(&applesmc_lock);
699 if (ret)
700 return ret;
701 else
702 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
703}
704
705static ssize_t applesmc_store_fan_manual(struct device *dev,
706 struct device_attribute *devattr,
707 const char *sysfsbuf, size_t count)
708{
709 int ret;
710 u8 buffer[2];
711 u32 input;
712 u16 val;
713 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
714
715 input = simple_strtoul(sysfsbuf, NULL, 10);
716
717 mutex_lock(&applesmc_lock);
718
719 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
720 val = (buffer[0] << 8 | buffer[1]);
721 if (ret)
722 goto out;
723
724 if (input)
725 val = val | (0x01 << attr->index);
726 else
727 val = val & ~(0x01 << attr->index);
728
729 buffer[0] = (val >> 8) & 0xFF;
730 buffer[1] = val & 0xFF;
731
732 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
733
734out:
735 mutex_unlock(&applesmc_lock);
736 if (ret)
737 return ret;
738 else
739 return count;
740}
741
742static ssize_t applesmc_show_fan_position(struct device *dev,
743 struct device_attribute *attr, char *sysfsbuf)
744{
745 int ret;
746 char newkey[5];
747 u8 buffer[17];
748 struct sensor_device_attribute_2 *sensor_attr =
749 to_sensor_dev_attr_2(attr);
750
751 newkey[0] = FAN_POSITION[0];
752 newkey[1] = '0' + sensor_attr->index;
753 newkey[2] = FAN_POSITION[2];
754 newkey[3] = FAN_POSITION[3];
755 newkey[4] = 0;
756
757 mutex_lock(&applesmc_lock);
758
759 ret = applesmc_read_key(newkey, buffer, 16);
760 buffer[16] = 0;
761
762 mutex_unlock(&applesmc_lock);
763 if (ret)
764 return ret;
765 else
766 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
767}
768
769static ssize_t applesmc_calibrate_show(struct device *dev,
770 struct device_attribute *attr, char *sysfsbuf)
771{
772 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
773}
774
775static ssize_t applesmc_calibrate_store(struct device *dev,
776 struct device_attribute *attr, const char *sysfsbuf, size_t count)
777{
778 mutex_lock(&applesmc_lock);
779 applesmc_calibrate();
780 mutex_unlock(&applesmc_lock);
781
782 return count;
783}
784
785/* Store the next backlight value to be written by the work */
786static unsigned int backlight_value;
787
788static void applesmc_backlight_set(struct work_struct *work)
789{
790 u8 buffer[2];
791
792 mutex_lock(&applesmc_lock);
793 buffer[0] = backlight_value;
794 buffer[1] = 0x00;
795 applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
796 mutex_unlock(&applesmc_lock);
797}
798static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
799
800static void applesmc_brightness_set(struct led_classdev *led_cdev,
801 enum led_brightness value)
802{
803 int ret;
804
805 backlight_value = value;
806 ret = queue_work(applesmc_led_wq, &backlight_work);
807
808 if (debug && (!ret))
809 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
810}
811
812static ssize_t applesmc_key_count_show(struct device *dev,
813 struct device_attribute *attr, char *sysfsbuf)
814{
815 int ret;
816 u8 buffer[4];
817 u32 count;
818
819 mutex_lock(&applesmc_lock);
820
821 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
822 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
823 ((u32)buffer[2]<<8) + buffer[3];
824
825 mutex_unlock(&applesmc_lock);
826 if (ret)
827 return ret;
828 else
829 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
830}
831
832static ssize_t applesmc_key_at_index_read_show(struct device *dev,
833 struct device_attribute *attr, char *sysfsbuf)
834{
835 char key[5];
836 char info[6];
837 int ret;
838
839 mutex_lock(&applesmc_lock);
840
841 ret = applesmc_get_key_at_index(key_at_index, key);
842
843 if (ret || !key[0]) {
844 mutex_unlock(&applesmc_lock);
845
846 return -EINVAL;
847 }
848
849 ret = applesmc_get_key_type(key, info);
850
851 if (ret) {
852 mutex_unlock(&applesmc_lock);
853
854 return ret;
855 }
856
857 /*
858 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
859 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
860 */
861 ret = applesmc_read_key(key, sysfsbuf, info[0]);
862
863 mutex_unlock(&applesmc_lock);
864
865 if (!ret) {
866 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400867 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700868 return ret;
869 }
870}
871
872static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
873 struct device_attribute *attr, char *sysfsbuf)
874{
875 char key[5];
876 char info[6];
877 int ret;
878
879 mutex_lock(&applesmc_lock);
880
881 ret = applesmc_get_key_at_index(key_at_index, key);
882
883 if (ret || !key[0]) {
884 mutex_unlock(&applesmc_lock);
885
886 return -EINVAL;
887 }
888
889 ret = applesmc_get_key_type(key, info);
890
891 mutex_unlock(&applesmc_lock);
892
893 if (!ret)
894 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
895 else
896 return ret;
897}
898
899static ssize_t applesmc_key_at_index_type_show(struct device *dev,
900 struct device_attribute *attr, char *sysfsbuf)
901{
902 char key[5];
903 char info[6];
904 int ret;
905
906 mutex_lock(&applesmc_lock);
907
908 ret = applesmc_get_key_at_index(key_at_index, key);
909
910 if (ret || !key[0]) {
911 mutex_unlock(&applesmc_lock);
912
913 return -EINVAL;
914 }
915
916 ret = applesmc_get_key_type(key, info);
917
918 mutex_unlock(&applesmc_lock);
919
920 if (!ret)
921 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
922 else
923 return ret;
924}
925
926static ssize_t applesmc_key_at_index_name_show(struct device *dev,
927 struct device_attribute *attr, char *sysfsbuf)
928{
929 char key[5];
930 int ret;
931
932 mutex_lock(&applesmc_lock);
933
934 ret = applesmc_get_key_at_index(key_at_index, key);
935
936 mutex_unlock(&applesmc_lock);
937
938 if (!ret && key[0])
939 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
940 else
941 return -EINVAL;
942}
943
944static ssize_t applesmc_key_at_index_show(struct device *dev,
945 struct device_attribute *attr, char *sysfsbuf)
946{
947 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
948}
949
950static ssize_t applesmc_key_at_index_store(struct device *dev,
951 struct device_attribute *attr, const char *sysfsbuf, size_t count)
952{
953 mutex_lock(&applesmc_lock);
954
955 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
956
957 mutex_unlock(&applesmc_lock);
958
959 return count;
960}
961
962static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +0100963 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700964 .default_trigger = "nand-disk",
965 .brightness_set = applesmc_brightness_set,
966};
967
Nicolas Boichatfa744192007-05-23 13:58:13 -0700968static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
969
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700970static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
971static DEVICE_ATTR(calibrate, 0644,
972 applesmc_calibrate_show, applesmc_calibrate_store);
973
974static struct attribute *accelerometer_attributes[] = {
975 &dev_attr_position.attr,
976 &dev_attr_calibrate.attr,
977 NULL
978};
979
980static const struct attribute_group accelerometer_attributes_group =
981 { .attrs = accelerometer_attributes };
982
983static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
984
985static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
986static DEVICE_ATTR(key_at_index, 0644,
987 applesmc_key_at_index_show, applesmc_key_at_index_store);
988static DEVICE_ATTR(key_at_index_name, 0444,
989 applesmc_key_at_index_name_show, NULL);
990static DEVICE_ATTR(key_at_index_type, 0444,
991 applesmc_key_at_index_type_show, NULL);
992static DEVICE_ATTR(key_at_index_data_length, 0444,
993 applesmc_key_at_index_data_length_show, NULL);
994static DEVICE_ATTR(key_at_index_data, 0444,
995 applesmc_key_at_index_read_show, NULL);
996
997static struct attribute *key_enumeration_attributes[] = {
998 &dev_attr_key_count.attr,
999 &dev_attr_key_at_index.attr,
1000 &dev_attr_key_at_index_name.attr,
1001 &dev_attr_key_at_index_type.attr,
1002 &dev_attr_key_at_index_data_length.attr,
1003 &dev_attr_key_at_index_data.attr,
1004 NULL
1005};
1006
1007static const struct attribute_group key_enumeration_group =
1008 { .attrs = key_enumeration_attributes };
1009
1010/*
1011 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1012 * - show actual speed
1013 * - show/store minimum speed
1014 * - show maximum speed
1015 * - show safe speed
1016 * - show/store target speed
1017 * - show/store manual mode
1018 */
1019#define sysfs_fan_speeds_offset(offset) \
1020static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1021 applesmc_show_fan_speed, NULL, 0, offset-1); \
1022\
1023static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1024 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1025\
1026static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1027 applesmc_show_fan_speed, NULL, 2, offset-1); \
1028\
1029static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1030 applesmc_show_fan_speed, NULL, 3, offset-1); \
1031\
1032static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1033 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1034\
1035static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1036 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1037\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001038static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001039 applesmc_show_fan_position, NULL, offset-1); \
1040\
1041static struct attribute *fan##offset##_attributes[] = { \
1042 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1043 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1044 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1045 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1046 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1047 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001048 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001049 NULL \
1050};
1051
1052/*
1053 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001054 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001055 */
1056sysfs_fan_speeds_offset(1);
1057sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001058sysfs_fan_speeds_offset(3);
1059sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001060
1061static const struct attribute_group fan_attribute_groups[] = {
1062 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001063 { .attrs = fan2_attributes },
1064 { .attrs = fan3_attributes },
1065 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001066};
1067
1068/*
1069 * Temperature sensors sysfs entries.
1070 */
1071static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1072 applesmc_show_temperature, NULL, 0);
1073static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1074 applesmc_show_temperature, NULL, 1);
1075static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1076 applesmc_show_temperature, NULL, 2);
1077static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1078 applesmc_show_temperature, NULL, 3);
1079static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1080 applesmc_show_temperature, NULL, 4);
1081static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1082 applesmc_show_temperature, NULL, 5);
1083static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1084 applesmc_show_temperature, NULL, 6);
1085static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1086 applesmc_show_temperature, NULL, 7);
1087static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1088 applesmc_show_temperature, NULL, 8);
1089static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1090 applesmc_show_temperature, NULL, 9);
1091static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1092 applesmc_show_temperature, NULL, 10);
1093static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1094 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001095static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1096 applesmc_show_temperature, NULL, 12);
1097static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1098 applesmc_show_temperature, NULL, 13);
1099static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1100 applesmc_show_temperature, NULL, 14);
1101static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1102 applesmc_show_temperature, NULL, 15);
1103static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1104 applesmc_show_temperature, NULL, 16);
1105static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1106 applesmc_show_temperature, NULL, 17);
1107static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1108 applesmc_show_temperature, NULL, 18);
1109static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1110 applesmc_show_temperature, NULL, 19);
1111static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1112 applesmc_show_temperature, NULL, 20);
1113static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1114 applesmc_show_temperature, NULL, 21);
1115static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1116 applesmc_show_temperature, NULL, 22);
1117static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1118 applesmc_show_temperature, NULL, 23);
1119static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1120 applesmc_show_temperature, NULL, 24);
1121static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1122 applesmc_show_temperature, NULL, 25);
1123static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1124 applesmc_show_temperature, NULL, 26);
1125static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1126 applesmc_show_temperature, NULL, 27);
1127static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1128 applesmc_show_temperature, NULL, 28);
1129static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1130 applesmc_show_temperature, NULL, 29);
1131static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1132 applesmc_show_temperature, NULL, 30);
1133static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1134 applesmc_show_temperature, NULL, 31);
1135static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1136 applesmc_show_temperature, NULL, 32);
1137static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1138 applesmc_show_temperature, NULL, 33);
1139static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1140 applesmc_show_temperature, NULL, 34);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001141
1142static struct attribute *temperature_attributes[] = {
1143 &sensor_dev_attr_temp1_input.dev_attr.attr,
1144 &sensor_dev_attr_temp2_input.dev_attr.attr,
1145 &sensor_dev_attr_temp3_input.dev_attr.attr,
1146 &sensor_dev_attr_temp4_input.dev_attr.attr,
1147 &sensor_dev_attr_temp5_input.dev_attr.attr,
1148 &sensor_dev_attr_temp6_input.dev_attr.attr,
1149 &sensor_dev_attr_temp7_input.dev_attr.attr,
1150 &sensor_dev_attr_temp8_input.dev_attr.attr,
1151 &sensor_dev_attr_temp9_input.dev_attr.attr,
1152 &sensor_dev_attr_temp10_input.dev_attr.attr,
1153 &sensor_dev_attr_temp11_input.dev_attr.attr,
1154 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001155 &sensor_dev_attr_temp13_input.dev_attr.attr,
1156 &sensor_dev_attr_temp14_input.dev_attr.attr,
1157 &sensor_dev_attr_temp15_input.dev_attr.attr,
1158 &sensor_dev_attr_temp16_input.dev_attr.attr,
1159 &sensor_dev_attr_temp17_input.dev_attr.attr,
1160 &sensor_dev_attr_temp18_input.dev_attr.attr,
1161 &sensor_dev_attr_temp19_input.dev_attr.attr,
1162 &sensor_dev_attr_temp20_input.dev_attr.attr,
1163 &sensor_dev_attr_temp21_input.dev_attr.attr,
1164 &sensor_dev_attr_temp22_input.dev_attr.attr,
1165 &sensor_dev_attr_temp23_input.dev_attr.attr,
1166 &sensor_dev_attr_temp24_input.dev_attr.attr,
1167 &sensor_dev_attr_temp25_input.dev_attr.attr,
1168 &sensor_dev_attr_temp26_input.dev_attr.attr,
1169 &sensor_dev_attr_temp27_input.dev_attr.attr,
1170 &sensor_dev_attr_temp28_input.dev_attr.attr,
1171 &sensor_dev_attr_temp29_input.dev_attr.attr,
1172 &sensor_dev_attr_temp30_input.dev_attr.attr,
1173 &sensor_dev_attr_temp31_input.dev_attr.attr,
1174 &sensor_dev_attr_temp32_input.dev_attr.attr,
1175 &sensor_dev_attr_temp33_input.dev_attr.attr,
1176 &sensor_dev_attr_temp34_input.dev_attr.attr,
1177 &sensor_dev_attr_temp35_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001178 NULL
1179};
1180
1181static const struct attribute_group temperature_attributes_group =
1182 { .attrs = temperature_attributes };
1183
1184/* Module stuff */
1185
1186/*
1187 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1188 */
Jeff Garzik18552562007-10-03 15:15:40 -04001189static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001190{
1191 int i = 0;
1192 struct dmi_match_data* dmi_data = id->driver_data;
1193 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1194 applesmc_accelerometer = dmi_data->accelerometer;
1195 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1196 applesmc_accelerometer ? "with" : "without");
1197 applesmc_light = dmi_data->light;
1198 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1199 applesmc_light ? "with" : "without");
1200
1201 applesmc_temperature_set = dmi_data->temperature_set;
1202 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1203 i++;
1204 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1205 return 1;
1206}
1207
1208/* Create accelerometer ressources */
1209static int applesmc_create_accelerometer(void)
1210{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001211 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001212 int ret;
1213
1214 ret = sysfs_create_group(&pdev->dev.kobj,
1215 &accelerometer_attributes_group);
1216 if (ret)
1217 goto out;
1218
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001219 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001220 if (!applesmc_idev) {
1221 ret = -ENOMEM;
1222 goto out_sysfs;
1223 }
1224
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001225 applesmc_idev->poll = applesmc_idev_poll;
1226 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1227
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001228 /* initial calibrate for the input device */
1229 applesmc_calibrate();
1230
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001231 /* initialize the input device */
1232 idev = applesmc_idev->input;
1233 idev->name = "applesmc";
1234 idev->id.bustype = BUS_HOST;
1235 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001236 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001237 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001238 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001239 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001240 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1241
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001242 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001243 if (ret)
1244 goto out_idev;
1245
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001246 return 0;
1247
1248out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001249 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001250
1251out_sysfs:
1252 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1253
1254out:
1255 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1256 return ret;
1257}
1258
1259/* Release all ressources used by the accelerometer */
1260static void applesmc_release_accelerometer(void)
1261{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001262 input_unregister_polled_device(applesmc_idev);
1263 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001264 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1265}
1266
1267static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1268/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1269 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001270/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001271 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001272/* MacBook: accelerometer and temperature set 2 */
1273 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1274/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001275 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001276/* MacPro: temperature set 4 */
1277 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001278/* iMac: temperature set 5 */
1279 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001280/* MacBook3: accelerometer and temperature set 6 */
1281 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001282/* MacBook Air: accelerometer, backlight and temperature set 7 */
1283 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001284/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1285 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001286/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1287 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001288/* iMac 5: light sensor only, temperature set 10 */
1289 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a2008-11-06 12:53:20 -08001290/* MacBook 5: accelerometer, backlight and temperature set 11 */
1291 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001292/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1293 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001294};
1295
1296/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1297 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1298static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001299 { applesmc_dmi_match, "Apple MacBook Air", {
1300 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1301 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001302 &applesmc_dmi_data[7]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001303 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1304 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1305 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1306 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001307 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1308 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1309 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1310 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001311 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1312 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1313 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1314 &applesmc_dmi_data[9]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001315 { applesmc_dmi_match, "Apple MacBook Pro", {
1316 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1317 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001318 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001319 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001320 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001321 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001322 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001323 { applesmc_dmi_match, "Apple MacBook (v3)", {
1324 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1325 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001326 &applesmc_dmi_data[6]},
Henrik Rydberg181209a2008-11-06 12:53:20 -08001327 { applesmc_dmi_match, "Apple MacBook 5", {
1328 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1329 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1330 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001331 { applesmc_dmi_match, "Apple MacBook", {
1332 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1333 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001334 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001335 { applesmc_dmi_match, "Apple Macmini", {
1336 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1337 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001338 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001339 { applesmc_dmi_match, "Apple MacPro2", {
1340 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1341 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001342 &applesmc_dmi_data[4]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001343 { applesmc_dmi_match, "Apple iMac 5", {
1344 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1345 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1346 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001347 { applesmc_dmi_match, "Apple iMac", {
1348 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1349 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001350 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001351 { .ident = NULL }
1352};
1353
1354static int __init applesmc_init(void)
1355{
1356 int ret;
1357 int count;
1358 int i;
1359
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001360 if (!dmi_check_system(applesmc_whitelist)) {
1361 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1362 ret = -ENODEV;
1363 goto out;
1364 }
1365
1366 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1367 "applesmc")) {
1368 ret = -ENXIO;
1369 goto out;
1370 }
1371
1372 ret = platform_driver_register(&applesmc_driver);
1373 if (ret)
1374 goto out_region;
1375
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001376 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1377 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001378 if (IS_ERR(pdev)) {
1379 ret = PTR_ERR(pdev);
1380 goto out_driver;
1381 }
1382
Nicolas Boichatfa744192007-05-23 13:58:13 -07001383 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001384 if (ret)
1385 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001386
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001387 /* Create key enumeration sysfs files */
1388 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1389 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001390 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001391
1392 /* create fan files */
1393 count = applesmc_get_fan_count();
1394 if (count < 0) {
1395 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
1396 } else {
1397 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1398
1399 switch (count) {
1400 default:
René Rebe8de57702007-10-16 14:19:20 -07001401 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1402 " but at most 4 fans are supported"
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001403 " by the driver.\n");
René Rebe8de57702007-10-16 14:19:20 -07001404 case 4:
1405 ret = sysfs_create_group(&pdev->dev.kobj,
1406 &fan_attribute_groups[3]);
1407 if (ret)
1408 goto out_key_enumeration;
1409 case 3:
1410 ret = sysfs_create_group(&pdev->dev.kobj,
1411 &fan_attribute_groups[2]);
1412 if (ret)
1413 goto out_key_enumeration;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001414 case 2:
1415 ret = sysfs_create_group(&pdev->dev.kobj,
1416 &fan_attribute_groups[1]);
1417 if (ret)
1418 goto out_key_enumeration;
1419 case 1:
1420 ret = sysfs_create_group(&pdev->dev.kobj,
1421 &fan_attribute_groups[0]);
1422 if (ret)
1423 goto out_fan_1;
1424 case 0:
1425 ;
1426 }
1427 }
1428
1429 for (i = 0;
1430 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1431 i++) {
1432 if (temperature_attributes[i] == NULL) {
1433 printk(KERN_ERR "applesmc: More temperature sensors "
1434 "in temperature_sensors_sets (at least %i)"
1435 "than available sysfs files in "
1436 "temperature_attributes (%i), please report "
1437 "this bug.\n", i, i-1);
1438 goto out_temperature;
1439 }
1440 ret = sysfs_create_file(&pdev->dev.kobj,
1441 temperature_attributes[i]);
1442 if (ret)
1443 goto out_temperature;
1444 }
1445
1446 if (applesmc_accelerometer) {
1447 ret = applesmc_create_accelerometer();
1448 if (ret)
1449 goto out_temperature;
1450 }
1451
1452 if (applesmc_light) {
1453 /* Add light sensor file */
1454 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1455 if (ret)
1456 goto out_accelerometer;
1457
1458 /* Create the workqueue */
1459 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1460 if (!applesmc_led_wq) {
1461 ret = -ENOMEM;
1462 goto out_light_sysfs;
1463 }
1464
1465 /* register as a led device */
1466 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1467 if (ret < 0)
1468 goto out_light_wq;
1469 }
1470
Tony Jones1beeffe2007-08-20 13:46:20 -07001471 hwmon_dev = hwmon_device_register(&pdev->dev);
1472 if (IS_ERR(hwmon_dev)) {
1473 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001474 goto out_light_ledclass;
1475 }
1476
1477 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1478
1479 return 0;
1480
1481out_light_ledclass:
1482 if (applesmc_light)
1483 led_classdev_unregister(&applesmc_backlight);
1484out_light_wq:
1485 if (applesmc_light)
1486 destroy_workqueue(applesmc_led_wq);
1487out_light_sysfs:
1488 if (applesmc_light)
1489 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1490out_accelerometer:
1491 if (applesmc_accelerometer)
1492 applesmc_release_accelerometer();
1493out_temperature:
1494 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
1495 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
1496out_fan_1:
1497 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
1498out_key_enumeration:
1499 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001500out_name:
1501 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001502out_device:
1503 platform_device_unregister(pdev);
1504out_driver:
1505 platform_driver_unregister(&applesmc_driver);
1506out_region:
1507 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1508out:
1509 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1510 return ret;
1511}
1512
1513static void __exit applesmc_exit(void)
1514{
Tony Jones1beeffe2007-08-20 13:46:20 -07001515 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001516 if (applesmc_light) {
1517 led_classdev_unregister(&applesmc_backlight);
1518 destroy_workqueue(applesmc_led_wq);
1519 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1520 }
1521 if (applesmc_accelerometer)
1522 applesmc_release_accelerometer();
1523 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
1524 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
1525 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
1526 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001527 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001528 platform_device_unregister(pdev);
1529 platform_driver_unregister(&applesmc_driver);
1530 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1531
1532 printk(KERN_INFO "applesmc: driver unloaded.\n");
1533}
1534
1535module_init(applesmc_init);
1536module_exit(applesmc_exit);
1537
1538MODULE_AUTHOR("Nicolas Boichat");
1539MODULE_DESCRIPTION("Apple SMC");
1540MODULE_LICENSE("GPL v2");