blob: 678e34b01e524d9c20fbe99b8c5473e1135a40db [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 */
Bharath Rameshfb9f88e2009-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 Rydberg181209a12008-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 Rameshfb9f88e2009-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 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700145};
146
147/* List of keys used to read/write fan speeds */
148static const char* fan_speed_keys[] = {
149 FAN_ACTUAL_SPEED,
150 FAN_MIN_SPEED,
151 FAN_MAX_SPEED,
152 FAN_SAFE_SPEED,
153 FAN_TARGET_SPEED
154};
155
156#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
157#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
158
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400159#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700160#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
161#define APPLESMC_INPUT_FLAT 4
162
163#define SENSOR_X 0
164#define SENSOR_Y 1
165#define SENSOR_Z 2
166
167/* Structure to be passed to DMI_MATCH function */
168struct dmi_match_data {
169/* Indicates whether this computer has an accelerometer. */
170 int accelerometer;
171/* Indicates whether this computer has light sensors and keyboard backlight. */
172 int light;
173/* Indicates which temperature sensors set to use. */
174 int temperature_set;
175};
176
177static const int debug;
178static struct platform_device *pdev;
179static s16 rest_x;
180static s16 rest_y;
Tony Jones1beeffe2007-08-20 13:46:20 -0700181static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400182static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700183
184/* Indicates whether this computer has an accelerometer. */
185static unsigned int applesmc_accelerometer;
186
187/* Indicates whether this computer has light sensors and keyboard backlight. */
188static unsigned int applesmc_light;
189
190/* Indicates which temperature sensors set to use. */
191static unsigned int applesmc_temperature_set;
192
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400193static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700194
195/*
196 * Last index written to key_at_index sysfs file, and value to use for all other
197 * key_at_index_* sysfs files.
198 */
199static unsigned int key_at_index;
200
201static struct workqueue_struct *applesmc_led_wq;
202
203/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700204 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700205 * (masked with 0x0f), returning zero if the value is obtained. Callers must
206 * hold applesmc_lock.
207 */
208static int __wait_status(u8 val)
209{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700210 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700211
212 val = val & APPLESMC_STATUS_MASK;
213
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700214 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
215 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700216 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
217 if (debug)
218 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700219 "Waited %d us for status %x\n",
220 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700221 return 0;
222 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700223 }
224
225 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
226 val, inb(APPLESMC_CMD_PORT));
227
228 return -EIO;
229}
230
231/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700232 * special treatment of command port - on newer macbooks, it seems necessary
233 * to resend the command byte before polling the status again. Callers must
234 * hold applesmc_lock.
235 */
236static int send_command(u8 cmd)
237{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700238 int us;
239 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700240 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700241 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700242 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
243 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700244 }
245 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
246 cmd, inb(APPLESMC_CMD_PORT));
247 return -EIO;
248}
249
250/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700251 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
252 * Returns zero on success or a negative error on failure. Callers must
253 * hold applesmc_lock.
254 */
255static int applesmc_read_key(const char* key, u8* buffer, u8 len)
256{
257 int i;
258
259 if (len > APPLESMC_MAX_DATA_LENGTH) {
260 printk(KERN_ERR "applesmc_read_key: cannot read more than "
261 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
262 return -EINVAL;
263 }
264
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700265 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700266 return -EIO;
267
268 for (i = 0; i < 4; i++) {
269 outb(key[i], APPLESMC_DATA_PORT);
270 if (__wait_status(0x04))
271 return -EIO;
272 }
273 if (debug)
274 printk(KERN_DEBUG "<%s", key);
275
276 outb(len, APPLESMC_DATA_PORT);
277 if (debug)
278 printk(KERN_DEBUG ">%x", len);
279
280 for (i = 0; i < len; i++) {
281 if (__wait_status(0x05))
282 return -EIO;
283 buffer[i] = inb(APPLESMC_DATA_PORT);
284 if (debug)
285 printk(KERN_DEBUG "<%x", buffer[i]);
286 }
287 if (debug)
288 printk(KERN_DEBUG "\n");
289
290 return 0;
291}
292
293/*
294 * applesmc_write_key - writes len bytes from buffer to a given key.
295 * Returns zero on success or a negative error on failure. Callers must
296 * hold applesmc_lock.
297 */
298static int applesmc_write_key(const char* key, u8* buffer, u8 len)
299{
300 int i;
301
302 if (len > APPLESMC_MAX_DATA_LENGTH) {
303 printk(KERN_ERR "applesmc_write_key: cannot write more than "
304 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
305 return -EINVAL;
306 }
307
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700308 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700309 return -EIO;
310
311 for (i = 0; i < 4; i++) {
312 outb(key[i], APPLESMC_DATA_PORT);
313 if (__wait_status(0x04))
314 return -EIO;
315 }
316
317 outb(len, APPLESMC_DATA_PORT);
318
319 for (i = 0; i < len; i++) {
320 if (__wait_status(0x04))
321 return -EIO;
322 outb(buffer[i], APPLESMC_DATA_PORT);
323 }
324
325 return 0;
326}
327
328/*
329 * applesmc_get_key_at_index - get key at index, and put the result in key
330 * (char[6]). Returns zero on success or a negative error on failure. Callers
331 * must hold applesmc_lock.
332 */
333static int applesmc_get_key_at_index(int index, char* key)
334{
335 int i;
336 u8 readkey[4];
337 readkey[0] = index >> 24;
338 readkey[1] = index >> 16;
339 readkey[2] = index >> 8;
340 readkey[3] = index;
341
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700342 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700343 return -EIO;
344
345 for (i = 0; i < 4; i++) {
346 outb(readkey[i], APPLESMC_DATA_PORT);
347 if (__wait_status(0x04))
348 return -EIO;
349 }
350
351 outb(4, APPLESMC_DATA_PORT);
352
353 for (i = 0; i < 4; i++) {
354 if (__wait_status(0x05))
355 return -EIO;
356 key[i] = inb(APPLESMC_DATA_PORT);
357 }
358 key[4] = 0;
359
360 return 0;
361}
362
363/*
364 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
365 * Returns zero on success or a negative error on failure. Callers must
366 * hold applesmc_lock.
367 */
368static int applesmc_get_key_type(char* key, char* type)
369{
370 int i;
371
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700372 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700373 return -EIO;
374
375 for (i = 0; i < 4; i++) {
376 outb(key[i], APPLESMC_DATA_PORT);
377 if (__wait_status(0x04))
378 return -EIO;
379 }
380
Henrik Rydberg05224092008-10-18 20:27:35 -0700381 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700382
383 for (i = 0; i < 6; i++) {
384 if (__wait_status(0x05))
385 return -EIO;
386 type[i] = inb(APPLESMC_DATA_PORT);
387 }
388 type[5] = 0;
389
390 return 0;
391}
392
393/*
394 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
395 * hold applesmc_lock.
396 */
397static int applesmc_read_motion_sensor(int index, s16* value)
398{
399 u8 buffer[2];
400 int ret;
401
402 switch (index) {
403 case SENSOR_X:
404 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
405 break;
406 case SENSOR_Y:
407 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
408 break;
409 case SENSOR_Z:
410 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
411 break;
412 default:
413 ret = -EINVAL;
414 }
415
416 *value = ((s16)buffer[0] << 8) | buffer[1];
417
418 return ret;
419}
420
421/*
422 * applesmc_device_init - initialize the accelerometer. Returns zero on success
423 * and negative error code on failure. Can sleep.
424 */
425static int applesmc_device_init(void)
426{
427 int total, ret = -ENXIO;
428 u8 buffer[2];
429
430 if (!applesmc_accelerometer)
431 return 0;
432
433 mutex_lock(&applesmc_lock);
434
435 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
436 if (debug)
437 printk(KERN_DEBUG "applesmc try %d\n", total);
438 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
439 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
440 if (total == INIT_TIMEOUT_MSECS) {
441 printk(KERN_DEBUG "applesmc: device has"
442 " already been initialized"
443 " (0x%02x, 0x%02x).\n",
444 buffer[0], buffer[1]);
445 } else {
446 printk(KERN_DEBUG "applesmc: device"
447 " successfully initialized"
448 " (0x%02x, 0x%02x).\n",
449 buffer[0], buffer[1]);
450 }
451 ret = 0;
452 goto out;
453 }
454 buffer[0] = 0xe0;
455 buffer[1] = 0x00;
456 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
457 msleep(INIT_WAIT_MSECS);
458 }
459
460 printk(KERN_WARNING "applesmc: failed to init the device\n");
461
462out:
463 mutex_unlock(&applesmc_lock);
464 return ret;
465}
466
467/*
468 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
469 * applesmc_lock.
470 */
471static int applesmc_get_fan_count(void)
472{
473 int ret;
474 u8 buffer[1];
475
476 mutex_lock(&applesmc_lock);
477
478 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
479
480 mutex_unlock(&applesmc_lock);
481 if (ret)
482 return ret;
483 else
484 return buffer[0];
485}
486
487/* Device model stuff */
488static int applesmc_probe(struct platform_device *dev)
489{
490 int ret;
491
492 ret = applesmc_device_init();
493 if (ret)
494 return ret;
495
496 printk(KERN_INFO "applesmc: device successfully initialized.\n");
497 return 0;
498}
499
500static int applesmc_resume(struct platform_device *dev)
501{
502 return applesmc_device_init();
503}
504
505static struct platform_driver applesmc_driver = {
506 .probe = applesmc_probe,
507 .resume = applesmc_resume,
508 .driver = {
509 .name = "applesmc",
510 .owner = THIS_MODULE,
511 },
512};
513
514/*
515 * applesmc_calibrate - Set our "resting" values. Callers must
516 * hold applesmc_lock.
517 */
518static void applesmc_calibrate(void)
519{
520 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
521 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
522 rest_x = -rest_x;
523}
524
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400525static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700526{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400527 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700528 s16 x, y;
529
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400530 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700531
532 if (applesmc_read_motion_sensor(SENSOR_X, &x))
533 goto out;
534 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
535 goto out;
536
537 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400538 input_report_abs(idev, ABS_X, x - rest_x);
539 input_report_abs(idev, ABS_Y, y - rest_y);
540 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700541
542out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700543 mutex_unlock(&applesmc_lock);
544}
545
546/* Sysfs Files */
547
Nicolas Boichatfa744192007-05-23 13:58:13 -0700548static ssize_t applesmc_name_show(struct device *dev,
549 struct device_attribute *attr, char *buf)
550{
551 return snprintf(buf, PAGE_SIZE, "applesmc\n");
552}
553
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700554static ssize_t applesmc_position_show(struct device *dev,
555 struct device_attribute *attr, char *buf)
556{
557 int ret;
558 s16 x, y, z;
559
560 mutex_lock(&applesmc_lock);
561
562 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
563 if (ret)
564 goto out;
565 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
566 if (ret)
567 goto out;
568 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
569 if (ret)
570 goto out;
571
572out:
573 mutex_unlock(&applesmc_lock);
574 if (ret)
575 return ret;
576 else
577 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
578}
579
580static ssize_t applesmc_light_show(struct device *dev,
581 struct device_attribute *attr, char *sysfsbuf)
582{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700583 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700584 int ret;
585 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700586 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700587
588 mutex_lock(&applesmc_lock);
589
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700590 if (!data_length) {
591 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
592 if (ret)
593 goto out;
594 data_length = clamp_val(query[0], 0, 10);
595 printk(KERN_INFO "applesmc: light sensor data length set to "
596 "%d\n", data_length);
597 }
598
599 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800600 /* newer macbooks report a single 10-bit bigendian value */
601 if (data_length == 10) {
602 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
603 goto out;
604 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700605 left = buffer[2];
606 if (ret)
607 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700608 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700609 right = buffer[2];
610
611out:
612 mutex_unlock(&applesmc_lock);
613 if (ret)
614 return ret;
615 else
616 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
617}
618
619/* Displays degree Celsius * 1000 */
620static ssize_t applesmc_show_temperature(struct device *dev,
621 struct device_attribute *devattr, char *sysfsbuf)
622{
623 int ret;
624 u8 buffer[2];
625 unsigned int temp;
626 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
627 const char* key =
628 temperature_sensors_sets[applesmc_temperature_set][attr->index];
629
630 mutex_lock(&applesmc_lock);
631
632 ret = applesmc_read_key(key, buffer, 2);
633 temp = buffer[0]*1000;
634 temp += (buffer[1] >> 6) * 250;
635
636 mutex_unlock(&applesmc_lock);
637
638 if (ret)
639 return ret;
640 else
641 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
642}
643
644static ssize_t applesmc_show_fan_speed(struct device *dev,
645 struct device_attribute *attr, char *sysfsbuf)
646{
647 int ret;
648 unsigned int speed = 0;
649 char newkey[5];
650 u8 buffer[2];
651 struct sensor_device_attribute_2 *sensor_attr =
652 to_sensor_dev_attr_2(attr);
653
654 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
655 newkey[1] = '0' + sensor_attr->index;
656 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
657 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
658 newkey[4] = 0;
659
660 mutex_lock(&applesmc_lock);
661
662 ret = applesmc_read_key(newkey, buffer, 2);
663 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
664
665 mutex_unlock(&applesmc_lock);
666 if (ret)
667 return ret;
668 else
669 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
670}
671
672static ssize_t applesmc_store_fan_speed(struct device *dev,
673 struct device_attribute *attr,
674 const char *sysfsbuf, size_t count)
675{
676 int ret;
677 u32 speed;
678 char newkey[5];
679 u8 buffer[2];
680 struct sensor_device_attribute_2 *sensor_attr =
681 to_sensor_dev_attr_2(attr);
682
683 speed = simple_strtoul(sysfsbuf, NULL, 10);
684
685 if (speed > 0x4000) /* Bigger than a 14-bit value */
686 return -EINVAL;
687
688 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
689 newkey[1] = '0' + sensor_attr->index;
690 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
691 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
692 newkey[4] = 0;
693
694 mutex_lock(&applesmc_lock);
695
696 buffer[0] = (speed >> 6) & 0xff;
697 buffer[1] = (speed << 2) & 0xff;
698 ret = applesmc_write_key(newkey, buffer, 2);
699
700 mutex_unlock(&applesmc_lock);
701 if (ret)
702 return ret;
703 else
704 return count;
705}
706
707static ssize_t applesmc_show_fan_manual(struct device *dev,
708 struct device_attribute *devattr, char *sysfsbuf)
709{
710 int ret;
711 u16 manual = 0;
712 u8 buffer[2];
713 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
714
715 mutex_lock(&applesmc_lock);
716
717 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
718 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
719
720 mutex_unlock(&applesmc_lock);
721 if (ret)
722 return ret;
723 else
724 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
725}
726
727static ssize_t applesmc_store_fan_manual(struct device *dev,
728 struct device_attribute *devattr,
729 const char *sysfsbuf, size_t count)
730{
731 int ret;
732 u8 buffer[2];
733 u32 input;
734 u16 val;
735 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
736
737 input = simple_strtoul(sysfsbuf, NULL, 10);
738
739 mutex_lock(&applesmc_lock);
740
741 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
742 val = (buffer[0] << 8 | buffer[1]);
743 if (ret)
744 goto out;
745
746 if (input)
747 val = val | (0x01 << attr->index);
748 else
749 val = val & ~(0x01 << attr->index);
750
751 buffer[0] = (val >> 8) & 0xFF;
752 buffer[1] = val & 0xFF;
753
754 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
755
756out:
757 mutex_unlock(&applesmc_lock);
758 if (ret)
759 return ret;
760 else
761 return count;
762}
763
764static ssize_t applesmc_show_fan_position(struct device *dev,
765 struct device_attribute *attr, char *sysfsbuf)
766{
767 int ret;
768 char newkey[5];
769 u8 buffer[17];
770 struct sensor_device_attribute_2 *sensor_attr =
771 to_sensor_dev_attr_2(attr);
772
773 newkey[0] = FAN_POSITION[0];
774 newkey[1] = '0' + sensor_attr->index;
775 newkey[2] = FAN_POSITION[2];
776 newkey[3] = FAN_POSITION[3];
777 newkey[4] = 0;
778
779 mutex_lock(&applesmc_lock);
780
781 ret = applesmc_read_key(newkey, buffer, 16);
782 buffer[16] = 0;
783
784 mutex_unlock(&applesmc_lock);
785 if (ret)
786 return ret;
787 else
788 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
789}
790
791static ssize_t applesmc_calibrate_show(struct device *dev,
792 struct device_attribute *attr, char *sysfsbuf)
793{
794 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
795}
796
797static ssize_t applesmc_calibrate_store(struct device *dev,
798 struct device_attribute *attr, const char *sysfsbuf, size_t count)
799{
800 mutex_lock(&applesmc_lock);
801 applesmc_calibrate();
802 mutex_unlock(&applesmc_lock);
803
804 return count;
805}
806
807/* Store the next backlight value to be written by the work */
808static unsigned int backlight_value;
809
810static void applesmc_backlight_set(struct work_struct *work)
811{
812 u8 buffer[2];
813
814 mutex_lock(&applesmc_lock);
815 buffer[0] = backlight_value;
816 buffer[1] = 0x00;
817 applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
818 mutex_unlock(&applesmc_lock);
819}
820static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
821
822static void applesmc_brightness_set(struct led_classdev *led_cdev,
823 enum led_brightness value)
824{
825 int ret;
826
827 backlight_value = value;
828 ret = queue_work(applesmc_led_wq, &backlight_work);
829
830 if (debug && (!ret))
831 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
832}
833
834static ssize_t applesmc_key_count_show(struct device *dev,
835 struct device_attribute *attr, char *sysfsbuf)
836{
837 int ret;
838 u8 buffer[4];
839 u32 count;
840
841 mutex_lock(&applesmc_lock);
842
843 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
844 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
845 ((u32)buffer[2]<<8) + buffer[3];
846
847 mutex_unlock(&applesmc_lock);
848 if (ret)
849 return ret;
850 else
851 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
852}
853
854static ssize_t applesmc_key_at_index_read_show(struct device *dev,
855 struct device_attribute *attr, char *sysfsbuf)
856{
857 char key[5];
858 char info[6];
859 int ret;
860
861 mutex_lock(&applesmc_lock);
862
863 ret = applesmc_get_key_at_index(key_at_index, key);
864
865 if (ret || !key[0]) {
866 mutex_unlock(&applesmc_lock);
867
868 return -EINVAL;
869 }
870
871 ret = applesmc_get_key_type(key, info);
872
873 if (ret) {
874 mutex_unlock(&applesmc_lock);
875
876 return ret;
877 }
878
879 /*
880 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
881 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
882 */
883 ret = applesmc_read_key(key, sysfsbuf, info[0]);
884
885 mutex_unlock(&applesmc_lock);
886
887 if (!ret) {
888 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400889 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700890 return ret;
891 }
892}
893
894static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
895 struct device_attribute *attr, char *sysfsbuf)
896{
897 char key[5];
898 char info[6];
899 int ret;
900
901 mutex_lock(&applesmc_lock);
902
903 ret = applesmc_get_key_at_index(key_at_index, key);
904
905 if (ret || !key[0]) {
906 mutex_unlock(&applesmc_lock);
907
908 return -EINVAL;
909 }
910
911 ret = applesmc_get_key_type(key, info);
912
913 mutex_unlock(&applesmc_lock);
914
915 if (!ret)
916 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
917 else
918 return ret;
919}
920
921static ssize_t applesmc_key_at_index_type_show(struct device *dev,
922 struct device_attribute *attr, char *sysfsbuf)
923{
924 char key[5];
925 char info[6];
926 int ret;
927
928 mutex_lock(&applesmc_lock);
929
930 ret = applesmc_get_key_at_index(key_at_index, key);
931
932 if (ret || !key[0]) {
933 mutex_unlock(&applesmc_lock);
934
935 return -EINVAL;
936 }
937
938 ret = applesmc_get_key_type(key, info);
939
940 mutex_unlock(&applesmc_lock);
941
942 if (!ret)
943 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
944 else
945 return ret;
946}
947
948static ssize_t applesmc_key_at_index_name_show(struct device *dev,
949 struct device_attribute *attr, char *sysfsbuf)
950{
951 char key[5];
952 int ret;
953
954 mutex_lock(&applesmc_lock);
955
956 ret = applesmc_get_key_at_index(key_at_index, key);
957
958 mutex_unlock(&applesmc_lock);
959
960 if (!ret && key[0])
961 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
962 else
963 return -EINVAL;
964}
965
966static ssize_t applesmc_key_at_index_show(struct device *dev,
967 struct device_attribute *attr, char *sysfsbuf)
968{
969 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
970}
971
972static ssize_t applesmc_key_at_index_store(struct device *dev,
973 struct device_attribute *attr, const char *sysfsbuf, size_t count)
974{
975 mutex_lock(&applesmc_lock);
976
977 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
978
979 mutex_unlock(&applesmc_lock);
980
981 return count;
982}
983
984static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +0100985 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700986 .default_trigger = "nand-disk",
987 .brightness_set = applesmc_brightness_set,
988};
989
Nicolas Boichatfa744192007-05-23 13:58:13 -0700990static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
991
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700992static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
993static DEVICE_ATTR(calibrate, 0644,
994 applesmc_calibrate_show, applesmc_calibrate_store);
995
996static struct attribute *accelerometer_attributes[] = {
997 &dev_attr_position.attr,
998 &dev_attr_calibrate.attr,
999 NULL
1000};
1001
1002static const struct attribute_group accelerometer_attributes_group =
1003 { .attrs = accelerometer_attributes };
1004
1005static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1006
1007static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1008static DEVICE_ATTR(key_at_index, 0644,
1009 applesmc_key_at_index_show, applesmc_key_at_index_store);
1010static DEVICE_ATTR(key_at_index_name, 0444,
1011 applesmc_key_at_index_name_show, NULL);
1012static DEVICE_ATTR(key_at_index_type, 0444,
1013 applesmc_key_at_index_type_show, NULL);
1014static DEVICE_ATTR(key_at_index_data_length, 0444,
1015 applesmc_key_at_index_data_length_show, NULL);
1016static DEVICE_ATTR(key_at_index_data, 0444,
1017 applesmc_key_at_index_read_show, NULL);
1018
1019static struct attribute *key_enumeration_attributes[] = {
1020 &dev_attr_key_count.attr,
1021 &dev_attr_key_at_index.attr,
1022 &dev_attr_key_at_index_name.attr,
1023 &dev_attr_key_at_index_type.attr,
1024 &dev_attr_key_at_index_data_length.attr,
1025 &dev_attr_key_at_index_data.attr,
1026 NULL
1027};
1028
1029static const struct attribute_group key_enumeration_group =
1030 { .attrs = key_enumeration_attributes };
1031
1032/*
1033 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1034 * - show actual speed
1035 * - show/store minimum speed
1036 * - show maximum speed
1037 * - show safe speed
1038 * - show/store target speed
1039 * - show/store manual mode
1040 */
1041#define sysfs_fan_speeds_offset(offset) \
1042static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1043 applesmc_show_fan_speed, NULL, 0, offset-1); \
1044\
1045static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1046 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1047\
1048static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1049 applesmc_show_fan_speed, NULL, 2, offset-1); \
1050\
1051static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1052 applesmc_show_fan_speed, NULL, 3, offset-1); \
1053\
1054static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1055 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1056\
1057static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1058 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1059\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001060static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001061 applesmc_show_fan_position, NULL, offset-1); \
1062\
1063static struct attribute *fan##offset##_attributes[] = { \
1064 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1065 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1066 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1067 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1068 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1069 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001070 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001071 NULL \
1072};
1073
1074/*
1075 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001076 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001077 */
1078sysfs_fan_speeds_offset(1);
1079sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001080sysfs_fan_speeds_offset(3);
1081sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001082
1083static const struct attribute_group fan_attribute_groups[] = {
1084 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001085 { .attrs = fan2_attributes },
1086 { .attrs = fan3_attributes },
1087 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001088};
1089
1090/*
1091 * Temperature sensors sysfs entries.
1092 */
1093static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1094 applesmc_show_temperature, NULL, 0);
1095static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1096 applesmc_show_temperature, NULL, 1);
1097static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1098 applesmc_show_temperature, NULL, 2);
1099static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1100 applesmc_show_temperature, NULL, 3);
1101static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1102 applesmc_show_temperature, NULL, 4);
1103static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1104 applesmc_show_temperature, NULL, 5);
1105static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1106 applesmc_show_temperature, NULL, 6);
1107static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1108 applesmc_show_temperature, NULL, 7);
1109static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1110 applesmc_show_temperature, NULL, 8);
1111static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1112 applesmc_show_temperature, NULL, 9);
1113static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1114 applesmc_show_temperature, NULL, 10);
1115static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1116 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001117static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1118 applesmc_show_temperature, NULL, 12);
1119static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1120 applesmc_show_temperature, NULL, 13);
1121static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1122 applesmc_show_temperature, NULL, 14);
1123static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1124 applesmc_show_temperature, NULL, 15);
1125static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1126 applesmc_show_temperature, NULL, 16);
1127static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1128 applesmc_show_temperature, NULL, 17);
1129static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1130 applesmc_show_temperature, NULL, 18);
1131static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1132 applesmc_show_temperature, NULL, 19);
1133static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1134 applesmc_show_temperature, NULL, 20);
1135static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1136 applesmc_show_temperature, NULL, 21);
1137static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1138 applesmc_show_temperature, NULL, 22);
1139static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1140 applesmc_show_temperature, NULL, 23);
1141static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1142 applesmc_show_temperature, NULL, 24);
1143static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1144 applesmc_show_temperature, NULL, 25);
1145static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1146 applesmc_show_temperature, NULL, 26);
1147static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1148 applesmc_show_temperature, NULL, 27);
1149static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1150 applesmc_show_temperature, NULL, 28);
1151static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1152 applesmc_show_temperature, NULL, 29);
1153static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1154 applesmc_show_temperature, NULL, 30);
1155static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1156 applesmc_show_temperature, NULL, 31);
1157static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1158 applesmc_show_temperature, NULL, 32);
1159static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1160 applesmc_show_temperature, NULL, 33);
1161static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1162 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001163static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1164 applesmc_show_temperature, NULL, 35);
1165static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1166 applesmc_show_temperature, NULL, 36);
1167static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1168 applesmc_show_temperature, NULL, 37);
1169static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1170 applesmc_show_temperature, NULL, 38);
1171static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1172 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001173
1174static struct attribute *temperature_attributes[] = {
1175 &sensor_dev_attr_temp1_input.dev_attr.attr,
1176 &sensor_dev_attr_temp2_input.dev_attr.attr,
1177 &sensor_dev_attr_temp3_input.dev_attr.attr,
1178 &sensor_dev_attr_temp4_input.dev_attr.attr,
1179 &sensor_dev_attr_temp5_input.dev_attr.attr,
1180 &sensor_dev_attr_temp6_input.dev_attr.attr,
1181 &sensor_dev_attr_temp7_input.dev_attr.attr,
1182 &sensor_dev_attr_temp8_input.dev_attr.attr,
1183 &sensor_dev_attr_temp9_input.dev_attr.attr,
1184 &sensor_dev_attr_temp10_input.dev_attr.attr,
1185 &sensor_dev_attr_temp11_input.dev_attr.attr,
1186 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001187 &sensor_dev_attr_temp13_input.dev_attr.attr,
1188 &sensor_dev_attr_temp14_input.dev_attr.attr,
1189 &sensor_dev_attr_temp15_input.dev_attr.attr,
1190 &sensor_dev_attr_temp16_input.dev_attr.attr,
1191 &sensor_dev_attr_temp17_input.dev_attr.attr,
1192 &sensor_dev_attr_temp18_input.dev_attr.attr,
1193 &sensor_dev_attr_temp19_input.dev_attr.attr,
1194 &sensor_dev_attr_temp20_input.dev_attr.attr,
1195 &sensor_dev_attr_temp21_input.dev_attr.attr,
1196 &sensor_dev_attr_temp22_input.dev_attr.attr,
1197 &sensor_dev_attr_temp23_input.dev_attr.attr,
1198 &sensor_dev_attr_temp24_input.dev_attr.attr,
1199 &sensor_dev_attr_temp25_input.dev_attr.attr,
1200 &sensor_dev_attr_temp26_input.dev_attr.attr,
1201 &sensor_dev_attr_temp27_input.dev_attr.attr,
1202 &sensor_dev_attr_temp28_input.dev_attr.attr,
1203 &sensor_dev_attr_temp29_input.dev_attr.attr,
1204 &sensor_dev_attr_temp30_input.dev_attr.attr,
1205 &sensor_dev_attr_temp31_input.dev_attr.attr,
1206 &sensor_dev_attr_temp32_input.dev_attr.attr,
1207 &sensor_dev_attr_temp33_input.dev_attr.attr,
1208 &sensor_dev_attr_temp34_input.dev_attr.attr,
1209 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001210 &sensor_dev_attr_temp36_input.dev_attr.attr,
1211 &sensor_dev_attr_temp37_input.dev_attr.attr,
1212 &sensor_dev_attr_temp38_input.dev_attr.attr,
1213 &sensor_dev_attr_temp39_input.dev_attr.attr,
1214 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001215 NULL
1216};
1217
1218static const struct attribute_group temperature_attributes_group =
1219 { .attrs = temperature_attributes };
1220
1221/* Module stuff */
1222
1223/*
1224 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1225 */
Jeff Garzik18552562007-10-03 15:15:40 -04001226static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001227{
1228 int i = 0;
1229 struct dmi_match_data* dmi_data = id->driver_data;
1230 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1231 applesmc_accelerometer = dmi_data->accelerometer;
1232 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1233 applesmc_accelerometer ? "with" : "without");
1234 applesmc_light = dmi_data->light;
1235 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1236 applesmc_light ? "with" : "without");
1237
1238 applesmc_temperature_set = dmi_data->temperature_set;
1239 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1240 i++;
1241 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1242 return 1;
1243}
1244
1245/* Create accelerometer ressources */
1246static int applesmc_create_accelerometer(void)
1247{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001248 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001249 int ret;
1250
1251 ret = sysfs_create_group(&pdev->dev.kobj,
1252 &accelerometer_attributes_group);
1253 if (ret)
1254 goto out;
1255
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001256 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001257 if (!applesmc_idev) {
1258 ret = -ENOMEM;
1259 goto out_sysfs;
1260 }
1261
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001262 applesmc_idev->poll = applesmc_idev_poll;
1263 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1264
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001265 /* initial calibrate for the input device */
1266 applesmc_calibrate();
1267
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001268 /* initialize the input device */
1269 idev = applesmc_idev->input;
1270 idev->name = "applesmc";
1271 idev->id.bustype = BUS_HOST;
1272 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001273 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001274 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001275 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001276 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001277 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1278
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001279 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001280 if (ret)
1281 goto out_idev;
1282
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001283 return 0;
1284
1285out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001286 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001287
1288out_sysfs:
1289 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1290
1291out:
1292 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1293 return ret;
1294}
1295
1296/* Release all ressources used by the accelerometer */
1297static void applesmc_release_accelerometer(void)
1298{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001299 input_unregister_polled_device(applesmc_idev);
1300 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001301 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1302}
1303
1304static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1305/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1306 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001307/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001308 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001309/* MacBook: accelerometer and temperature set 2 */
1310 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1311/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001312 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001313/* MacPro: temperature set 4 */
1314 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001315/* iMac: temperature set 5 */
1316 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001317/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001318 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001319/* MacBook Air: accelerometer, backlight and temperature set 7 */
1320 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001321/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1322 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001323/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1324 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001325/* iMac 5: light sensor only, temperature set 10 */
1326 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a12008-11-06 12:53:20 -08001327/* MacBook 5: accelerometer, backlight and temperature set 11 */
1328 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001329/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1330 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001331/* iMac 8: light sensor only, temperature set 13 */
1332 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001333/* iMac 6: light sensor only, temperature set 14 */
1334 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001335/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1336 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001337/* MacPro3,1: temperature set 16 */
1338 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001339};
1340
1341/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1342 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1343static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001344 { applesmc_dmi_match, "Apple MacBook Air 2", {
1345 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1346 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1347 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001348 { applesmc_dmi_match, "Apple MacBook Air", {
1349 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1350 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001351 &applesmc_dmi_data[7]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001352 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1353 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1354 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1355 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001356 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1357 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1358 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1359 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001360 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1361 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1362 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1363 &applesmc_dmi_data[9]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001364 { applesmc_dmi_match, "Apple MacBook Pro", {
1365 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1366 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001367 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001368 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001369 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001370 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001371 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001372 { applesmc_dmi_match, "Apple MacBook (v3)", {
1373 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1374 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001375 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001376 { applesmc_dmi_match, "Apple MacBook 4", {
1377 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1378 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1379 &applesmc_dmi_data[6]},
Henrik Rydberg181209a12008-11-06 12:53:20 -08001380 { applesmc_dmi_match, "Apple MacBook 5", {
1381 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1382 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1383 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001384 { applesmc_dmi_match, "Apple MacBook", {
1385 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1386 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001387 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001388 { applesmc_dmi_match, "Apple Macmini", {
1389 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1390 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001391 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001392 { applesmc_dmi_match, "Apple MacPro2", {
1393 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1394 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001395 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001396 { applesmc_dmi_match, "Apple MacPro3", {
1397 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1398 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1399 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001400 { applesmc_dmi_match, "Apple MacPro", {
1401 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1402 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1403 &applesmc_dmi_data[4]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001404 { applesmc_dmi_match, "Apple iMac 8", {
1405 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1406 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1407 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001408 { applesmc_dmi_match, "Apple iMac 6", {
1409 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1410 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1411 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001412 { applesmc_dmi_match, "Apple iMac 5", {
1413 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1414 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1415 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001416 { applesmc_dmi_match, "Apple iMac", {
1417 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1418 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001419 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001420 { .ident = NULL }
1421};
1422
1423static int __init applesmc_init(void)
1424{
1425 int ret;
1426 int count;
1427 int i;
1428
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001429 if (!dmi_check_system(applesmc_whitelist)) {
1430 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1431 ret = -ENODEV;
1432 goto out;
1433 }
1434
1435 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1436 "applesmc")) {
1437 ret = -ENXIO;
1438 goto out;
1439 }
1440
1441 ret = platform_driver_register(&applesmc_driver);
1442 if (ret)
1443 goto out_region;
1444
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001445 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1446 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001447 if (IS_ERR(pdev)) {
1448 ret = PTR_ERR(pdev);
1449 goto out_driver;
1450 }
1451
Nicolas Boichatfa744192007-05-23 13:58:13 -07001452 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001453 if (ret)
1454 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001455
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001456 /* Create key enumeration sysfs files */
1457 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1458 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001459 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001460
1461 /* create fan files */
1462 count = applesmc_get_fan_count();
1463 if (count < 0) {
1464 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
1465 } else {
1466 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1467
1468 switch (count) {
1469 default:
René Rebe8de57702007-10-16 14:19:20 -07001470 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1471 " but at most 4 fans are supported"
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001472 " by the driver.\n");
René Rebe8de57702007-10-16 14:19:20 -07001473 case 4:
1474 ret = sysfs_create_group(&pdev->dev.kobj,
1475 &fan_attribute_groups[3]);
1476 if (ret)
1477 goto out_key_enumeration;
1478 case 3:
1479 ret = sysfs_create_group(&pdev->dev.kobj,
1480 &fan_attribute_groups[2]);
1481 if (ret)
1482 goto out_key_enumeration;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001483 case 2:
1484 ret = sysfs_create_group(&pdev->dev.kobj,
1485 &fan_attribute_groups[1]);
1486 if (ret)
1487 goto out_key_enumeration;
1488 case 1:
1489 ret = sysfs_create_group(&pdev->dev.kobj,
1490 &fan_attribute_groups[0]);
1491 if (ret)
1492 goto out_fan_1;
1493 case 0:
1494 ;
1495 }
1496 }
1497
1498 for (i = 0;
1499 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1500 i++) {
1501 if (temperature_attributes[i] == NULL) {
1502 printk(KERN_ERR "applesmc: More temperature sensors "
1503 "in temperature_sensors_sets (at least %i)"
1504 "than available sysfs files in "
1505 "temperature_attributes (%i), please report "
1506 "this bug.\n", i, i-1);
1507 goto out_temperature;
1508 }
1509 ret = sysfs_create_file(&pdev->dev.kobj,
1510 temperature_attributes[i]);
1511 if (ret)
1512 goto out_temperature;
1513 }
1514
1515 if (applesmc_accelerometer) {
1516 ret = applesmc_create_accelerometer();
1517 if (ret)
1518 goto out_temperature;
1519 }
1520
1521 if (applesmc_light) {
1522 /* Add light sensor file */
1523 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1524 if (ret)
1525 goto out_accelerometer;
1526
1527 /* Create the workqueue */
1528 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1529 if (!applesmc_led_wq) {
1530 ret = -ENOMEM;
1531 goto out_light_sysfs;
1532 }
1533
1534 /* register as a led device */
1535 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1536 if (ret < 0)
1537 goto out_light_wq;
1538 }
1539
Tony Jones1beeffe2007-08-20 13:46:20 -07001540 hwmon_dev = hwmon_device_register(&pdev->dev);
1541 if (IS_ERR(hwmon_dev)) {
1542 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001543 goto out_light_ledclass;
1544 }
1545
1546 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1547
1548 return 0;
1549
1550out_light_ledclass:
1551 if (applesmc_light)
1552 led_classdev_unregister(&applesmc_backlight);
1553out_light_wq:
1554 if (applesmc_light)
1555 destroy_workqueue(applesmc_led_wq);
1556out_light_sysfs:
1557 if (applesmc_light)
1558 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1559out_accelerometer:
1560 if (applesmc_accelerometer)
1561 applesmc_release_accelerometer();
1562out_temperature:
1563 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
1564 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
1565out_fan_1:
1566 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
1567out_key_enumeration:
1568 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001569out_name:
1570 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001571out_device:
1572 platform_device_unregister(pdev);
1573out_driver:
1574 platform_driver_unregister(&applesmc_driver);
1575out_region:
1576 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1577out:
1578 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1579 return ret;
1580}
1581
1582static void __exit applesmc_exit(void)
1583{
Tony Jones1beeffe2007-08-20 13:46:20 -07001584 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001585 if (applesmc_light) {
1586 led_classdev_unregister(&applesmc_backlight);
1587 destroy_workqueue(applesmc_led_wq);
1588 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1589 }
1590 if (applesmc_accelerometer)
1591 applesmc_release_accelerometer();
1592 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
1593 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
1594 sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
1595 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001596 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001597 platform_device_unregister(pdev);
1598 platform_driver_unregister(&applesmc_driver);
1599 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1600
1601 printk(KERN_INFO "applesmc: driver unloaded.\n");
1602}
1603
1604module_init(applesmc_init);
1605module_exit(applesmc_exit);
1606
1607MODULE_AUTHOR("Nicolas Boichat");
1608MODULE_DESCRIPTION("Apple SMC");
1609MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001610MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);