blob: b6598aa557a0dcfe56fb87902319dff2394b6934 [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>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020038#include <linux/io.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070039#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 */
Bharath Rameshfb9f88e12009-01-29 14:25:24 -080086static const char *temperature_sensors_sets[][41] = {
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 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -0800128/* Set 13: iMac 8,1 */
129 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
130 "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -0800131/* Set 14: iMac 6,1 */
132 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
133 "TO0P", "Tp0P", NULL },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -0800134/* Set 15: MacBook Air 2,1 */
135 { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0",
136 "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P",
137 "Ts0S", NULL },
Bharath Rameshfb9f88e12009-01-29 14:25:24 -0800138/* Set 16: Mac Pro 3,1 (2 x Quad-Core) */
139 { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P",
140 "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "TH0P", "TH1P",
141 "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", "TM1P",
142 "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", "TM9S",
143 "TN0C", "TN0D", "TN0H", "TS0C", "Tp0C", "Tp1C", "Tv0S", "Tv1S",
144 NULL },
Justin P. Mattocke1741712010-04-14 16:14:10 +0200145/* Set 17: iMac 9,1 */
146 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TH0P", "TL0P",
147 "TN0D", "TN0H", "TN0P", "TO0P", "Tm0P", "Tp0P", NULL },
148/* Set 18: MacBook Pro 2,2 */
149 { "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "TM0P", "TTF0",
150 "Th0H", "Th1H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +0200151/* Set 19: Macbook Pro 5,3 */
152 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
153 "TG0F", "TG0H", "TG0P", "TG0T", "TN0D", "TN0P", "TTF0", "Th2H",
154 "Tm0P", "Ts0P", "Ts0S", NULL },
155/* Set 20: MacBook Pro 5,4 */
156 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TN0D",
157 "TN0P", "TTF0", "Th2H", "Ts0P", "Ts0S", NULL },
Bernhard Froemel872bad52010-05-27 19:58:52 +0200158/* Set 21: MacBook Pro 6,2 */
159 { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", "TC0P", "TC1C", "TG0D",
160 "TG0P", "TG0T", "TMCD", "TP0P", "TPCD", "Th1H", "Th2H", "Tm0P",
161 "Ts0P", "Ts0S", NULL },
Henrik Rydberg405eaa12010-05-27 19:58:53 +0200162/* Set 22: MacBook Pro 7,1 */
163 { "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", "TN0D", "TN0P", "TN0S",
164 "TN1D", "TN1F", "TN1G", "TN1S", "Th1H", "Ts0P", "Ts0S", NULL },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700165};
166
167/* List of keys used to read/write fan speeds */
168static const char* fan_speed_keys[] = {
169 FAN_ACTUAL_SPEED,
170 FAN_MIN_SPEED,
171 FAN_MAX_SPEED,
172 FAN_SAFE_SPEED,
173 FAN_TARGET_SPEED
174};
175
176#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
177#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
178
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400179#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700180#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
181#define APPLESMC_INPUT_FLAT 4
182
183#define SENSOR_X 0
184#define SENSOR_Y 1
185#define SENSOR_Z 2
186
187/* Structure to be passed to DMI_MATCH function */
188struct dmi_match_data {
189/* Indicates whether this computer has an accelerometer. */
190 int accelerometer;
191/* Indicates whether this computer has light sensors and keyboard backlight. */
192 int light;
193/* Indicates which temperature sensors set to use. */
194 int temperature_set;
195};
196
197static const int debug;
198static struct platform_device *pdev;
199static s16 rest_x;
200static s16 rest_y;
Henrik Rydberga976f152009-09-21 17:04:50 -0700201static u8 backlight_state[2];
202
Tony Jones1beeffe2007-08-20 13:46:20 -0700203static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400204static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700205
206/* Indicates whether this computer has an accelerometer. */
207static unsigned int applesmc_accelerometer;
208
209/* Indicates whether this computer has light sensors and keyboard backlight. */
210static unsigned int applesmc_light;
211
Henrik Rydberg0559a532010-05-11 09:17:47 +0200212/* The number of fans handled by the driver */
213static unsigned int fans_handled;
214
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700215/* Indicates which temperature sensors set to use. */
216static unsigned int applesmc_temperature_set;
217
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400218static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700219
220/*
221 * Last index written to key_at_index sysfs file, and value to use for all other
222 * key_at_index_* sysfs files.
223 */
224static unsigned int key_at_index;
225
226static struct workqueue_struct *applesmc_led_wq;
227
228/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700229 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700230 * (masked with 0x0f), returning zero if the value is obtained. Callers must
231 * hold applesmc_lock.
232 */
233static int __wait_status(u8 val)
234{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700235 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700236
237 val = val & APPLESMC_STATUS_MASK;
238
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700239 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
240 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700241 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
242 if (debug)
243 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700244 "Waited %d us for status %x\n",
245 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700246 return 0;
247 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700248 }
249
250 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
251 val, inb(APPLESMC_CMD_PORT));
252
253 return -EIO;
254}
255
256/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700257 * special treatment of command port - on newer macbooks, it seems necessary
258 * to resend the command byte before polling the status again. Callers must
259 * hold applesmc_lock.
260 */
261static int send_command(u8 cmd)
262{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700263 int us;
264 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700265 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700266 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700267 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
268 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700269 }
270 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
271 cmd, inb(APPLESMC_CMD_PORT));
272 return -EIO;
273}
274
275/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700276 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
277 * Returns zero on success or a negative error on failure. Callers must
278 * hold applesmc_lock.
279 */
280static int applesmc_read_key(const char* key, u8* buffer, u8 len)
281{
282 int i;
283
284 if (len > APPLESMC_MAX_DATA_LENGTH) {
285 printk(KERN_ERR "applesmc_read_key: cannot read more than "
286 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
287 return -EINVAL;
288 }
289
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700290 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700291 return -EIO;
292
293 for (i = 0; i < 4; i++) {
294 outb(key[i], APPLESMC_DATA_PORT);
295 if (__wait_status(0x04))
296 return -EIO;
297 }
298 if (debug)
299 printk(KERN_DEBUG "<%s", key);
300
301 outb(len, APPLESMC_DATA_PORT);
302 if (debug)
303 printk(KERN_DEBUG ">%x", len);
304
305 for (i = 0; i < len; i++) {
306 if (__wait_status(0x05))
307 return -EIO;
308 buffer[i] = inb(APPLESMC_DATA_PORT);
309 if (debug)
310 printk(KERN_DEBUG "<%x", buffer[i]);
311 }
312 if (debug)
313 printk(KERN_DEBUG "\n");
314
315 return 0;
316}
317
318/*
319 * applesmc_write_key - writes len bytes from buffer to a given key.
320 * Returns zero on success or a negative error on failure. Callers must
321 * hold applesmc_lock.
322 */
323static int applesmc_write_key(const char* key, u8* buffer, u8 len)
324{
325 int i;
326
327 if (len > APPLESMC_MAX_DATA_LENGTH) {
328 printk(KERN_ERR "applesmc_write_key: cannot write more than "
329 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
330 return -EINVAL;
331 }
332
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700333 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700334 return -EIO;
335
336 for (i = 0; i < 4; i++) {
337 outb(key[i], APPLESMC_DATA_PORT);
338 if (__wait_status(0x04))
339 return -EIO;
340 }
341
342 outb(len, APPLESMC_DATA_PORT);
343
344 for (i = 0; i < len; i++) {
345 if (__wait_status(0x04))
346 return -EIO;
347 outb(buffer[i], APPLESMC_DATA_PORT);
348 }
349
350 return 0;
351}
352
353/*
354 * applesmc_get_key_at_index - get key at index, and put the result in key
355 * (char[6]). Returns zero on success or a negative error on failure. Callers
356 * must hold applesmc_lock.
357 */
358static int applesmc_get_key_at_index(int index, char* key)
359{
360 int i;
361 u8 readkey[4];
362 readkey[0] = index >> 24;
363 readkey[1] = index >> 16;
364 readkey[2] = index >> 8;
365 readkey[3] = index;
366
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700367 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700368 return -EIO;
369
370 for (i = 0; i < 4; i++) {
371 outb(readkey[i], APPLESMC_DATA_PORT);
372 if (__wait_status(0x04))
373 return -EIO;
374 }
375
376 outb(4, APPLESMC_DATA_PORT);
377
378 for (i = 0; i < 4; i++) {
379 if (__wait_status(0x05))
380 return -EIO;
381 key[i] = inb(APPLESMC_DATA_PORT);
382 }
383 key[4] = 0;
384
385 return 0;
386}
387
388/*
389 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
390 * Returns zero on success or a negative error on failure. Callers must
391 * hold applesmc_lock.
392 */
393static int applesmc_get_key_type(char* key, char* type)
394{
395 int i;
396
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700397 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700398 return -EIO;
399
400 for (i = 0; i < 4; i++) {
401 outb(key[i], APPLESMC_DATA_PORT);
402 if (__wait_status(0x04))
403 return -EIO;
404 }
405
Henrik Rydberg05224092008-10-18 20:27:35 -0700406 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700407
408 for (i = 0; i < 6; i++) {
409 if (__wait_status(0x05))
410 return -EIO;
411 type[i] = inb(APPLESMC_DATA_PORT);
412 }
413 type[5] = 0;
414
415 return 0;
416}
417
418/*
419 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
420 * hold applesmc_lock.
421 */
422static int applesmc_read_motion_sensor(int index, s16* value)
423{
424 u8 buffer[2];
425 int ret;
426
427 switch (index) {
428 case SENSOR_X:
429 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
430 break;
431 case SENSOR_Y:
432 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
433 break;
434 case SENSOR_Z:
435 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
436 break;
437 default:
438 ret = -EINVAL;
439 }
440
441 *value = ((s16)buffer[0] << 8) | buffer[1];
442
443 return ret;
444}
445
446/*
447 * applesmc_device_init - initialize the accelerometer. Returns zero on success
448 * and negative error code on failure. Can sleep.
449 */
450static int applesmc_device_init(void)
451{
452 int total, ret = -ENXIO;
453 u8 buffer[2];
454
455 if (!applesmc_accelerometer)
456 return 0;
457
458 mutex_lock(&applesmc_lock);
459
460 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
461 if (debug)
462 printk(KERN_DEBUG "applesmc try %d\n", total);
463 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
464 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
465 if (total == INIT_TIMEOUT_MSECS) {
466 printk(KERN_DEBUG "applesmc: device has"
467 " already been initialized"
468 " (0x%02x, 0x%02x).\n",
469 buffer[0], buffer[1]);
470 } else {
471 printk(KERN_DEBUG "applesmc: device"
472 " successfully initialized"
473 " (0x%02x, 0x%02x).\n",
474 buffer[0], buffer[1]);
475 }
476 ret = 0;
477 goto out;
478 }
479 buffer[0] = 0xe0;
480 buffer[1] = 0x00;
481 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
482 msleep(INIT_WAIT_MSECS);
483 }
484
485 printk(KERN_WARNING "applesmc: failed to init the device\n");
486
487out:
488 mutex_unlock(&applesmc_lock);
489 return ret;
490}
491
492/*
493 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
494 * applesmc_lock.
495 */
496static int applesmc_get_fan_count(void)
497{
498 int ret;
499 u8 buffer[1];
500
501 mutex_lock(&applesmc_lock);
502
503 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
504
505 mutex_unlock(&applesmc_lock);
506 if (ret)
507 return ret;
508 else
509 return buffer[0];
510}
511
512/* Device model stuff */
513static int applesmc_probe(struct platform_device *dev)
514{
515 int ret;
516
517 ret = applesmc_device_init();
518 if (ret)
519 return ret;
520
521 printk(KERN_INFO "applesmc: device successfully initialized.\n");
522 return 0;
523}
524
Henrik Rydberga976f152009-09-21 17:04:50 -0700525/* Synchronize device with memorized backlight state */
526static int applesmc_pm_resume(struct device *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700527{
Henrik Rydberga976f152009-09-21 17:04:50 -0700528 mutex_lock(&applesmc_lock);
529 if (applesmc_light)
530 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
531 mutex_unlock(&applesmc_lock);
532 return 0;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700533}
534
Henrik Rydberga976f152009-09-21 17:04:50 -0700535/* Reinitialize device on resume from hibernation */
536static int applesmc_pm_restore(struct device *dev)
537{
538 int ret = applesmc_device_init();
539 if (ret)
540 return ret;
541 return applesmc_pm_resume(dev);
542}
543
Alexey Dobriyan47145212009-12-14 18:00:08 -0800544static const struct dev_pm_ops applesmc_pm_ops = {
Henrik Rydberga976f152009-09-21 17:04:50 -0700545 .resume = applesmc_pm_resume,
546 .restore = applesmc_pm_restore,
547};
548
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700549static struct platform_driver applesmc_driver = {
550 .probe = applesmc_probe,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700551 .driver = {
552 .name = "applesmc",
553 .owner = THIS_MODULE,
Henrik Rydberga976f152009-09-21 17:04:50 -0700554 .pm = &applesmc_pm_ops,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700555 },
556};
557
558/*
559 * applesmc_calibrate - Set our "resting" values. Callers must
560 * hold applesmc_lock.
561 */
562static void applesmc_calibrate(void)
563{
564 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
565 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
566 rest_x = -rest_x;
567}
568
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400569static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700570{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400571 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700572 s16 x, y;
573
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400574 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700575
576 if (applesmc_read_motion_sensor(SENSOR_X, &x))
577 goto out;
578 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
579 goto out;
580
581 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400582 input_report_abs(idev, ABS_X, x - rest_x);
583 input_report_abs(idev, ABS_Y, y - rest_y);
584 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700585
586out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700587 mutex_unlock(&applesmc_lock);
588}
589
590/* Sysfs Files */
591
Nicolas Boichatfa744192007-05-23 13:58:13 -0700592static ssize_t applesmc_name_show(struct device *dev,
593 struct device_attribute *attr, char *buf)
594{
595 return snprintf(buf, PAGE_SIZE, "applesmc\n");
596}
597
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700598static ssize_t applesmc_position_show(struct device *dev,
599 struct device_attribute *attr, char *buf)
600{
601 int ret;
602 s16 x, y, z;
603
604 mutex_lock(&applesmc_lock);
605
606 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
607 if (ret)
608 goto out;
609 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
610 if (ret)
611 goto out;
612 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
613 if (ret)
614 goto out;
615
616out:
617 mutex_unlock(&applesmc_lock);
618 if (ret)
619 return ret;
620 else
621 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
622}
623
624static ssize_t applesmc_light_show(struct device *dev,
625 struct device_attribute *attr, char *sysfsbuf)
626{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700627 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700628 int ret;
629 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700630 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700631
632 mutex_lock(&applesmc_lock);
633
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700634 if (!data_length) {
635 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
636 if (ret)
637 goto out;
638 data_length = clamp_val(query[0], 0, 10);
639 printk(KERN_INFO "applesmc: light sensor data length set to "
640 "%d\n", data_length);
641 }
642
643 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800644 /* newer macbooks report a single 10-bit bigendian value */
645 if (data_length == 10) {
646 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
647 goto out;
648 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700649 left = buffer[2];
650 if (ret)
651 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700652 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700653 right = buffer[2];
654
655out:
656 mutex_unlock(&applesmc_lock);
657 if (ret)
658 return ret;
659 else
660 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
661}
662
Alex Murrayfa5575c2010-05-27 19:58:54 +0200663/* Displays sensor key as label */
664static ssize_t applesmc_show_sensor_label(struct device *dev,
665 struct device_attribute *devattr, char *sysfsbuf)
666{
667 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
668 const char *key =
669 temperature_sensors_sets[applesmc_temperature_set][attr->index];
670
671 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
672}
673
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700674/* Displays degree Celsius * 1000 */
675static ssize_t applesmc_show_temperature(struct device *dev,
676 struct device_attribute *devattr, char *sysfsbuf)
677{
678 int ret;
679 u8 buffer[2];
680 unsigned int temp;
681 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
682 const char* key =
683 temperature_sensors_sets[applesmc_temperature_set][attr->index];
684
685 mutex_lock(&applesmc_lock);
686
687 ret = applesmc_read_key(key, buffer, 2);
688 temp = buffer[0]*1000;
689 temp += (buffer[1] >> 6) * 250;
690
691 mutex_unlock(&applesmc_lock);
692
693 if (ret)
694 return ret;
695 else
696 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
697}
698
699static ssize_t applesmc_show_fan_speed(struct device *dev,
700 struct device_attribute *attr, char *sysfsbuf)
701{
702 int ret;
703 unsigned int speed = 0;
704 char newkey[5];
705 u8 buffer[2];
706 struct sensor_device_attribute_2 *sensor_attr =
707 to_sensor_dev_attr_2(attr);
708
709 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
710 newkey[1] = '0' + sensor_attr->index;
711 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
712 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
713 newkey[4] = 0;
714
715 mutex_lock(&applesmc_lock);
716
717 ret = applesmc_read_key(newkey, buffer, 2);
718 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
719
720 mutex_unlock(&applesmc_lock);
721 if (ret)
722 return ret;
723 else
724 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
725}
726
727static ssize_t applesmc_store_fan_speed(struct device *dev,
728 struct device_attribute *attr,
729 const char *sysfsbuf, size_t count)
730{
731 int ret;
732 u32 speed;
733 char newkey[5];
734 u8 buffer[2];
735 struct sensor_device_attribute_2 *sensor_attr =
736 to_sensor_dev_attr_2(attr);
737
738 speed = simple_strtoul(sysfsbuf, NULL, 10);
739
740 if (speed > 0x4000) /* Bigger than a 14-bit value */
741 return -EINVAL;
742
743 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
744 newkey[1] = '0' + sensor_attr->index;
745 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
746 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
747 newkey[4] = 0;
748
749 mutex_lock(&applesmc_lock);
750
751 buffer[0] = (speed >> 6) & 0xff;
752 buffer[1] = (speed << 2) & 0xff;
753 ret = applesmc_write_key(newkey, buffer, 2);
754
755 mutex_unlock(&applesmc_lock);
756 if (ret)
757 return ret;
758 else
759 return count;
760}
761
762static ssize_t applesmc_show_fan_manual(struct device *dev,
763 struct device_attribute *devattr, char *sysfsbuf)
764{
765 int ret;
766 u16 manual = 0;
767 u8 buffer[2];
768 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
769
770 mutex_lock(&applesmc_lock);
771
772 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
773 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
774
775 mutex_unlock(&applesmc_lock);
776 if (ret)
777 return ret;
778 else
779 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
780}
781
782static ssize_t applesmc_store_fan_manual(struct device *dev,
783 struct device_attribute *devattr,
784 const char *sysfsbuf, size_t count)
785{
786 int ret;
787 u8 buffer[2];
788 u32 input;
789 u16 val;
790 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
791
792 input = simple_strtoul(sysfsbuf, NULL, 10);
793
794 mutex_lock(&applesmc_lock);
795
796 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
797 val = (buffer[0] << 8 | buffer[1]);
798 if (ret)
799 goto out;
800
801 if (input)
802 val = val | (0x01 << attr->index);
803 else
804 val = val & ~(0x01 << attr->index);
805
806 buffer[0] = (val >> 8) & 0xFF;
807 buffer[1] = val & 0xFF;
808
809 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
810
811out:
812 mutex_unlock(&applesmc_lock);
813 if (ret)
814 return ret;
815 else
816 return count;
817}
818
819static ssize_t applesmc_show_fan_position(struct device *dev,
820 struct device_attribute *attr, char *sysfsbuf)
821{
822 int ret;
823 char newkey[5];
824 u8 buffer[17];
825 struct sensor_device_attribute_2 *sensor_attr =
826 to_sensor_dev_attr_2(attr);
827
828 newkey[0] = FAN_POSITION[0];
829 newkey[1] = '0' + sensor_attr->index;
830 newkey[2] = FAN_POSITION[2];
831 newkey[3] = FAN_POSITION[3];
832 newkey[4] = 0;
833
834 mutex_lock(&applesmc_lock);
835
836 ret = applesmc_read_key(newkey, buffer, 16);
837 buffer[16] = 0;
838
839 mutex_unlock(&applesmc_lock);
840 if (ret)
841 return ret;
842 else
843 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
844}
845
846static ssize_t applesmc_calibrate_show(struct device *dev,
847 struct device_attribute *attr, char *sysfsbuf)
848{
849 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
850}
851
852static ssize_t applesmc_calibrate_store(struct device *dev,
853 struct device_attribute *attr, const char *sysfsbuf, size_t count)
854{
855 mutex_lock(&applesmc_lock);
856 applesmc_calibrate();
857 mutex_unlock(&applesmc_lock);
858
859 return count;
860}
861
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700862static void applesmc_backlight_set(struct work_struct *work)
863{
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700864 mutex_lock(&applesmc_lock);
Henrik Rydberga976f152009-09-21 17:04:50 -0700865 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700866 mutex_unlock(&applesmc_lock);
867}
868static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
869
870static void applesmc_brightness_set(struct led_classdev *led_cdev,
871 enum led_brightness value)
872{
873 int ret;
874
Henrik Rydberga976f152009-09-21 17:04:50 -0700875 backlight_state[0] = value;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700876 ret = queue_work(applesmc_led_wq, &backlight_work);
877
878 if (debug && (!ret))
879 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
880}
881
882static ssize_t applesmc_key_count_show(struct device *dev,
883 struct device_attribute *attr, char *sysfsbuf)
884{
885 int ret;
886 u8 buffer[4];
887 u32 count;
888
889 mutex_lock(&applesmc_lock);
890
891 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
892 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
893 ((u32)buffer[2]<<8) + buffer[3];
894
895 mutex_unlock(&applesmc_lock);
896 if (ret)
897 return ret;
898 else
899 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
900}
901
902static ssize_t applesmc_key_at_index_read_show(struct device *dev,
903 struct device_attribute *attr, char *sysfsbuf)
904{
905 char key[5];
906 char info[6];
907 int ret;
908
909 mutex_lock(&applesmc_lock);
910
911 ret = applesmc_get_key_at_index(key_at_index, key);
912
913 if (ret || !key[0]) {
914 mutex_unlock(&applesmc_lock);
915
916 return -EINVAL;
917 }
918
919 ret = applesmc_get_key_type(key, info);
920
921 if (ret) {
922 mutex_unlock(&applesmc_lock);
923
924 return ret;
925 }
926
927 /*
928 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
929 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
930 */
931 ret = applesmc_read_key(key, sysfsbuf, info[0]);
932
933 mutex_unlock(&applesmc_lock);
934
935 if (!ret) {
936 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400937 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700938 return ret;
939 }
940}
941
942static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
943 struct device_attribute *attr, char *sysfsbuf)
944{
945 char key[5];
946 char info[6];
947 int ret;
948
949 mutex_lock(&applesmc_lock);
950
951 ret = applesmc_get_key_at_index(key_at_index, key);
952
953 if (ret || !key[0]) {
954 mutex_unlock(&applesmc_lock);
955
956 return -EINVAL;
957 }
958
959 ret = applesmc_get_key_type(key, info);
960
961 mutex_unlock(&applesmc_lock);
962
963 if (!ret)
964 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
965 else
966 return ret;
967}
968
969static ssize_t applesmc_key_at_index_type_show(struct device *dev,
970 struct device_attribute *attr, char *sysfsbuf)
971{
972 char key[5];
973 char info[6];
974 int ret;
975
976 mutex_lock(&applesmc_lock);
977
978 ret = applesmc_get_key_at_index(key_at_index, key);
979
980 if (ret || !key[0]) {
981 mutex_unlock(&applesmc_lock);
982
983 return -EINVAL;
984 }
985
986 ret = applesmc_get_key_type(key, info);
987
988 mutex_unlock(&applesmc_lock);
989
990 if (!ret)
991 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
992 else
993 return ret;
994}
995
996static ssize_t applesmc_key_at_index_name_show(struct device *dev,
997 struct device_attribute *attr, char *sysfsbuf)
998{
999 char key[5];
1000 int ret;
1001
1002 mutex_lock(&applesmc_lock);
1003
1004 ret = applesmc_get_key_at_index(key_at_index, key);
1005
1006 mutex_unlock(&applesmc_lock);
1007
1008 if (!ret && key[0])
1009 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
1010 else
1011 return -EINVAL;
1012}
1013
1014static ssize_t applesmc_key_at_index_show(struct device *dev,
1015 struct device_attribute *attr, char *sysfsbuf)
1016{
1017 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
1018}
1019
1020static ssize_t applesmc_key_at_index_store(struct device *dev,
1021 struct device_attribute *attr, const char *sysfsbuf, size_t count)
1022{
1023 mutex_lock(&applesmc_lock);
1024
1025 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
1026
1027 mutex_unlock(&applesmc_lock);
1028
1029 return count;
1030}
1031
1032static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +01001033 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001034 .default_trigger = "nand-disk",
1035 .brightness_set = applesmc_brightness_set,
1036};
1037
Nicolas Boichatfa744192007-05-23 13:58:13 -07001038static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
1039
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001040static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
1041static DEVICE_ATTR(calibrate, 0644,
1042 applesmc_calibrate_show, applesmc_calibrate_store);
1043
1044static struct attribute *accelerometer_attributes[] = {
1045 &dev_attr_position.attr,
1046 &dev_attr_calibrate.attr,
1047 NULL
1048};
1049
1050static const struct attribute_group accelerometer_attributes_group =
1051 { .attrs = accelerometer_attributes };
1052
1053static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1054
1055static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1056static DEVICE_ATTR(key_at_index, 0644,
1057 applesmc_key_at_index_show, applesmc_key_at_index_store);
1058static DEVICE_ATTR(key_at_index_name, 0444,
1059 applesmc_key_at_index_name_show, NULL);
1060static DEVICE_ATTR(key_at_index_type, 0444,
1061 applesmc_key_at_index_type_show, NULL);
1062static DEVICE_ATTR(key_at_index_data_length, 0444,
1063 applesmc_key_at_index_data_length_show, NULL);
1064static DEVICE_ATTR(key_at_index_data, 0444,
1065 applesmc_key_at_index_read_show, NULL);
1066
1067static struct attribute *key_enumeration_attributes[] = {
1068 &dev_attr_key_count.attr,
1069 &dev_attr_key_at_index.attr,
1070 &dev_attr_key_at_index_name.attr,
1071 &dev_attr_key_at_index_type.attr,
1072 &dev_attr_key_at_index_data_length.attr,
1073 &dev_attr_key_at_index_data.attr,
1074 NULL
1075};
1076
1077static const struct attribute_group key_enumeration_group =
1078 { .attrs = key_enumeration_attributes };
1079
1080/*
1081 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1082 * - show actual speed
1083 * - show/store minimum speed
1084 * - show maximum speed
1085 * - show safe speed
1086 * - show/store target speed
1087 * - show/store manual mode
1088 */
1089#define sysfs_fan_speeds_offset(offset) \
1090static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1091 applesmc_show_fan_speed, NULL, 0, offset-1); \
1092\
1093static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1094 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1095\
1096static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1097 applesmc_show_fan_speed, NULL, 2, offset-1); \
1098\
1099static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1100 applesmc_show_fan_speed, NULL, 3, offset-1); \
1101\
1102static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1103 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1104\
1105static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1106 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1107\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001108static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001109 applesmc_show_fan_position, NULL, offset-1); \
1110\
1111static struct attribute *fan##offset##_attributes[] = { \
1112 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1113 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1114 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1115 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1116 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1117 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001118 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001119 NULL \
1120};
1121
1122/*
1123 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001124 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001125 */
1126sysfs_fan_speeds_offset(1);
1127sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001128sysfs_fan_speeds_offset(3);
1129sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001130
1131static const struct attribute_group fan_attribute_groups[] = {
1132 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001133 { .attrs = fan2_attributes },
1134 { .attrs = fan3_attributes },
1135 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001136};
1137
1138/*
1139 * Temperature sensors sysfs entries.
1140 */
Alex Murrayfa5575c2010-05-27 19:58:54 +02001141static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO,
1142 applesmc_show_sensor_label, NULL, 0);
1143static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO,
1144 applesmc_show_sensor_label, NULL, 1);
1145static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO,
1146 applesmc_show_sensor_label, NULL, 2);
1147static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO,
1148 applesmc_show_sensor_label, NULL, 3);
1149static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO,
1150 applesmc_show_sensor_label, NULL, 4);
1151static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO,
1152 applesmc_show_sensor_label, NULL, 5);
1153static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO,
1154 applesmc_show_sensor_label, NULL, 6);
1155static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO,
1156 applesmc_show_sensor_label, NULL, 7);
1157static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO,
1158 applesmc_show_sensor_label, NULL, 8);
1159static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO,
1160 applesmc_show_sensor_label, NULL, 9);
1161static SENSOR_DEVICE_ATTR(temp11_label, S_IRUGO,
1162 applesmc_show_sensor_label, NULL, 10);
1163static SENSOR_DEVICE_ATTR(temp12_label, S_IRUGO,
1164 applesmc_show_sensor_label, NULL, 11);
1165static SENSOR_DEVICE_ATTR(temp13_label, S_IRUGO,
1166 applesmc_show_sensor_label, NULL, 12);
1167static SENSOR_DEVICE_ATTR(temp14_label, S_IRUGO,
1168 applesmc_show_sensor_label, NULL, 13);
1169static SENSOR_DEVICE_ATTR(temp15_label, S_IRUGO,
1170 applesmc_show_sensor_label, NULL, 14);
1171static SENSOR_DEVICE_ATTR(temp16_label, S_IRUGO,
1172 applesmc_show_sensor_label, NULL, 15);
1173static SENSOR_DEVICE_ATTR(temp17_label, S_IRUGO,
1174 applesmc_show_sensor_label, NULL, 16);
1175static SENSOR_DEVICE_ATTR(temp18_label, S_IRUGO,
1176 applesmc_show_sensor_label, NULL, 17);
1177static SENSOR_DEVICE_ATTR(temp19_label, S_IRUGO,
1178 applesmc_show_sensor_label, NULL, 18);
1179static SENSOR_DEVICE_ATTR(temp20_label, S_IRUGO,
1180 applesmc_show_sensor_label, NULL, 19);
1181static SENSOR_DEVICE_ATTR(temp21_label, S_IRUGO,
1182 applesmc_show_sensor_label, NULL, 20);
1183static SENSOR_DEVICE_ATTR(temp22_label, S_IRUGO,
1184 applesmc_show_sensor_label, NULL, 21);
1185static SENSOR_DEVICE_ATTR(temp23_label, S_IRUGO,
1186 applesmc_show_sensor_label, NULL, 22);
1187static SENSOR_DEVICE_ATTR(temp24_label, S_IRUGO,
1188 applesmc_show_sensor_label, NULL, 23);
1189static SENSOR_DEVICE_ATTR(temp25_label, S_IRUGO,
1190 applesmc_show_sensor_label, NULL, 24);
1191static SENSOR_DEVICE_ATTR(temp26_label, S_IRUGO,
1192 applesmc_show_sensor_label, NULL, 25);
1193static SENSOR_DEVICE_ATTR(temp27_label, S_IRUGO,
1194 applesmc_show_sensor_label, NULL, 26);
1195static SENSOR_DEVICE_ATTR(temp28_label, S_IRUGO,
1196 applesmc_show_sensor_label, NULL, 27);
1197static SENSOR_DEVICE_ATTR(temp29_label, S_IRUGO,
1198 applesmc_show_sensor_label, NULL, 28);
1199static SENSOR_DEVICE_ATTR(temp30_label, S_IRUGO,
1200 applesmc_show_sensor_label, NULL, 29);
1201static SENSOR_DEVICE_ATTR(temp31_label, S_IRUGO,
1202 applesmc_show_sensor_label, NULL, 30);
1203static SENSOR_DEVICE_ATTR(temp32_label, S_IRUGO,
1204 applesmc_show_sensor_label, NULL, 31);
1205static SENSOR_DEVICE_ATTR(temp33_label, S_IRUGO,
1206 applesmc_show_sensor_label, NULL, 32);
1207static SENSOR_DEVICE_ATTR(temp34_label, S_IRUGO,
1208 applesmc_show_sensor_label, NULL, 33);
1209static SENSOR_DEVICE_ATTR(temp35_label, S_IRUGO,
1210 applesmc_show_sensor_label, NULL, 34);
1211static SENSOR_DEVICE_ATTR(temp36_label, S_IRUGO,
1212 applesmc_show_sensor_label, NULL, 35);
1213static SENSOR_DEVICE_ATTR(temp37_label, S_IRUGO,
1214 applesmc_show_sensor_label, NULL, 36);
1215static SENSOR_DEVICE_ATTR(temp38_label, S_IRUGO,
1216 applesmc_show_sensor_label, NULL, 37);
1217static SENSOR_DEVICE_ATTR(temp39_label, S_IRUGO,
1218 applesmc_show_sensor_label, NULL, 38);
1219static SENSOR_DEVICE_ATTR(temp40_label, S_IRUGO,
1220 applesmc_show_sensor_label, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001221static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1222 applesmc_show_temperature, NULL, 0);
1223static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1224 applesmc_show_temperature, NULL, 1);
1225static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1226 applesmc_show_temperature, NULL, 2);
1227static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1228 applesmc_show_temperature, NULL, 3);
1229static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1230 applesmc_show_temperature, NULL, 4);
1231static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1232 applesmc_show_temperature, NULL, 5);
1233static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1234 applesmc_show_temperature, NULL, 6);
1235static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1236 applesmc_show_temperature, NULL, 7);
1237static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1238 applesmc_show_temperature, NULL, 8);
1239static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1240 applesmc_show_temperature, NULL, 9);
1241static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1242 applesmc_show_temperature, NULL, 10);
1243static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1244 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001245static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1246 applesmc_show_temperature, NULL, 12);
1247static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1248 applesmc_show_temperature, NULL, 13);
1249static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1250 applesmc_show_temperature, NULL, 14);
1251static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1252 applesmc_show_temperature, NULL, 15);
1253static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1254 applesmc_show_temperature, NULL, 16);
1255static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1256 applesmc_show_temperature, NULL, 17);
1257static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1258 applesmc_show_temperature, NULL, 18);
1259static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1260 applesmc_show_temperature, NULL, 19);
1261static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1262 applesmc_show_temperature, NULL, 20);
1263static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1264 applesmc_show_temperature, NULL, 21);
1265static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1266 applesmc_show_temperature, NULL, 22);
1267static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1268 applesmc_show_temperature, NULL, 23);
1269static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1270 applesmc_show_temperature, NULL, 24);
1271static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1272 applesmc_show_temperature, NULL, 25);
1273static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1274 applesmc_show_temperature, NULL, 26);
1275static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1276 applesmc_show_temperature, NULL, 27);
1277static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1278 applesmc_show_temperature, NULL, 28);
1279static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1280 applesmc_show_temperature, NULL, 29);
1281static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1282 applesmc_show_temperature, NULL, 30);
1283static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1284 applesmc_show_temperature, NULL, 31);
1285static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1286 applesmc_show_temperature, NULL, 32);
1287static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1288 applesmc_show_temperature, NULL, 33);
1289static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1290 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e12009-01-29 14:25:24 -08001291static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1292 applesmc_show_temperature, NULL, 35);
1293static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1294 applesmc_show_temperature, NULL, 36);
1295static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1296 applesmc_show_temperature, NULL, 37);
1297static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1298 applesmc_show_temperature, NULL, 38);
1299static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1300 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001301
Alex Murrayfa5575c2010-05-27 19:58:54 +02001302static struct attribute *label_attributes[] = {
1303 &sensor_dev_attr_temp1_label.dev_attr.attr,
1304 &sensor_dev_attr_temp2_label.dev_attr.attr,
1305 &sensor_dev_attr_temp3_label.dev_attr.attr,
1306 &sensor_dev_attr_temp4_label.dev_attr.attr,
1307 &sensor_dev_attr_temp5_label.dev_attr.attr,
1308 &sensor_dev_attr_temp6_label.dev_attr.attr,
1309 &sensor_dev_attr_temp7_label.dev_attr.attr,
1310 &sensor_dev_attr_temp8_label.dev_attr.attr,
1311 &sensor_dev_attr_temp9_label.dev_attr.attr,
1312 &sensor_dev_attr_temp10_label.dev_attr.attr,
1313 &sensor_dev_attr_temp11_label.dev_attr.attr,
1314 &sensor_dev_attr_temp12_label.dev_attr.attr,
1315 &sensor_dev_attr_temp13_label.dev_attr.attr,
1316 &sensor_dev_attr_temp14_label.dev_attr.attr,
1317 &sensor_dev_attr_temp15_label.dev_attr.attr,
1318 &sensor_dev_attr_temp16_label.dev_attr.attr,
1319 &sensor_dev_attr_temp17_label.dev_attr.attr,
1320 &sensor_dev_attr_temp18_label.dev_attr.attr,
1321 &sensor_dev_attr_temp19_label.dev_attr.attr,
1322 &sensor_dev_attr_temp20_label.dev_attr.attr,
1323 &sensor_dev_attr_temp21_label.dev_attr.attr,
1324 &sensor_dev_attr_temp22_label.dev_attr.attr,
1325 &sensor_dev_attr_temp23_label.dev_attr.attr,
1326 &sensor_dev_attr_temp24_label.dev_attr.attr,
1327 &sensor_dev_attr_temp25_label.dev_attr.attr,
1328 &sensor_dev_attr_temp26_label.dev_attr.attr,
1329 &sensor_dev_attr_temp27_label.dev_attr.attr,
1330 &sensor_dev_attr_temp28_label.dev_attr.attr,
1331 &sensor_dev_attr_temp29_label.dev_attr.attr,
1332 &sensor_dev_attr_temp30_label.dev_attr.attr,
1333 &sensor_dev_attr_temp31_label.dev_attr.attr,
1334 &sensor_dev_attr_temp32_label.dev_attr.attr,
1335 &sensor_dev_attr_temp33_label.dev_attr.attr,
1336 &sensor_dev_attr_temp34_label.dev_attr.attr,
1337 &sensor_dev_attr_temp35_label.dev_attr.attr,
1338 &sensor_dev_attr_temp36_label.dev_attr.attr,
1339 &sensor_dev_attr_temp37_label.dev_attr.attr,
1340 &sensor_dev_attr_temp38_label.dev_attr.attr,
1341 &sensor_dev_attr_temp39_label.dev_attr.attr,
1342 &sensor_dev_attr_temp40_label.dev_attr.attr,
1343 NULL
1344};
1345
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001346static struct attribute *temperature_attributes[] = {
1347 &sensor_dev_attr_temp1_input.dev_attr.attr,
1348 &sensor_dev_attr_temp2_input.dev_attr.attr,
1349 &sensor_dev_attr_temp3_input.dev_attr.attr,
1350 &sensor_dev_attr_temp4_input.dev_attr.attr,
1351 &sensor_dev_attr_temp5_input.dev_attr.attr,
1352 &sensor_dev_attr_temp6_input.dev_attr.attr,
1353 &sensor_dev_attr_temp7_input.dev_attr.attr,
1354 &sensor_dev_attr_temp8_input.dev_attr.attr,
1355 &sensor_dev_attr_temp9_input.dev_attr.attr,
1356 &sensor_dev_attr_temp10_input.dev_attr.attr,
1357 &sensor_dev_attr_temp11_input.dev_attr.attr,
1358 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001359 &sensor_dev_attr_temp13_input.dev_attr.attr,
1360 &sensor_dev_attr_temp14_input.dev_attr.attr,
1361 &sensor_dev_attr_temp15_input.dev_attr.attr,
1362 &sensor_dev_attr_temp16_input.dev_attr.attr,
1363 &sensor_dev_attr_temp17_input.dev_attr.attr,
1364 &sensor_dev_attr_temp18_input.dev_attr.attr,
1365 &sensor_dev_attr_temp19_input.dev_attr.attr,
1366 &sensor_dev_attr_temp20_input.dev_attr.attr,
1367 &sensor_dev_attr_temp21_input.dev_attr.attr,
1368 &sensor_dev_attr_temp22_input.dev_attr.attr,
1369 &sensor_dev_attr_temp23_input.dev_attr.attr,
1370 &sensor_dev_attr_temp24_input.dev_attr.attr,
1371 &sensor_dev_attr_temp25_input.dev_attr.attr,
1372 &sensor_dev_attr_temp26_input.dev_attr.attr,
1373 &sensor_dev_attr_temp27_input.dev_attr.attr,
1374 &sensor_dev_attr_temp28_input.dev_attr.attr,
1375 &sensor_dev_attr_temp29_input.dev_attr.attr,
1376 &sensor_dev_attr_temp30_input.dev_attr.attr,
1377 &sensor_dev_attr_temp31_input.dev_attr.attr,
1378 &sensor_dev_attr_temp32_input.dev_attr.attr,
1379 &sensor_dev_attr_temp33_input.dev_attr.attr,
1380 &sensor_dev_attr_temp34_input.dev_attr.attr,
1381 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e12009-01-29 14:25:24 -08001382 &sensor_dev_attr_temp36_input.dev_attr.attr,
1383 &sensor_dev_attr_temp37_input.dev_attr.attr,
1384 &sensor_dev_attr_temp38_input.dev_attr.attr,
1385 &sensor_dev_attr_temp39_input.dev_attr.attr,
1386 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001387 NULL
1388};
1389
1390static const struct attribute_group temperature_attributes_group =
1391 { .attrs = temperature_attributes };
1392
Alex Murrayfa5575c2010-05-27 19:58:54 +02001393static const struct attribute_group label_attributes_group = {
1394 .attrs = label_attributes
1395};
1396
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001397/* Module stuff */
1398
1399/*
1400 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1401 */
Jeff Garzik18552562007-10-03 15:15:40 -04001402static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001403{
1404 int i = 0;
1405 struct dmi_match_data* dmi_data = id->driver_data;
1406 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1407 applesmc_accelerometer = dmi_data->accelerometer;
1408 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1409 applesmc_accelerometer ? "with" : "without");
1410 applesmc_light = dmi_data->light;
1411 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1412 applesmc_light ? "with" : "without");
1413
1414 applesmc_temperature_set = dmi_data->temperature_set;
1415 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1416 i++;
1417 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1418 return 1;
1419}
1420
1421/* Create accelerometer ressources */
1422static int applesmc_create_accelerometer(void)
1423{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001424 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001425 int ret;
1426
1427 ret = sysfs_create_group(&pdev->dev.kobj,
1428 &accelerometer_attributes_group);
1429 if (ret)
1430 goto out;
1431
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001432 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001433 if (!applesmc_idev) {
1434 ret = -ENOMEM;
1435 goto out_sysfs;
1436 }
1437
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001438 applesmc_idev->poll = applesmc_idev_poll;
1439 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1440
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001441 /* initial calibrate for the input device */
1442 applesmc_calibrate();
1443
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001444 /* initialize the input device */
1445 idev = applesmc_idev->input;
1446 idev->name = "applesmc";
1447 idev->id.bustype = BUS_HOST;
1448 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001449 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001450 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001451 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001452 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001453 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1454
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001455 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001456 if (ret)
1457 goto out_idev;
1458
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001459 return 0;
1460
1461out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001462 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001463
1464out_sysfs:
1465 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1466
1467out:
1468 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1469 return ret;
1470}
1471
1472/* Release all ressources used by the accelerometer */
1473static void applesmc_release_accelerometer(void)
1474{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001475 input_unregister_polled_device(applesmc_idev);
1476 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001477 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1478}
1479
1480static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1481/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1482 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001483/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001484 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001485/* MacBook: accelerometer and temperature set 2 */
1486 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1487/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001488 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001489/* MacPro: temperature set 4 */
1490 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001491/* iMac: temperature set 5 */
1492 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001493/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001494 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001495/* MacBook Air: accelerometer, backlight and temperature set 7 */
1496 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001497/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1498 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001499/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1500 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001501/* iMac 5: light sensor only, temperature set 10 */
1502 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a2008-11-06 12:53:20 -08001503/* MacBook 5: accelerometer, backlight and temperature set 11 */
1504 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001505/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1506 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001507/* iMac 8: light sensor only, temperature set 13 */
1508 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001509/* iMac 6: light sensor only, temperature set 14 */
1510 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001511/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1512 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e12009-01-29 14:25:24 -08001513/* MacPro3,1: temperature set 16 */
1514 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Justin P. Mattocke1741712010-04-14 16:14:10 +02001515/* iMac 9,1: light sensor only, temperature set 17 */
1516 { .accelerometer = 0, .light = 0, .temperature_set = 17 },
1517/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
1518 { .accelerometer = 1, .light = 1, .temperature_set = 18 },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001519/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
1520 { .accelerometer = 1, .light = 1, .temperature_set = 19 },
1521/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
1522 { .accelerometer = 1, .light = 1, .temperature_set = 20 },
Bernhard Froemel872bad52010-05-27 19:58:52 +02001523/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
1524 { .accelerometer = 1, .light = 1, .temperature_set = 21 },
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001525/* MacBook Pro 7,1: accelerometer, backlight and temperature set 22 */
1526 { .accelerometer = 1, .light = 1, .temperature_set = 22 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001527};
1528
1529/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1530 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1531static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001532 { applesmc_dmi_match, "Apple MacBook Air 2", {
1533 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1534 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1535 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001536 { applesmc_dmi_match, "Apple MacBook Air", {
1537 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1538 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001539 &applesmc_dmi_data[7]},
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001540 { applesmc_dmi_match, "Apple MacBook Pro 7", {
1541 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1542 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro7") },
1543 &applesmc_dmi_data[22]},
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001544 { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
1545 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1546 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
1547 &applesmc_dmi_data[20]},
1548 { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
1549 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1550 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
1551 &applesmc_dmi_data[19]},
Bernhard Froemel872bad52010-05-27 19:58:52 +02001552 { applesmc_dmi_match, "Apple MacBook Pro 6", {
1553 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1554 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
1555 &applesmc_dmi_data[21]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001556 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1557 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1558 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1559 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001560 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1561 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1562 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1563 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001564 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1565 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1566 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1567 &applesmc_dmi_data[9]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001568 { applesmc_dmi_match, "Apple MacBook Pro 2,2", {
1569 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Computer, Inc."),
1570 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2") },
1571 &applesmc_dmi_data[18]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001572 { applesmc_dmi_match, "Apple MacBook Pro", {
1573 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1574 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001575 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001576 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001577 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001578 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001579 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001580 { applesmc_dmi_match, "Apple MacBook (v3)", {
1581 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1582 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001583 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001584 { applesmc_dmi_match, "Apple MacBook 4", {
1585 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1586 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1587 &applesmc_dmi_data[6]},
Henrik Rydberg181209a2008-11-06 12:53:20 -08001588 { applesmc_dmi_match, "Apple MacBook 5", {
1589 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1590 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1591 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001592 { applesmc_dmi_match, "Apple MacBook", {
1593 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1594 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001595 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001596 { applesmc_dmi_match, "Apple Macmini", {
1597 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1598 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001599 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001600 { applesmc_dmi_match, "Apple MacPro2", {
1601 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1602 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001603 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e12009-01-29 14:25:24 -08001604 { applesmc_dmi_match, "Apple MacPro3", {
1605 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1606 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1607 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001608 { applesmc_dmi_match, "Apple MacPro", {
1609 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1610 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1611 &applesmc_dmi_data[4]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001612 { applesmc_dmi_match, "Apple iMac 9,1", {
1613 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
1614 DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1") },
1615 &applesmc_dmi_data[17]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001616 { applesmc_dmi_match, "Apple iMac 8", {
1617 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1618 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1619 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001620 { applesmc_dmi_match, "Apple iMac 6", {
1621 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1622 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1623 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001624 { applesmc_dmi_match, "Apple iMac 5", {
1625 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1626 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1627 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001628 { applesmc_dmi_match, "Apple iMac", {
1629 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1630 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001631 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001632 { .ident = NULL }
1633};
1634
1635static int __init applesmc_init(void)
1636{
1637 int ret;
1638 int count;
1639 int i;
1640
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001641 if (!dmi_check_system(applesmc_whitelist)) {
1642 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1643 ret = -ENODEV;
1644 goto out;
1645 }
1646
1647 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1648 "applesmc")) {
1649 ret = -ENXIO;
1650 goto out;
1651 }
1652
1653 ret = platform_driver_register(&applesmc_driver);
1654 if (ret)
1655 goto out_region;
1656
Jean Delvareddfbf2af2007-05-08 20:27:04 -07001657 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1658 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001659 if (IS_ERR(pdev)) {
1660 ret = PTR_ERR(pdev);
1661 goto out_driver;
1662 }
1663
Nicolas Boichatfa744192007-05-23 13:58:13 -07001664 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001665 if (ret)
1666 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001667
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001668 /* Create key enumeration sysfs files */
1669 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1670 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001671 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001672
1673 /* create fan files */
1674 count = applesmc_get_fan_count();
Henrik Rydberg0559a532010-05-11 09:17:47 +02001675 if (count < 0)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001676 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001677 else
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001678 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1679
Henrik Rydberg0559a532010-05-11 09:17:47 +02001680 if (count > 4) {
1681 count = 4;
1682 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1683 " but at most 4 fans are supported"
1684 " by the driver.\n");
1685 }
1686
1687 while (fans_handled < count) {
1688 ret = sysfs_create_group(&pdev->dev.kobj,
1689 &fan_attribute_groups[fans_handled]);
1690 if (ret)
1691 goto out_fans;
1692 fans_handled++;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001693 }
1694
1695 for (i = 0;
1696 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1697 i++) {
Alex Murrayfa5575c2010-05-27 19:58:54 +02001698 if (temperature_attributes[i] == NULL ||
1699 label_attributes[i] == NULL) {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001700 printk(KERN_ERR "applesmc: More temperature sensors "
1701 "in temperature_sensors_sets (at least %i)"
1702 "than available sysfs files in "
1703 "temperature_attributes (%i), please report "
1704 "this bug.\n", i, i-1);
1705 goto out_temperature;
1706 }
1707 ret = sysfs_create_file(&pdev->dev.kobj,
1708 temperature_attributes[i]);
1709 if (ret)
1710 goto out_temperature;
Alex Murrayfa5575c2010-05-27 19:58:54 +02001711 ret = sysfs_create_file(&pdev->dev.kobj,
1712 label_attributes[i]);
1713 if (ret)
1714 goto out_temperature;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001715 }
1716
1717 if (applesmc_accelerometer) {
1718 ret = applesmc_create_accelerometer();
1719 if (ret)
1720 goto out_temperature;
1721 }
1722
1723 if (applesmc_light) {
1724 /* Add light sensor file */
1725 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1726 if (ret)
1727 goto out_accelerometer;
1728
1729 /* Create the workqueue */
1730 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1731 if (!applesmc_led_wq) {
1732 ret = -ENOMEM;
1733 goto out_light_sysfs;
1734 }
1735
1736 /* register as a led device */
1737 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1738 if (ret < 0)
1739 goto out_light_wq;
1740 }
1741
Tony Jones1beeffe2007-08-20 13:46:20 -07001742 hwmon_dev = hwmon_device_register(&pdev->dev);
1743 if (IS_ERR(hwmon_dev)) {
1744 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001745 goto out_light_ledclass;
1746 }
1747
1748 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1749
1750 return 0;
1751
1752out_light_ledclass:
1753 if (applesmc_light)
1754 led_classdev_unregister(&applesmc_backlight);
1755out_light_wq:
1756 if (applesmc_light)
1757 destroy_workqueue(applesmc_led_wq);
1758out_light_sysfs:
1759 if (applesmc_light)
1760 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1761out_accelerometer:
1762 if (applesmc_accelerometer)
1763 applesmc_release_accelerometer();
1764out_temperature:
Alex Murrayfa5575c2010-05-27 19:58:54 +02001765 sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001766 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001767out_fans:
1768 while (fans_handled)
1769 sysfs_remove_group(&pdev->dev.kobj,
1770 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001771 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001772out_name:
1773 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001774out_device:
1775 platform_device_unregister(pdev);
1776out_driver:
1777 platform_driver_unregister(&applesmc_driver);
1778out_region:
1779 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1780out:
1781 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1782 return ret;
1783}
1784
1785static void __exit applesmc_exit(void)
1786{
Tony Jones1beeffe2007-08-20 13:46:20 -07001787 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001788 if (applesmc_light) {
1789 led_classdev_unregister(&applesmc_backlight);
1790 destroy_workqueue(applesmc_led_wq);
1791 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1792 }
1793 if (applesmc_accelerometer)
1794 applesmc_release_accelerometer();
Alex Murrayfa5575c2010-05-27 19:58:54 +02001795 sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001796 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001797 while (fans_handled)
1798 sysfs_remove_group(&pdev->dev.kobj,
1799 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001800 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001801 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001802 platform_device_unregister(pdev);
1803 platform_driver_unregister(&applesmc_driver);
1804 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1805
1806 printk(KERN_INFO "applesmc: driver unloaded.\n");
1807}
1808
1809module_init(applesmc_init);
1810module_exit(applesmc_exit);
1811
1812MODULE_AUTHOR("Nicolas Boichat");
1813MODULE_DESCRIPTION("Apple SMC");
1814MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001815MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);