blob: 57c033169ac27eb332c29ae7b8494d6c4c3260ab [file] [log] [blame]
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001/*
2 * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature
3 * sensors, fan control, keyboard backlight control) used in Intel-based Apple
4 * computers.
5 *
6 * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
7 *
8 * Based on hdaps.c driver:
9 * Copyright (C) 2005 Robert Love <rml@novell.com>
10 * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
11 *
12 * Fan control based on smcFanControl:
13 * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
14 *
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License v2 as published by the
17 * Free Software Foundation.
18 *
19 * This program is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 * more details.
23 *
24 * You should have received a copy of the GNU General Public License along with
25 * this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29#include <linux/delay.h>
30#include <linux/platform_device.h>
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040031#include <linux/input-polldev.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070032#include <linux/kernel.h>
33#include <linux/module.h>
34#include <linux/timer.h>
35#include <linux/dmi.h>
36#include <linux/mutex.h>
37#include <linux/hwmon-sysfs.h>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020038#include <linux/io.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070039#include <linux/leds.h>
40#include <linux/hwmon.h>
41#include <linux/workqueue.h>
42
43/* data port used by Apple SMC */
44#define APPLESMC_DATA_PORT 0x300
45/* command/status port used by Apple SMC */
46#define APPLESMC_CMD_PORT 0x304
47
48#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
49
50#define APPLESMC_MAX_DATA_LENGTH 32
51
Henrik Rydberg8c9398d2008-10-18 20:27:43 -070052#define APPLESMC_MIN_WAIT 0x0040
53#define APPLESMC_MAX_WAIT 0x8000
54
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070055#define APPLESMC_STATUS_MASK 0x0f
56#define APPLESMC_READ_CMD 0x10
57#define APPLESMC_WRITE_CMD 0x11
58#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
59#define APPLESMC_GET_KEY_TYPE_CMD 0x13
60
61#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */
62
Henrik Rydberg8bd1a122008-10-18 20:27:39 -070063#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */
64#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040065#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070066
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040067#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070068
69#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */
70#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */
71#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */
72#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */
73
74#define FANS_COUNT "FNum" /* r-o ui8 */
75#define FANS_MANUAL "FS! " /* r-w ui16 */
76#define FAN_ACTUAL_SPEED "F0Ac" /* r-o fpe2 (2 bytes) */
77#define FAN_MIN_SPEED "F0Mn" /* r-o fpe2 (2 bytes) */
78#define FAN_MAX_SPEED "F0Mx" /* r-o fpe2 (2 bytes) */
79#define FAN_SAFE_SPEED "F0Sf" /* r-o fpe2 (2 bytes) */
80#define FAN_TARGET_SPEED "F0Tg" /* r-w fpe2 (2 bytes) */
81#define FAN_POSITION "F0ID" /* r-o char[16] */
82
83/*
84 * Temperature sensors keys (sp78 - 2 bytes).
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070085 */
Bharath 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 Rydberg181209a2008-11-06 12:53:20 -0800121/* Set 11: Macbook 5,1 */
122 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P",
123 "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL },
Henrik Rydberga6660322008-11-06 12:53:21 -0800124/* Set 12: Macbook Pro 5,1 */
125 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
126 "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0",
127 "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL },
Henrik Rydbergeefc4882008-11-06 12:53:22 -0800128/* Set 13: iMac 8,1 */
129 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
130 "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -0800131/* Set 14: iMac 6,1 */
132 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
133 "TO0P", "Tp0P", NULL },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -0800134/* Set 15: MacBook Air 2,1 */
135 { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0",
136 "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P",
137 "Ts0S", NULL },
Bharath 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 },
Justin P. Mattocke1741712010-04-14 16:14:10 +0200145/* Set 17: iMac 9,1 */
146 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TH0P", "TL0P",
147 "TN0D", "TN0H", "TN0P", "TO0P", "Tm0P", "Tp0P", NULL },
148/* Set 18: MacBook Pro 2,2 */
149 { "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "TM0P", "TTF0",
150 "Th0H", "Th1H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +0200151/* Set 19: Macbook Pro 5,3 */
152 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
153 "TG0F", "TG0H", "TG0P", "TG0T", "TN0D", "TN0P", "TTF0", "Th2H",
154 "Tm0P", "Ts0P", "Ts0S", NULL },
155/* Set 20: MacBook Pro 5,4 */
156 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TN0D",
157 "TN0P", "TTF0", "Th2H", "Ts0P", "Ts0S", NULL },
Bernhard Froemel872bad52010-05-27 19:58:52 +0200158/* Set 21: MacBook Pro 6,2 */
159 { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", "TC0P", "TC1C", "TG0D",
160 "TG0P", "TG0T", "TMCD", "TP0P", "TPCD", "Th1H", "Th2H", "Tm0P",
161 "Ts0P", "Ts0S", NULL },
Henrik Rydberg405eaa12010-05-27 19:58:53 +0200162/* Set 22: MacBook Pro 7,1 */
163 { "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", "TN0D", "TN0P", "TN0S",
164 "TN1D", "TN1F", "TN1G", "TN1S", "Th1H", "Ts0P", "Ts0S", NULL },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700165};
166
167/* List of keys used to read/write fan speeds */
168static const char* fan_speed_keys[] = {
169 FAN_ACTUAL_SPEED,
170 FAN_MIN_SPEED,
171 FAN_MAX_SPEED,
172 FAN_SAFE_SPEED,
173 FAN_TARGET_SPEED
174};
175
176#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
177#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
178
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400179#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700180#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
181#define APPLESMC_INPUT_FLAT 4
182
183#define SENSOR_X 0
184#define SENSOR_Y 1
185#define SENSOR_Z 2
186
187/* Structure to be passed to DMI_MATCH function */
188struct dmi_match_data {
189/* Indicates whether this computer has an accelerometer. */
190 int accelerometer;
191/* Indicates whether this computer has light sensors and keyboard backlight. */
192 int light;
193/* Indicates which temperature sensors set to use. */
194 int temperature_set;
195};
196
197static const int debug;
198static struct platform_device *pdev;
199static s16 rest_x;
200static s16 rest_y;
Henrik Rydberga976f152009-09-21 17:04:50 -0700201static u8 backlight_state[2];
202
Tony Jones1beeffe2007-08-20 13:46:20 -0700203static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400204static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700205
206/* Indicates whether this computer has an accelerometer. */
207static unsigned int applesmc_accelerometer;
208
209/* Indicates whether this computer has light sensors and keyboard backlight. */
210static unsigned int applesmc_light;
211
Henrik Rydberg0559a532010-05-11 09:17:47 +0200212/* The number of fans handled by the driver */
213static unsigned int fans_handled;
214
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700215/* Indicates which temperature sensors set to use. */
216static unsigned int applesmc_temperature_set;
217
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400218static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700219
220/*
221 * Last index written to key_at_index sysfs file, and value to use for all other
222 * key_at_index_* sysfs files.
223 */
224static unsigned int key_at_index;
225
226static struct workqueue_struct *applesmc_led_wq;
227
228/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700229 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700230 * (masked with 0x0f), returning zero if the value is obtained. Callers must
231 * hold applesmc_lock.
232 */
233static int __wait_status(u8 val)
234{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700235 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700236
237 val = val & APPLESMC_STATUS_MASK;
238
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700239 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
240 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700241 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
242 if (debug)
243 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700244 "Waited %d us for status %x\n",
245 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700246 return 0;
247 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700248 }
249
250 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
251 val, inb(APPLESMC_CMD_PORT));
252
253 return -EIO;
254}
255
256/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700257 * special treatment of command port - on newer macbooks, it seems necessary
258 * to resend the command byte before polling the status again. Callers must
259 * hold applesmc_lock.
260 */
261static int send_command(u8 cmd)
262{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700263 int us;
264 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700265 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700266 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700267 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
268 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700269 }
270 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
271 cmd, inb(APPLESMC_CMD_PORT));
272 return -EIO;
273}
274
275/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700276 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
277 * Returns zero on success or a negative error on failure. Callers must
278 * hold applesmc_lock.
279 */
280static int applesmc_read_key(const char* key, u8* buffer, u8 len)
281{
282 int i;
283
284 if (len > APPLESMC_MAX_DATA_LENGTH) {
285 printk(KERN_ERR "applesmc_read_key: cannot read more than "
286 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
287 return -EINVAL;
288 }
289
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700290 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700291 return -EIO;
292
293 for (i = 0; i < 4; i++) {
294 outb(key[i], APPLESMC_DATA_PORT);
295 if (__wait_status(0x04))
296 return -EIO;
297 }
298 if (debug)
299 printk(KERN_DEBUG "<%s", key);
300
301 outb(len, APPLESMC_DATA_PORT);
302 if (debug)
303 printk(KERN_DEBUG ">%x", len);
304
305 for (i = 0; i < len; i++) {
306 if (__wait_status(0x05))
307 return -EIO;
308 buffer[i] = inb(APPLESMC_DATA_PORT);
309 if (debug)
310 printk(KERN_DEBUG "<%x", buffer[i]);
311 }
312 if (debug)
313 printk(KERN_DEBUG "\n");
314
315 return 0;
316}
317
318/*
319 * applesmc_write_key - writes len bytes from buffer to a given key.
320 * Returns zero on success or a negative error on failure. Callers must
321 * hold applesmc_lock.
322 */
323static int applesmc_write_key(const char* key, u8* buffer, u8 len)
324{
325 int i;
326
327 if (len > APPLESMC_MAX_DATA_LENGTH) {
328 printk(KERN_ERR "applesmc_write_key: cannot write more than "
329 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
330 return -EINVAL;
331 }
332
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700333 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700334 return -EIO;
335
336 for (i = 0; i < 4; i++) {
337 outb(key[i], APPLESMC_DATA_PORT);
338 if (__wait_status(0x04))
339 return -EIO;
340 }
341
342 outb(len, APPLESMC_DATA_PORT);
343
344 for (i = 0; i < len; i++) {
345 if (__wait_status(0x04))
346 return -EIO;
347 outb(buffer[i], APPLESMC_DATA_PORT);
348 }
349
350 return 0;
351}
352
353/*
354 * applesmc_get_key_at_index - get key at index, and put the result in key
355 * (char[6]). Returns zero on success or a negative error on failure. Callers
356 * must hold applesmc_lock.
357 */
358static int applesmc_get_key_at_index(int index, char* key)
359{
360 int i;
361 u8 readkey[4];
362 readkey[0] = index >> 24;
363 readkey[1] = index >> 16;
364 readkey[2] = index >> 8;
365 readkey[3] = index;
366
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700367 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700368 return -EIO;
369
370 for (i = 0; i < 4; i++) {
371 outb(readkey[i], APPLESMC_DATA_PORT);
372 if (__wait_status(0x04))
373 return -EIO;
374 }
375
376 outb(4, APPLESMC_DATA_PORT);
377
378 for (i = 0; i < 4; i++) {
379 if (__wait_status(0x05))
380 return -EIO;
381 key[i] = inb(APPLESMC_DATA_PORT);
382 }
383 key[4] = 0;
384
385 return 0;
386}
387
388/*
389 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
390 * Returns zero on success or a negative error on failure. Callers must
391 * hold applesmc_lock.
392 */
393static int applesmc_get_key_type(char* key, char* type)
394{
395 int i;
396
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700397 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700398 return -EIO;
399
400 for (i = 0; i < 4; i++) {
401 outb(key[i], APPLESMC_DATA_PORT);
402 if (__wait_status(0x04))
403 return -EIO;
404 }
405
Henrik Rydberg05224092008-10-18 20:27:35 -0700406 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700407
408 for (i = 0; i < 6; i++) {
409 if (__wait_status(0x05))
410 return -EIO;
411 type[i] = inb(APPLESMC_DATA_PORT);
412 }
413 type[5] = 0;
414
415 return 0;
416}
417
418/*
419 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
420 * hold applesmc_lock.
421 */
422static int applesmc_read_motion_sensor(int index, s16* value)
423{
424 u8 buffer[2];
425 int ret;
426
427 switch (index) {
428 case SENSOR_X:
429 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
430 break;
431 case SENSOR_Y:
432 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
433 break;
434 case SENSOR_Z:
435 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
436 break;
437 default:
438 ret = -EINVAL;
439 }
440
441 *value = ((s16)buffer[0] << 8) | buffer[1];
442
443 return ret;
444}
445
446/*
447 * applesmc_device_init - initialize the accelerometer. Returns zero on success
448 * and negative error code on failure. Can sleep.
449 */
450static int applesmc_device_init(void)
451{
452 int total, ret = -ENXIO;
453 u8 buffer[2];
454
455 if (!applesmc_accelerometer)
456 return 0;
457
458 mutex_lock(&applesmc_lock);
459
460 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
461 if (debug)
462 printk(KERN_DEBUG "applesmc try %d\n", total);
463 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
464 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
465 if (total == INIT_TIMEOUT_MSECS) {
466 printk(KERN_DEBUG "applesmc: device has"
467 " already been initialized"
468 " (0x%02x, 0x%02x).\n",
469 buffer[0], buffer[1]);
470 } else {
471 printk(KERN_DEBUG "applesmc: device"
472 " successfully initialized"
473 " (0x%02x, 0x%02x).\n",
474 buffer[0], buffer[1]);
475 }
476 ret = 0;
477 goto out;
478 }
479 buffer[0] = 0xe0;
480 buffer[1] = 0x00;
481 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
482 msleep(INIT_WAIT_MSECS);
483 }
484
485 printk(KERN_WARNING "applesmc: failed to init the device\n");
486
487out:
488 mutex_unlock(&applesmc_lock);
489 return ret;
490}
491
492/*
493 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
494 * applesmc_lock.
495 */
496static int applesmc_get_fan_count(void)
497{
498 int ret;
499 u8 buffer[1];
500
501 mutex_lock(&applesmc_lock);
502
503 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
504
505 mutex_unlock(&applesmc_lock);
506 if (ret)
507 return ret;
508 else
509 return buffer[0];
510}
511
512/* Device model stuff */
513static int applesmc_probe(struct platform_device *dev)
514{
515 int ret;
516
517 ret = applesmc_device_init();
518 if (ret)
519 return ret;
520
521 printk(KERN_INFO "applesmc: device successfully initialized.\n");
522 return 0;
523}
524
Henrik Rydberga976f152009-09-21 17:04:50 -0700525/* Synchronize device with memorized backlight state */
526static int applesmc_pm_resume(struct device *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700527{
Henrik Rydberga976f152009-09-21 17:04:50 -0700528 mutex_lock(&applesmc_lock);
529 if (applesmc_light)
530 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
531 mutex_unlock(&applesmc_lock);
532 return 0;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700533}
534
Henrik Rydberga976f152009-09-21 17:04:50 -0700535/* Reinitialize device on resume from hibernation */
536static int applesmc_pm_restore(struct device *dev)
537{
538 int ret = applesmc_device_init();
539 if (ret)
540 return ret;
541 return applesmc_pm_resume(dev);
542}
543
Alexey Dobriyan47145212009-12-14 18:00:08 -0800544static const struct dev_pm_ops applesmc_pm_ops = {
Henrik Rydberga976f152009-09-21 17:04:50 -0700545 .resume = applesmc_pm_resume,
546 .restore = applesmc_pm_restore,
547};
548
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700549static struct platform_driver applesmc_driver = {
550 .probe = applesmc_probe,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700551 .driver = {
552 .name = "applesmc",
553 .owner = THIS_MODULE,
Henrik Rydberga976f152009-09-21 17:04:50 -0700554 .pm = &applesmc_pm_ops,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700555 },
556};
557
558/*
559 * applesmc_calibrate - Set our "resting" values. Callers must
560 * hold applesmc_lock.
561 */
562static void applesmc_calibrate(void)
563{
564 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
565 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
566 rest_x = -rest_x;
567}
568
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400569static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700570{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400571 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700572 s16 x, y;
573
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400574 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700575
576 if (applesmc_read_motion_sensor(SENSOR_X, &x))
577 goto out;
578 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
579 goto out;
580
581 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400582 input_report_abs(idev, ABS_X, x - rest_x);
583 input_report_abs(idev, ABS_Y, y - rest_y);
584 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700585
586out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700587 mutex_unlock(&applesmc_lock);
588}
589
590/* Sysfs Files */
591
Nicolas Boichatfa744192007-05-23 13:58:13 -0700592static ssize_t applesmc_name_show(struct device *dev,
593 struct device_attribute *attr, char *buf)
594{
595 return snprintf(buf, PAGE_SIZE, "applesmc\n");
596}
597
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700598static ssize_t applesmc_position_show(struct device *dev,
599 struct device_attribute *attr, char *buf)
600{
601 int ret;
602 s16 x, y, z;
603
604 mutex_lock(&applesmc_lock);
605
606 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
607 if (ret)
608 goto out;
609 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
610 if (ret)
611 goto out;
612 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
613 if (ret)
614 goto out;
615
616out:
617 mutex_unlock(&applesmc_lock);
618 if (ret)
619 return ret;
620 else
621 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
622}
623
624static ssize_t applesmc_light_show(struct device *dev,
625 struct device_attribute *attr, char *sysfsbuf)
626{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700627 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700628 int ret;
629 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700630 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700631
632 mutex_lock(&applesmc_lock);
633
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700634 if (!data_length) {
635 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
636 if (ret)
637 goto out;
638 data_length = clamp_val(query[0], 0, 10);
639 printk(KERN_INFO "applesmc: light sensor data length set to "
640 "%d\n", data_length);
641 }
642
643 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800644 /* newer macbooks report a single 10-bit bigendian value */
645 if (data_length == 10) {
646 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
647 goto out;
648 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700649 left = buffer[2];
650 if (ret)
651 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700652 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700653 right = buffer[2];
654
655out:
656 mutex_unlock(&applesmc_lock);
657 if (ret)
658 return ret;
659 else
660 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
661}
662
663/* Displays degree Celsius * 1000 */
664static ssize_t applesmc_show_temperature(struct device *dev,
665 struct device_attribute *devattr, char *sysfsbuf)
666{
667 int ret;
668 u8 buffer[2];
669 unsigned int temp;
670 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
671 const char* key =
672 temperature_sensors_sets[applesmc_temperature_set][attr->index];
673
674 mutex_lock(&applesmc_lock);
675
676 ret = applesmc_read_key(key, buffer, 2);
677 temp = buffer[0]*1000;
678 temp += (buffer[1] >> 6) * 250;
679
680 mutex_unlock(&applesmc_lock);
681
682 if (ret)
683 return ret;
684 else
685 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
686}
687
688static ssize_t applesmc_show_fan_speed(struct device *dev,
689 struct device_attribute *attr, char *sysfsbuf)
690{
691 int ret;
692 unsigned int speed = 0;
693 char newkey[5];
694 u8 buffer[2];
695 struct sensor_device_attribute_2 *sensor_attr =
696 to_sensor_dev_attr_2(attr);
697
698 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
699 newkey[1] = '0' + sensor_attr->index;
700 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
701 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
702 newkey[4] = 0;
703
704 mutex_lock(&applesmc_lock);
705
706 ret = applesmc_read_key(newkey, buffer, 2);
707 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
708
709 mutex_unlock(&applesmc_lock);
710 if (ret)
711 return ret;
712 else
713 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
714}
715
716static ssize_t applesmc_store_fan_speed(struct device *dev,
717 struct device_attribute *attr,
718 const char *sysfsbuf, size_t count)
719{
720 int ret;
721 u32 speed;
722 char newkey[5];
723 u8 buffer[2];
724 struct sensor_device_attribute_2 *sensor_attr =
725 to_sensor_dev_attr_2(attr);
726
727 speed = simple_strtoul(sysfsbuf, NULL, 10);
728
729 if (speed > 0x4000) /* Bigger than a 14-bit value */
730 return -EINVAL;
731
732 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
733 newkey[1] = '0' + sensor_attr->index;
734 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
735 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
736 newkey[4] = 0;
737
738 mutex_lock(&applesmc_lock);
739
740 buffer[0] = (speed >> 6) & 0xff;
741 buffer[1] = (speed << 2) & 0xff;
742 ret = applesmc_write_key(newkey, buffer, 2);
743
744 mutex_unlock(&applesmc_lock);
745 if (ret)
746 return ret;
747 else
748 return count;
749}
750
751static ssize_t applesmc_show_fan_manual(struct device *dev,
752 struct device_attribute *devattr, char *sysfsbuf)
753{
754 int ret;
755 u16 manual = 0;
756 u8 buffer[2];
757 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
758
759 mutex_lock(&applesmc_lock);
760
761 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
762 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
763
764 mutex_unlock(&applesmc_lock);
765 if (ret)
766 return ret;
767 else
768 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
769}
770
771static ssize_t applesmc_store_fan_manual(struct device *dev,
772 struct device_attribute *devattr,
773 const char *sysfsbuf, size_t count)
774{
775 int ret;
776 u8 buffer[2];
777 u32 input;
778 u16 val;
779 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
780
781 input = simple_strtoul(sysfsbuf, NULL, 10);
782
783 mutex_lock(&applesmc_lock);
784
785 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
786 val = (buffer[0] << 8 | buffer[1]);
787 if (ret)
788 goto out;
789
790 if (input)
791 val = val | (0x01 << attr->index);
792 else
793 val = val & ~(0x01 << attr->index);
794
795 buffer[0] = (val >> 8) & 0xFF;
796 buffer[1] = val & 0xFF;
797
798 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
799
800out:
801 mutex_unlock(&applesmc_lock);
802 if (ret)
803 return ret;
804 else
805 return count;
806}
807
808static ssize_t applesmc_show_fan_position(struct device *dev,
809 struct device_attribute *attr, char *sysfsbuf)
810{
811 int ret;
812 char newkey[5];
813 u8 buffer[17];
814 struct sensor_device_attribute_2 *sensor_attr =
815 to_sensor_dev_attr_2(attr);
816
817 newkey[0] = FAN_POSITION[0];
818 newkey[1] = '0' + sensor_attr->index;
819 newkey[2] = FAN_POSITION[2];
820 newkey[3] = FAN_POSITION[3];
821 newkey[4] = 0;
822
823 mutex_lock(&applesmc_lock);
824
825 ret = applesmc_read_key(newkey, buffer, 16);
826 buffer[16] = 0;
827
828 mutex_unlock(&applesmc_lock);
829 if (ret)
830 return ret;
831 else
832 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
833}
834
835static ssize_t applesmc_calibrate_show(struct device *dev,
836 struct device_attribute *attr, char *sysfsbuf)
837{
838 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
839}
840
841static ssize_t applesmc_calibrate_store(struct device *dev,
842 struct device_attribute *attr, const char *sysfsbuf, size_t count)
843{
844 mutex_lock(&applesmc_lock);
845 applesmc_calibrate();
846 mutex_unlock(&applesmc_lock);
847
848 return count;
849}
850
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700851static void applesmc_backlight_set(struct work_struct *work)
852{
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700853 mutex_lock(&applesmc_lock);
Henrik Rydberga976f152009-09-21 17:04:50 -0700854 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700855 mutex_unlock(&applesmc_lock);
856}
857static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
858
859static void applesmc_brightness_set(struct led_classdev *led_cdev,
860 enum led_brightness value)
861{
862 int ret;
863
Henrik Rydberga976f152009-09-21 17:04:50 -0700864 backlight_state[0] = value;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700865 ret = queue_work(applesmc_led_wq, &backlight_work);
866
867 if (debug && (!ret))
868 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
869}
870
871static ssize_t applesmc_key_count_show(struct device *dev,
872 struct device_attribute *attr, char *sysfsbuf)
873{
874 int ret;
875 u8 buffer[4];
876 u32 count;
877
878 mutex_lock(&applesmc_lock);
879
880 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
881 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
882 ((u32)buffer[2]<<8) + buffer[3];
883
884 mutex_unlock(&applesmc_lock);
885 if (ret)
886 return ret;
887 else
888 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
889}
890
891static ssize_t applesmc_key_at_index_read_show(struct device *dev,
892 struct device_attribute *attr, char *sysfsbuf)
893{
894 char key[5];
895 char info[6];
896 int ret;
897
898 mutex_lock(&applesmc_lock);
899
900 ret = applesmc_get_key_at_index(key_at_index, key);
901
902 if (ret || !key[0]) {
903 mutex_unlock(&applesmc_lock);
904
905 return -EINVAL;
906 }
907
908 ret = applesmc_get_key_type(key, info);
909
910 if (ret) {
911 mutex_unlock(&applesmc_lock);
912
913 return ret;
914 }
915
916 /*
917 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
918 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
919 */
920 ret = applesmc_read_key(key, sysfsbuf, info[0]);
921
922 mutex_unlock(&applesmc_lock);
923
924 if (!ret) {
925 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400926 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700927 return ret;
928 }
929}
930
931static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
932 struct device_attribute *attr, char *sysfsbuf)
933{
934 char key[5];
935 char info[6];
936 int ret;
937
938 mutex_lock(&applesmc_lock);
939
940 ret = applesmc_get_key_at_index(key_at_index, key);
941
942 if (ret || !key[0]) {
943 mutex_unlock(&applesmc_lock);
944
945 return -EINVAL;
946 }
947
948 ret = applesmc_get_key_type(key, info);
949
950 mutex_unlock(&applesmc_lock);
951
952 if (!ret)
953 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
954 else
955 return ret;
956}
957
958static ssize_t applesmc_key_at_index_type_show(struct device *dev,
959 struct device_attribute *attr, char *sysfsbuf)
960{
961 char key[5];
962 char info[6];
963 int ret;
964
965 mutex_lock(&applesmc_lock);
966
967 ret = applesmc_get_key_at_index(key_at_index, key);
968
969 if (ret || !key[0]) {
970 mutex_unlock(&applesmc_lock);
971
972 return -EINVAL;
973 }
974
975 ret = applesmc_get_key_type(key, info);
976
977 mutex_unlock(&applesmc_lock);
978
979 if (!ret)
980 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
981 else
982 return ret;
983}
984
985static ssize_t applesmc_key_at_index_name_show(struct device *dev,
986 struct device_attribute *attr, char *sysfsbuf)
987{
988 char key[5];
989 int ret;
990
991 mutex_lock(&applesmc_lock);
992
993 ret = applesmc_get_key_at_index(key_at_index, key);
994
995 mutex_unlock(&applesmc_lock);
996
997 if (!ret && key[0])
998 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
999 else
1000 return -EINVAL;
1001}
1002
1003static ssize_t applesmc_key_at_index_show(struct device *dev,
1004 struct device_attribute *attr, char *sysfsbuf)
1005{
1006 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
1007}
1008
1009static ssize_t applesmc_key_at_index_store(struct device *dev,
1010 struct device_attribute *attr, const char *sysfsbuf, size_t count)
1011{
1012 mutex_lock(&applesmc_lock);
1013
1014 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
1015
1016 mutex_unlock(&applesmc_lock);
1017
1018 return count;
1019}
1020
1021static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +01001022 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001023 .default_trigger = "nand-disk",
1024 .brightness_set = applesmc_brightness_set,
1025};
1026
Nicolas Boichatfa744192007-05-23 13:58:13 -07001027static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
1028
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001029static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
1030static DEVICE_ATTR(calibrate, 0644,
1031 applesmc_calibrate_show, applesmc_calibrate_store);
1032
1033static struct attribute *accelerometer_attributes[] = {
1034 &dev_attr_position.attr,
1035 &dev_attr_calibrate.attr,
1036 NULL
1037};
1038
1039static const struct attribute_group accelerometer_attributes_group =
1040 { .attrs = accelerometer_attributes };
1041
1042static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1043
1044static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1045static DEVICE_ATTR(key_at_index, 0644,
1046 applesmc_key_at_index_show, applesmc_key_at_index_store);
1047static DEVICE_ATTR(key_at_index_name, 0444,
1048 applesmc_key_at_index_name_show, NULL);
1049static DEVICE_ATTR(key_at_index_type, 0444,
1050 applesmc_key_at_index_type_show, NULL);
1051static DEVICE_ATTR(key_at_index_data_length, 0444,
1052 applesmc_key_at_index_data_length_show, NULL);
1053static DEVICE_ATTR(key_at_index_data, 0444,
1054 applesmc_key_at_index_read_show, NULL);
1055
1056static struct attribute *key_enumeration_attributes[] = {
1057 &dev_attr_key_count.attr,
1058 &dev_attr_key_at_index.attr,
1059 &dev_attr_key_at_index_name.attr,
1060 &dev_attr_key_at_index_type.attr,
1061 &dev_attr_key_at_index_data_length.attr,
1062 &dev_attr_key_at_index_data.attr,
1063 NULL
1064};
1065
1066static const struct attribute_group key_enumeration_group =
1067 { .attrs = key_enumeration_attributes };
1068
1069/*
1070 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1071 * - show actual speed
1072 * - show/store minimum speed
1073 * - show maximum speed
1074 * - show safe speed
1075 * - show/store target speed
1076 * - show/store manual mode
1077 */
1078#define sysfs_fan_speeds_offset(offset) \
1079static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1080 applesmc_show_fan_speed, NULL, 0, offset-1); \
1081\
1082static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1083 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1084\
1085static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1086 applesmc_show_fan_speed, NULL, 2, offset-1); \
1087\
1088static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1089 applesmc_show_fan_speed, NULL, 3, offset-1); \
1090\
1091static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1092 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1093\
1094static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1095 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1096\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001097static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001098 applesmc_show_fan_position, NULL, offset-1); \
1099\
1100static struct attribute *fan##offset##_attributes[] = { \
1101 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1102 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1103 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1104 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1105 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1106 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001107 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001108 NULL \
1109};
1110
1111/*
1112 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001113 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001114 */
1115sysfs_fan_speeds_offset(1);
1116sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001117sysfs_fan_speeds_offset(3);
1118sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001119
1120static const struct attribute_group fan_attribute_groups[] = {
1121 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001122 { .attrs = fan2_attributes },
1123 { .attrs = fan3_attributes },
1124 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001125};
1126
1127/*
1128 * Temperature sensors sysfs entries.
1129 */
1130static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1131 applesmc_show_temperature, NULL, 0);
1132static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1133 applesmc_show_temperature, NULL, 1);
1134static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1135 applesmc_show_temperature, NULL, 2);
1136static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1137 applesmc_show_temperature, NULL, 3);
1138static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1139 applesmc_show_temperature, NULL, 4);
1140static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1141 applesmc_show_temperature, NULL, 5);
1142static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1143 applesmc_show_temperature, NULL, 6);
1144static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1145 applesmc_show_temperature, NULL, 7);
1146static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1147 applesmc_show_temperature, NULL, 8);
1148static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1149 applesmc_show_temperature, NULL, 9);
1150static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1151 applesmc_show_temperature, NULL, 10);
1152static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1153 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001154static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1155 applesmc_show_temperature, NULL, 12);
1156static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1157 applesmc_show_temperature, NULL, 13);
1158static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1159 applesmc_show_temperature, NULL, 14);
1160static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1161 applesmc_show_temperature, NULL, 15);
1162static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1163 applesmc_show_temperature, NULL, 16);
1164static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1165 applesmc_show_temperature, NULL, 17);
1166static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1167 applesmc_show_temperature, NULL, 18);
1168static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1169 applesmc_show_temperature, NULL, 19);
1170static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1171 applesmc_show_temperature, NULL, 20);
1172static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1173 applesmc_show_temperature, NULL, 21);
1174static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1175 applesmc_show_temperature, NULL, 22);
1176static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1177 applesmc_show_temperature, NULL, 23);
1178static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1179 applesmc_show_temperature, NULL, 24);
1180static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1181 applesmc_show_temperature, NULL, 25);
1182static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1183 applesmc_show_temperature, NULL, 26);
1184static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1185 applesmc_show_temperature, NULL, 27);
1186static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1187 applesmc_show_temperature, NULL, 28);
1188static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1189 applesmc_show_temperature, NULL, 29);
1190static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1191 applesmc_show_temperature, NULL, 30);
1192static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1193 applesmc_show_temperature, NULL, 31);
1194static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1195 applesmc_show_temperature, NULL, 32);
1196static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1197 applesmc_show_temperature, NULL, 33);
1198static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1199 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001200static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1201 applesmc_show_temperature, NULL, 35);
1202static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1203 applesmc_show_temperature, NULL, 36);
1204static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1205 applesmc_show_temperature, NULL, 37);
1206static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1207 applesmc_show_temperature, NULL, 38);
1208static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1209 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001210
1211static struct attribute *temperature_attributes[] = {
1212 &sensor_dev_attr_temp1_input.dev_attr.attr,
1213 &sensor_dev_attr_temp2_input.dev_attr.attr,
1214 &sensor_dev_attr_temp3_input.dev_attr.attr,
1215 &sensor_dev_attr_temp4_input.dev_attr.attr,
1216 &sensor_dev_attr_temp5_input.dev_attr.attr,
1217 &sensor_dev_attr_temp6_input.dev_attr.attr,
1218 &sensor_dev_attr_temp7_input.dev_attr.attr,
1219 &sensor_dev_attr_temp8_input.dev_attr.attr,
1220 &sensor_dev_attr_temp9_input.dev_attr.attr,
1221 &sensor_dev_attr_temp10_input.dev_attr.attr,
1222 &sensor_dev_attr_temp11_input.dev_attr.attr,
1223 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001224 &sensor_dev_attr_temp13_input.dev_attr.attr,
1225 &sensor_dev_attr_temp14_input.dev_attr.attr,
1226 &sensor_dev_attr_temp15_input.dev_attr.attr,
1227 &sensor_dev_attr_temp16_input.dev_attr.attr,
1228 &sensor_dev_attr_temp17_input.dev_attr.attr,
1229 &sensor_dev_attr_temp18_input.dev_attr.attr,
1230 &sensor_dev_attr_temp19_input.dev_attr.attr,
1231 &sensor_dev_attr_temp20_input.dev_attr.attr,
1232 &sensor_dev_attr_temp21_input.dev_attr.attr,
1233 &sensor_dev_attr_temp22_input.dev_attr.attr,
1234 &sensor_dev_attr_temp23_input.dev_attr.attr,
1235 &sensor_dev_attr_temp24_input.dev_attr.attr,
1236 &sensor_dev_attr_temp25_input.dev_attr.attr,
1237 &sensor_dev_attr_temp26_input.dev_attr.attr,
1238 &sensor_dev_attr_temp27_input.dev_attr.attr,
1239 &sensor_dev_attr_temp28_input.dev_attr.attr,
1240 &sensor_dev_attr_temp29_input.dev_attr.attr,
1241 &sensor_dev_attr_temp30_input.dev_attr.attr,
1242 &sensor_dev_attr_temp31_input.dev_attr.attr,
1243 &sensor_dev_attr_temp32_input.dev_attr.attr,
1244 &sensor_dev_attr_temp33_input.dev_attr.attr,
1245 &sensor_dev_attr_temp34_input.dev_attr.attr,
1246 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001247 &sensor_dev_attr_temp36_input.dev_attr.attr,
1248 &sensor_dev_attr_temp37_input.dev_attr.attr,
1249 &sensor_dev_attr_temp38_input.dev_attr.attr,
1250 &sensor_dev_attr_temp39_input.dev_attr.attr,
1251 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001252 NULL
1253};
1254
1255static const struct attribute_group temperature_attributes_group =
1256 { .attrs = temperature_attributes };
1257
1258/* Module stuff */
1259
1260/*
1261 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1262 */
Jeff Garzik18552562007-10-03 15:15:40 -04001263static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001264{
1265 int i = 0;
1266 struct dmi_match_data* dmi_data = id->driver_data;
1267 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1268 applesmc_accelerometer = dmi_data->accelerometer;
1269 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1270 applesmc_accelerometer ? "with" : "without");
1271 applesmc_light = dmi_data->light;
1272 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1273 applesmc_light ? "with" : "without");
1274
1275 applesmc_temperature_set = dmi_data->temperature_set;
1276 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1277 i++;
1278 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1279 return 1;
1280}
1281
1282/* Create accelerometer ressources */
1283static int applesmc_create_accelerometer(void)
1284{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001285 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001286 int ret;
1287
1288 ret = sysfs_create_group(&pdev->dev.kobj,
1289 &accelerometer_attributes_group);
1290 if (ret)
1291 goto out;
1292
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001293 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001294 if (!applesmc_idev) {
1295 ret = -ENOMEM;
1296 goto out_sysfs;
1297 }
1298
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001299 applesmc_idev->poll = applesmc_idev_poll;
1300 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1301
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001302 /* initial calibrate for the input device */
1303 applesmc_calibrate();
1304
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001305 /* initialize the input device */
1306 idev = applesmc_idev->input;
1307 idev->name = "applesmc";
1308 idev->id.bustype = BUS_HOST;
1309 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001310 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001311 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001312 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001313 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001314 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1315
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001316 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001317 if (ret)
1318 goto out_idev;
1319
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001320 return 0;
1321
1322out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001323 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001324
1325out_sysfs:
1326 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1327
1328out:
1329 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1330 return ret;
1331}
1332
1333/* Release all ressources used by the accelerometer */
1334static void applesmc_release_accelerometer(void)
1335{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001336 input_unregister_polled_device(applesmc_idev);
1337 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001338 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1339}
1340
1341static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1342/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1343 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001344/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001345 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001346/* MacBook: accelerometer and temperature set 2 */
1347 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1348/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001349 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001350/* MacPro: temperature set 4 */
1351 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001352/* iMac: temperature set 5 */
1353 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001354/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001355 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001356/* MacBook Air: accelerometer, backlight and temperature set 7 */
1357 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001358/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1359 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001360/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1361 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001362/* iMac 5: light sensor only, temperature set 10 */
1363 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a2008-11-06 12:53:20 -08001364/* MacBook 5: accelerometer, backlight and temperature set 11 */
1365 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001366/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1367 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001368/* iMac 8: light sensor only, temperature set 13 */
1369 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001370/* iMac 6: light sensor only, temperature set 14 */
1371 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001372/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1373 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001374/* MacPro3,1: temperature set 16 */
1375 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Justin P. Mattocke1741712010-04-14 16:14:10 +02001376/* iMac 9,1: light sensor only, temperature set 17 */
1377 { .accelerometer = 0, .light = 0, .temperature_set = 17 },
1378/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
1379 { .accelerometer = 1, .light = 1, .temperature_set = 18 },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001380/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
1381 { .accelerometer = 1, .light = 1, .temperature_set = 19 },
1382/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
1383 { .accelerometer = 1, .light = 1, .temperature_set = 20 },
Bernhard Froemel872bad52010-05-27 19:58:52 +02001384/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
1385 { .accelerometer = 1, .light = 1, .temperature_set = 21 },
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001386/* MacBook Pro 7,1: accelerometer, backlight and temperature set 22 */
1387 { .accelerometer = 1, .light = 1, .temperature_set = 22 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001388};
1389
1390/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1391 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1392static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001393 { applesmc_dmi_match, "Apple MacBook Air 2", {
1394 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1395 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1396 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001397 { applesmc_dmi_match, "Apple MacBook Air", {
1398 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1399 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001400 &applesmc_dmi_data[7]},
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001401 { applesmc_dmi_match, "Apple MacBook Pro 7", {
1402 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1403 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro7") },
1404 &applesmc_dmi_data[22]},
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001405 { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
1406 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1407 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
1408 &applesmc_dmi_data[20]},
1409 { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
1410 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1411 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
1412 &applesmc_dmi_data[19]},
Bernhard Froemel872bad52010-05-27 19:58:52 +02001413 { applesmc_dmi_match, "Apple MacBook Pro 6", {
1414 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1415 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
1416 &applesmc_dmi_data[21]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001417 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1418 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1419 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1420 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001421 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1422 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1423 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1424 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001425 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1426 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1427 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1428 &applesmc_dmi_data[9]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001429 { applesmc_dmi_match, "Apple MacBook Pro 2,2", {
1430 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Computer, Inc."),
1431 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2") },
1432 &applesmc_dmi_data[18]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001433 { applesmc_dmi_match, "Apple MacBook Pro", {
1434 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1435 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001436 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001437 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001438 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001439 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001440 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001441 { applesmc_dmi_match, "Apple MacBook (v3)", {
1442 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1443 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001444 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001445 { applesmc_dmi_match, "Apple MacBook 4", {
1446 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1447 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1448 &applesmc_dmi_data[6]},
Henrik Rydberg181209a2008-11-06 12:53:20 -08001449 { applesmc_dmi_match, "Apple MacBook 5", {
1450 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1451 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1452 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001453 { applesmc_dmi_match, "Apple MacBook", {
1454 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1455 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001456 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001457 { applesmc_dmi_match, "Apple Macmini", {
1458 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1459 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001460 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001461 { applesmc_dmi_match, "Apple MacPro2", {
1462 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1463 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001464 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001465 { applesmc_dmi_match, "Apple MacPro3", {
1466 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1467 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1468 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001469 { applesmc_dmi_match, "Apple MacPro", {
1470 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1471 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1472 &applesmc_dmi_data[4]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001473 { applesmc_dmi_match, "Apple iMac 9,1", {
1474 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
1475 DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1") },
1476 &applesmc_dmi_data[17]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001477 { applesmc_dmi_match, "Apple iMac 8", {
1478 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1479 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1480 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001481 { applesmc_dmi_match, "Apple iMac 6", {
1482 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1483 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1484 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001485 { applesmc_dmi_match, "Apple iMac 5", {
1486 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1487 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1488 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001489 { applesmc_dmi_match, "Apple iMac", {
1490 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1491 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001492 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001493 { .ident = NULL }
1494};
1495
1496static int __init applesmc_init(void)
1497{
1498 int ret;
1499 int count;
1500 int i;
1501
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001502 if (!dmi_check_system(applesmc_whitelist)) {
1503 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1504 ret = -ENODEV;
1505 goto out;
1506 }
1507
1508 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1509 "applesmc")) {
1510 ret = -ENXIO;
1511 goto out;
1512 }
1513
1514 ret = platform_driver_register(&applesmc_driver);
1515 if (ret)
1516 goto out_region;
1517
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001518 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1519 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001520 if (IS_ERR(pdev)) {
1521 ret = PTR_ERR(pdev);
1522 goto out_driver;
1523 }
1524
Nicolas Boichatfa744192007-05-23 13:58:13 -07001525 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001526 if (ret)
1527 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001528
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001529 /* Create key enumeration sysfs files */
1530 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1531 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001532 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001533
1534 /* create fan files */
1535 count = applesmc_get_fan_count();
Henrik Rydberg0559a532010-05-11 09:17:47 +02001536 if (count < 0)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001537 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001538 else
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001539 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1540
Henrik Rydberg0559a532010-05-11 09:17:47 +02001541 if (count > 4) {
1542 count = 4;
1543 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1544 " but at most 4 fans are supported"
1545 " by the driver.\n");
1546 }
1547
1548 while (fans_handled < count) {
1549 ret = sysfs_create_group(&pdev->dev.kobj,
1550 &fan_attribute_groups[fans_handled]);
1551 if (ret)
1552 goto out_fans;
1553 fans_handled++;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001554 }
1555
1556 for (i = 0;
1557 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1558 i++) {
1559 if (temperature_attributes[i] == NULL) {
1560 printk(KERN_ERR "applesmc: More temperature sensors "
1561 "in temperature_sensors_sets (at least %i)"
1562 "than available sysfs files in "
1563 "temperature_attributes (%i), please report "
1564 "this bug.\n", i, i-1);
1565 goto out_temperature;
1566 }
1567 ret = sysfs_create_file(&pdev->dev.kobj,
1568 temperature_attributes[i]);
1569 if (ret)
1570 goto out_temperature;
1571 }
1572
1573 if (applesmc_accelerometer) {
1574 ret = applesmc_create_accelerometer();
1575 if (ret)
1576 goto out_temperature;
1577 }
1578
1579 if (applesmc_light) {
1580 /* Add light sensor file */
1581 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1582 if (ret)
1583 goto out_accelerometer;
1584
1585 /* Create the workqueue */
1586 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1587 if (!applesmc_led_wq) {
1588 ret = -ENOMEM;
1589 goto out_light_sysfs;
1590 }
1591
1592 /* register as a led device */
1593 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1594 if (ret < 0)
1595 goto out_light_wq;
1596 }
1597
Tony Jones1beeffe2007-08-20 13:46:20 -07001598 hwmon_dev = hwmon_device_register(&pdev->dev);
1599 if (IS_ERR(hwmon_dev)) {
1600 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001601 goto out_light_ledclass;
1602 }
1603
1604 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1605
1606 return 0;
1607
1608out_light_ledclass:
1609 if (applesmc_light)
1610 led_classdev_unregister(&applesmc_backlight);
1611out_light_wq:
1612 if (applesmc_light)
1613 destroy_workqueue(applesmc_led_wq);
1614out_light_sysfs:
1615 if (applesmc_light)
1616 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1617out_accelerometer:
1618 if (applesmc_accelerometer)
1619 applesmc_release_accelerometer();
1620out_temperature:
1621 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001622out_fans:
1623 while (fans_handled)
1624 sysfs_remove_group(&pdev->dev.kobj,
1625 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001626 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001627out_name:
1628 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001629out_device:
1630 platform_device_unregister(pdev);
1631out_driver:
1632 platform_driver_unregister(&applesmc_driver);
1633out_region:
1634 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1635out:
1636 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1637 return ret;
1638}
1639
1640static void __exit applesmc_exit(void)
1641{
Tony Jones1beeffe2007-08-20 13:46:20 -07001642 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001643 if (applesmc_light) {
1644 led_classdev_unregister(&applesmc_backlight);
1645 destroy_workqueue(applesmc_led_wq);
1646 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1647 }
1648 if (applesmc_accelerometer)
1649 applesmc_release_accelerometer();
1650 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001651 while (fans_handled)
1652 sysfs_remove_group(&pdev->dev.kobj,
1653 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001654 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001655 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001656 platform_device_unregister(pdev);
1657 platform_driver_unregister(&applesmc_driver);
1658 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1659
1660 printk(KERN_INFO "applesmc: driver unloaded.\n");
1661}
1662
1663module_init(applesmc_init);
1664module_exit(applesmc_exit);
1665
1666MODULE_AUTHOR("Nicolas Boichat");
1667MODULE_DESCRIPTION("Apple SMC");
1668MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001669MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);