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