blob: ce41a437477d6b863225db3cd58b24956e02e100 [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 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700162};
163
164/* List of keys used to read/write fan speeds */
165static const char* fan_speed_keys[] = {
166 FAN_ACTUAL_SPEED,
167 FAN_MIN_SPEED,
168 FAN_MAX_SPEED,
169 FAN_SAFE_SPEED,
170 FAN_TARGET_SPEED
171};
172
173#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
174#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
175
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400176#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700177#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
178#define APPLESMC_INPUT_FLAT 4
179
180#define SENSOR_X 0
181#define SENSOR_Y 1
182#define SENSOR_Z 2
183
184/* Structure to be passed to DMI_MATCH function */
185struct dmi_match_data {
186/* Indicates whether this computer has an accelerometer. */
187 int accelerometer;
188/* Indicates whether this computer has light sensors and keyboard backlight. */
189 int light;
190/* Indicates which temperature sensors set to use. */
191 int temperature_set;
192};
193
194static const int debug;
195static struct platform_device *pdev;
196static s16 rest_x;
197static s16 rest_y;
Henrik Rydberga976f152009-09-21 17:04:50 -0700198static u8 backlight_state[2];
199
Tony Jones1beeffe2007-08-20 13:46:20 -0700200static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400201static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700202
203/* Indicates whether this computer has an accelerometer. */
204static unsigned int applesmc_accelerometer;
205
206/* Indicates whether this computer has light sensors and keyboard backlight. */
207static unsigned int applesmc_light;
208
Henrik Rydberg0559a532010-05-11 09:17:47 +0200209/* The number of fans handled by the driver */
210static unsigned int fans_handled;
211
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700212/* Indicates which temperature sensors set to use. */
213static unsigned int applesmc_temperature_set;
214
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400215static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700216
217/*
218 * Last index written to key_at_index sysfs file, and value to use for all other
219 * key_at_index_* sysfs files.
220 */
221static unsigned int key_at_index;
222
223static struct workqueue_struct *applesmc_led_wq;
224
225/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700226 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700227 * (masked with 0x0f), returning zero if the value is obtained. Callers must
228 * hold applesmc_lock.
229 */
230static int __wait_status(u8 val)
231{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700232 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700233
234 val = val & APPLESMC_STATUS_MASK;
235
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700236 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
237 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700238 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
239 if (debug)
240 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700241 "Waited %d us for status %x\n",
242 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700243 return 0;
244 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700245 }
246
247 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
248 val, inb(APPLESMC_CMD_PORT));
249
250 return -EIO;
251}
252
253/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700254 * special treatment of command port - on newer macbooks, it seems necessary
255 * to resend the command byte before polling the status again. Callers must
256 * hold applesmc_lock.
257 */
258static int send_command(u8 cmd)
259{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700260 int us;
261 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700262 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700263 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700264 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
265 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700266 }
267 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
268 cmd, inb(APPLESMC_CMD_PORT));
269 return -EIO;
270}
271
272/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700273 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
274 * Returns zero on success or a negative error on failure. Callers must
275 * hold applesmc_lock.
276 */
277static int applesmc_read_key(const char* key, u8* buffer, u8 len)
278{
279 int i;
280
281 if (len > APPLESMC_MAX_DATA_LENGTH) {
282 printk(KERN_ERR "applesmc_read_key: cannot read more than "
283 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
284 return -EINVAL;
285 }
286
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700287 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700288 return -EIO;
289
290 for (i = 0; i < 4; i++) {
291 outb(key[i], APPLESMC_DATA_PORT);
292 if (__wait_status(0x04))
293 return -EIO;
294 }
295 if (debug)
296 printk(KERN_DEBUG "<%s", key);
297
298 outb(len, APPLESMC_DATA_PORT);
299 if (debug)
300 printk(KERN_DEBUG ">%x", len);
301
302 for (i = 0; i < len; i++) {
303 if (__wait_status(0x05))
304 return -EIO;
305 buffer[i] = inb(APPLESMC_DATA_PORT);
306 if (debug)
307 printk(KERN_DEBUG "<%x", buffer[i]);
308 }
309 if (debug)
310 printk(KERN_DEBUG "\n");
311
312 return 0;
313}
314
315/*
316 * applesmc_write_key - writes len bytes from buffer to a given key.
317 * Returns zero on success or a negative error on failure. Callers must
318 * hold applesmc_lock.
319 */
320static int applesmc_write_key(const char* key, u8* buffer, u8 len)
321{
322 int i;
323
324 if (len > APPLESMC_MAX_DATA_LENGTH) {
325 printk(KERN_ERR "applesmc_write_key: cannot write more than "
326 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
327 return -EINVAL;
328 }
329
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700330 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700331 return -EIO;
332
333 for (i = 0; i < 4; i++) {
334 outb(key[i], APPLESMC_DATA_PORT);
335 if (__wait_status(0x04))
336 return -EIO;
337 }
338
339 outb(len, APPLESMC_DATA_PORT);
340
341 for (i = 0; i < len; i++) {
342 if (__wait_status(0x04))
343 return -EIO;
344 outb(buffer[i], APPLESMC_DATA_PORT);
345 }
346
347 return 0;
348}
349
350/*
351 * applesmc_get_key_at_index - get key at index, and put the result in key
352 * (char[6]). Returns zero on success or a negative error on failure. Callers
353 * must hold applesmc_lock.
354 */
355static int applesmc_get_key_at_index(int index, char* key)
356{
357 int i;
358 u8 readkey[4];
359 readkey[0] = index >> 24;
360 readkey[1] = index >> 16;
361 readkey[2] = index >> 8;
362 readkey[3] = index;
363
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700364 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700365 return -EIO;
366
367 for (i = 0; i < 4; i++) {
368 outb(readkey[i], APPLESMC_DATA_PORT);
369 if (__wait_status(0x04))
370 return -EIO;
371 }
372
373 outb(4, APPLESMC_DATA_PORT);
374
375 for (i = 0; i < 4; i++) {
376 if (__wait_status(0x05))
377 return -EIO;
378 key[i] = inb(APPLESMC_DATA_PORT);
379 }
380 key[4] = 0;
381
382 return 0;
383}
384
385/*
386 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
387 * Returns zero on success or a negative error on failure. Callers must
388 * hold applesmc_lock.
389 */
390static int applesmc_get_key_type(char* key, char* type)
391{
392 int i;
393
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700394 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700395 return -EIO;
396
397 for (i = 0; i < 4; i++) {
398 outb(key[i], APPLESMC_DATA_PORT);
399 if (__wait_status(0x04))
400 return -EIO;
401 }
402
Henrik Rydberg05224092008-10-18 20:27:35 -0700403 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700404
405 for (i = 0; i < 6; i++) {
406 if (__wait_status(0x05))
407 return -EIO;
408 type[i] = inb(APPLESMC_DATA_PORT);
409 }
410 type[5] = 0;
411
412 return 0;
413}
414
415/*
416 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
417 * hold applesmc_lock.
418 */
419static int applesmc_read_motion_sensor(int index, s16* value)
420{
421 u8 buffer[2];
422 int ret;
423
424 switch (index) {
425 case SENSOR_X:
426 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
427 break;
428 case SENSOR_Y:
429 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
430 break;
431 case SENSOR_Z:
432 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
433 break;
434 default:
435 ret = -EINVAL;
436 }
437
438 *value = ((s16)buffer[0] << 8) | buffer[1];
439
440 return ret;
441}
442
443/*
444 * applesmc_device_init - initialize the accelerometer. Returns zero on success
445 * and negative error code on failure. Can sleep.
446 */
447static int applesmc_device_init(void)
448{
449 int total, ret = -ENXIO;
450 u8 buffer[2];
451
452 if (!applesmc_accelerometer)
453 return 0;
454
455 mutex_lock(&applesmc_lock);
456
457 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
458 if (debug)
459 printk(KERN_DEBUG "applesmc try %d\n", total);
460 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
461 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
462 if (total == INIT_TIMEOUT_MSECS) {
463 printk(KERN_DEBUG "applesmc: device has"
464 " already been initialized"
465 " (0x%02x, 0x%02x).\n",
466 buffer[0], buffer[1]);
467 } else {
468 printk(KERN_DEBUG "applesmc: device"
469 " successfully initialized"
470 " (0x%02x, 0x%02x).\n",
471 buffer[0], buffer[1]);
472 }
473 ret = 0;
474 goto out;
475 }
476 buffer[0] = 0xe0;
477 buffer[1] = 0x00;
478 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
479 msleep(INIT_WAIT_MSECS);
480 }
481
482 printk(KERN_WARNING "applesmc: failed to init the device\n");
483
484out:
485 mutex_unlock(&applesmc_lock);
486 return ret;
487}
488
489/*
490 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
491 * applesmc_lock.
492 */
493static int applesmc_get_fan_count(void)
494{
495 int ret;
496 u8 buffer[1];
497
498 mutex_lock(&applesmc_lock);
499
500 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
501
502 mutex_unlock(&applesmc_lock);
503 if (ret)
504 return ret;
505 else
506 return buffer[0];
507}
508
509/* Device model stuff */
510static int applesmc_probe(struct platform_device *dev)
511{
512 int ret;
513
514 ret = applesmc_device_init();
515 if (ret)
516 return ret;
517
518 printk(KERN_INFO "applesmc: device successfully initialized.\n");
519 return 0;
520}
521
Henrik Rydberga976f152009-09-21 17:04:50 -0700522/* Synchronize device with memorized backlight state */
523static int applesmc_pm_resume(struct device *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700524{
Henrik Rydberga976f152009-09-21 17:04:50 -0700525 mutex_lock(&applesmc_lock);
526 if (applesmc_light)
527 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
528 mutex_unlock(&applesmc_lock);
529 return 0;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700530}
531
Henrik Rydberga976f152009-09-21 17:04:50 -0700532/* Reinitialize device on resume from hibernation */
533static int applesmc_pm_restore(struct device *dev)
534{
535 int ret = applesmc_device_init();
536 if (ret)
537 return ret;
538 return applesmc_pm_resume(dev);
539}
540
Alexey Dobriyan47145212009-12-14 18:00:08 -0800541static const struct dev_pm_ops applesmc_pm_ops = {
Henrik Rydberga976f152009-09-21 17:04:50 -0700542 .resume = applesmc_pm_resume,
543 .restore = applesmc_pm_restore,
544};
545
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700546static struct platform_driver applesmc_driver = {
547 .probe = applesmc_probe,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700548 .driver = {
549 .name = "applesmc",
550 .owner = THIS_MODULE,
Henrik Rydberga976f152009-09-21 17:04:50 -0700551 .pm = &applesmc_pm_ops,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700552 },
553};
554
555/*
556 * applesmc_calibrate - Set our "resting" values. Callers must
557 * hold applesmc_lock.
558 */
559static void applesmc_calibrate(void)
560{
561 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
562 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
563 rest_x = -rest_x;
564}
565
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400566static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700567{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400568 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700569 s16 x, y;
570
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400571 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700572
573 if (applesmc_read_motion_sensor(SENSOR_X, &x))
574 goto out;
575 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
576 goto out;
577
578 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400579 input_report_abs(idev, ABS_X, x - rest_x);
580 input_report_abs(idev, ABS_Y, y - rest_y);
581 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700582
583out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700584 mutex_unlock(&applesmc_lock);
585}
586
587/* Sysfs Files */
588
Nicolas Boichatfa744192007-05-23 13:58:13 -0700589static ssize_t applesmc_name_show(struct device *dev,
590 struct device_attribute *attr, char *buf)
591{
592 return snprintf(buf, PAGE_SIZE, "applesmc\n");
593}
594
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700595static ssize_t applesmc_position_show(struct device *dev,
596 struct device_attribute *attr, char *buf)
597{
598 int ret;
599 s16 x, y, z;
600
601 mutex_lock(&applesmc_lock);
602
603 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
604 if (ret)
605 goto out;
606 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
607 if (ret)
608 goto out;
609 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
610 if (ret)
611 goto out;
612
613out:
614 mutex_unlock(&applesmc_lock);
615 if (ret)
616 return ret;
617 else
618 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
619}
620
621static ssize_t applesmc_light_show(struct device *dev,
622 struct device_attribute *attr, char *sysfsbuf)
623{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700624 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700625 int ret;
626 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700627 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700628
629 mutex_lock(&applesmc_lock);
630
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700631 if (!data_length) {
632 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
633 if (ret)
634 goto out;
635 data_length = clamp_val(query[0], 0, 10);
636 printk(KERN_INFO "applesmc: light sensor data length set to "
637 "%d\n", data_length);
638 }
639
640 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800641 /* newer macbooks report a single 10-bit bigendian value */
642 if (data_length == 10) {
643 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
644 goto out;
645 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700646 left = buffer[2];
647 if (ret)
648 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700649 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700650 right = buffer[2];
651
652out:
653 mutex_unlock(&applesmc_lock);
654 if (ret)
655 return ret;
656 else
657 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
658}
659
660/* Displays degree Celsius * 1000 */
661static ssize_t applesmc_show_temperature(struct device *dev,
662 struct device_attribute *devattr, char *sysfsbuf)
663{
664 int ret;
665 u8 buffer[2];
666 unsigned int temp;
667 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
668 const char* key =
669 temperature_sensors_sets[applesmc_temperature_set][attr->index];
670
671 mutex_lock(&applesmc_lock);
672
673 ret = applesmc_read_key(key, buffer, 2);
674 temp = buffer[0]*1000;
675 temp += (buffer[1] >> 6) * 250;
676
677 mutex_unlock(&applesmc_lock);
678
679 if (ret)
680 return ret;
681 else
682 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
683}
684
685static ssize_t applesmc_show_fan_speed(struct device *dev,
686 struct device_attribute *attr, char *sysfsbuf)
687{
688 int ret;
689 unsigned int speed = 0;
690 char newkey[5];
691 u8 buffer[2];
692 struct sensor_device_attribute_2 *sensor_attr =
693 to_sensor_dev_attr_2(attr);
694
695 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
696 newkey[1] = '0' + sensor_attr->index;
697 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
698 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
699 newkey[4] = 0;
700
701 mutex_lock(&applesmc_lock);
702
703 ret = applesmc_read_key(newkey, buffer, 2);
704 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
705
706 mutex_unlock(&applesmc_lock);
707 if (ret)
708 return ret;
709 else
710 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
711}
712
713static ssize_t applesmc_store_fan_speed(struct device *dev,
714 struct device_attribute *attr,
715 const char *sysfsbuf, size_t count)
716{
717 int ret;
718 u32 speed;
719 char newkey[5];
720 u8 buffer[2];
721 struct sensor_device_attribute_2 *sensor_attr =
722 to_sensor_dev_attr_2(attr);
723
724 speed = simple_strtoul(sysfsbuf, NULL, 10);
725
726 if (speed > 0x4000) /* Bigger than a 14-bit value */
727 return -EINVAL;
728
729 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
730 newkey[1] = '0' + sensor_attr->index;
731 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
732 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
733 newkey[4] = 0;
734
735 mutex_lock(&applesmc_lock);
736
737 buffer[0] = (speed >> 6) & 0xff;
738 buffer[1] = (speed << 2) & 0xff;
739 ret = applesmc_write_key(newkey, buffer, 2);
740
741 mutex_unlock(&applesmc_lock);
742 if (ret)
743 return ret;
744 else
745 return count;
746}
747
748static ssize_t applesmc_show_fan_manual(struct device *dev,
749 struct device_attribute *devattr, char *sysfsbuf)
750{
751 int ret;
752 u16 manual = 0;
753 u8 buffer[2];
754 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
755
756 mutex_lock(&applesmc_lock);
757
758 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
759 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
760
761 mutex_unlock(&applesmc_lock);
762 if (ret)
763 return ret;
764 else
765 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
766}
767
768static ssize_t applesmc_store_fan_manual(struct device *dev,
769 struct device_attribute *devattr,
770 const char *sysfsbuf, size_t count)
771{
772 int ret;
773 u8 buffer[2];
774 u32 input;
775 u16 val;
776 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
777
778 input = simple_strtoul(sysfsbuf, NULL, 10);
779
780 mutex_lock(&applesmc_lock);
781
782 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
783 val = (buffer[0] << 8 | buffer[1]);
784 if (ret)
785 goto out;
786
787 if (input)
788 val = val | (0x01 << attr->index);
789 else
790 val = val & ~(0x01 << attr->index);
791
792 buffer[0] = (val >> 8) & 0xFF;
793 buffer[1] = val & 0xFF;
794
795 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
796
797out:
798 mutex_unlock(&applesmc_lock);
799 if (ret)
800 return ret;
801 else
802 return count;
803}
804
805static ssize_t applesmc_show_fan_position(struct device *dev,
806 struct device_attribute *attr, char *sysfsbuf)
807{
808 int ret;
809 char newkey[5];
810 u8 buffer[17];
811 struct sensor_device_attribute_2 *sensor_attr =
812 to_sensor_dev_attr_2(attr);
813
814 newkey[0] = FAN_POSITION[0];
815 newkey[1] = '0' + sensor_attr->index;
816 newkey[2] = FAN_POSITION[2];
817 newkey[3] = FAN_POSITION[3];
818 newkey[4] = 0;
819
820 mutex_lock(&applesmc_lock);
821
822 ret = applesmc_read_key(newkey, buffer, 16);
823 buffer[16] = 0;
824
825 mutex_unlock(&applesmc_lock);
826 if (ret)
827 return ret;
828 else
829 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
830}
831
832static ssize_t applesmc_calibrate_show(struct device *dev,
833 struct device_attribute *attr, char *sysfsbuf)
834{
835 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
836}
837
838static ssize_t applesmc_calibrate_store(struct device *dev,
839 struct device_attribute *attr, const char *sysfsbuf, size_t count)
840{
841 mutex_lock(&applesmc_lock);
842 applesmc_calibrate();
843 mutex_unlock(&applesmc_lock);
844
845 return count;
846}
847
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700848static void applesmc_backlight_set(struct work_struct *work)
849{
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700850 mutex_lock(&applesmc_lock);
Henrik Rydberga976f152009-09-21 17:04:50 -0700851 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700852 mutex_unlock(&applesmc_lock);
853}
854static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
855
856static void applesmc_brightness_set(struct led_classdev *led_cdev,
857 enum led_brightness value)
858{
859 int ret;
860
Henrik Rydberga976f152009-09-21 17:04:50 -0700861 backlight_state[0] = value;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700862 ret = queue_work(applesmc_led_wq, &backlight_work);
863
864 if (debug && (!ret))
865 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
866}
867
868static ssize_t applesmc_key_count_show(struct device *dev,
869 struct device_attribute *attr, char *sysfsbuf)
870{
871 int ret;
872 u8 buffer[4];
873 u32 count;
874
875 mutex_lock(&applesmc_lock);
876
877 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
878 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
879 ((u32)buffer[2]<<8) + buffer[3];
880
881 mutex_unlock(&applesmc_lock);
882 if (ret)
883 return ret;
884 else
885 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
886}
887
888static ssize_t applesmc_key_at_index_read_show(struct device *dev,
889 struct device_attribute *attr, char *sysfsbuf)
890{
891 char key[5];
892 char info[6];
893 int ret;
894
895 mutex_lock(&applesmc_lock);
896
897 ret = applesmc_get_key_at_index(key_at_index, key);
898
899 if (ret || !key[0]) {
900 mutex_unlock(&applesmc_lock);
901
902 return -EINVAL;
903 }
904
905 ret = applesmc_get_key_type(key, info);
906
907 if (ret) {
908 mutex_unlock(&applesmc_lock);
909
910 return ret;
911 }
912
913 /*
914 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
915 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
916 */
917 ret = applesmc_read_key(key, sysfsbuf, info[0]);
918
919 mutex_unlock(&applesmc_lock);
920
921 if (!ret) {
922 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400923 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700924 return ret;
925 }
926}
927
928static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
929 struct device_attribute *attr, char *sysfsbuf)
930{
931 char key[5];
932 char info[6];
933 int ret;
934
935 mutex_lock(&applesmc_lock);
936
937 ret = applesmc_get_key_at_index(key_at_index, key);
938
939 if (ret || !key[0]) {
940 mutex_unlock(&applesmc_lock);
941
942 return -EINVAL;
943 }
944
945 ret = applesmc_get_key_type(key, info);
946
947 mutex_unlock(&applesmc_lock);
948
949 if (!ret)
950 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
951 else
952 return ret;
953}
954
955static ssize_t applesmc_key_at_index_type_show(struct device *dev,
956 struct device_attribute *attr, char *sysfsbuf)
957{
958 char key[5];
959 char info[6];
960 int ret;
961
962 mutex_lock(&applesmc_lock);
963
964 ret = applesmc_get_key_at_index(key_at_index, key);
965
966 if (ret || !key[0]) {
967 mutex_unlock(&applesmc_lock);
968
969 return -EINVAL;
970 }
971
972 ret = applesmc_get_key_type(key, info);
973
974 mutex_unlock(&applesmc_lock);
975
976 if (!ret)
977 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
978 else
979 return ret;
980}
981
982static ssize_t applesmc_key_at_index_name_show(struct device *dev,
983 struct device_attribute *attr, char *sysfsbuf)
984{
985 char key[5];
986 int ret;
987
988 mutex_lock(&applesmc_lock);
989
990 ret = applesmc_get_key_at_index(key_at_index, key);
991
992 mutex_unlock(&applesmc_lock);
993
994 if (!ret && key[0])
995 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
996 else
997 return -EINVAL;
998}
999
1000static ssize_t applesmc_key_at_index_show(struct device *dev,
1001 struct device_attribute *attr, char *sysfsbuf)
1002{
1003 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
1004}
1005
1006static ssize_t applesmc_key_at_index_store(struct device *dev,
1007 struct device_attribute *attr, const char *sysfsbuf, size_t count)
1008{
1009 mutex_lock(&applesmc_lock);
1010
1011 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
1012
1013 mutex_unlock(&applesmc_lock);
1014
1015 return count;
1016}
1017
1018static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +01001019 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001020 .default_trigger = "nand-disk",
1021 .brightness_set = applesmc_brightness_set,
1022};
1023
Nicolas Boichatfa744192007-05-23 13:58:13 -07001024static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
1025
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001026static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
1027static DEVICE_ATTR(calibrate, 0644,
1028 applesmc_calibrate_show, applesmc_calibrate_store);
1029
1030static struct attribute *accelerometer_attributes[] = {
1031 &dev_attr_position.attr,
1032 &dev_attr_calibrate.attr,
1033 NULL
1034};
1035
1036static const struct attribute_group accelerometer_attributes_group =
1037 { .attrs = accelerometer_attributes };
1038
1039static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1040
1041static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1042static DEVICE_ATTR(key_at_index, 0644,
1043 applesmc_key_at_index_show, applesmc_key_at_index_store);
1044static DEVICE_ATTR(key_at_index_name, 0444,
1045 applesmc_key_at_index_name_show, NULL);
1046static DEVICE_ATTR(key_at_index_type, 0444,
1047 applesmc_key_at_index_type_show, NULL);
1048static DEVICE_ATTR(key_at_index_data_length, 0444,
1049 applesmc_key_at_index_data_length_show, NULL);
1050static DEVICE_ATTR(key_at_index_data, 0444,
1051 applesmc_key_at_index_read_show, NULL);
1052
1053static struct attribute *key_enumeration_attributes[] = {
1054 &dev_attr_key_count.attr,
1055 &dev_attr_key_at_index.attr,
1056 &dev_attr_key_at_index_name.attr,
1057 &dev_attr_key_at_index_type.attr,
1058 &dev_attr_key_at_index_data_length.attr,
1059 &dev_attr_key_at_index_data.attr,
1060 NULL
1061};
1062
1063static const struct attribute_group key_enumeration_group =
1064 { .attrs = key_enumeration_attributes };
1065
1066/*
1067 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1068 * - show actual speed
1069 * - show/store minimum speed
1070 * - show maximum speed
1071 * - show safe speed
1072 * - show/store target speed
1073 * - show/store manual mode
1074 */
1075#define sysfs_fan_speeds_offset(offset) \
1076static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1077 applesmc_show_fan_speed, NULL, 0, offset-1); \
1078\
1079static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1080 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1081\
1082static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1083 applesmc_show_fan_speed, NULL, 2, offset-1); \
1084\
1085static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1086 applesmc_show_fan_speed, NULL, 3, offset-1); \
1087\
1088static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1089 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1090\
1091static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1092 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1093\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001094static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001095 applesmc_show_fan_position, NULL, offset-1); \
1096\
1097static struct attribute *fan##offset##_attributes[] = { \
1098 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1099 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1100 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1101 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1102 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1103 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001104 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001105 NULL \
1106};
1107
1108/*
1109 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001110 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001111 */
1112sysfs_fan_speeds_offset(1);
1113sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001114sysfs_fan_speeds_offset(3);
1115sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001116
1117static const struct attribute_group fan_attribute_groups[] = {
1118 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001119 { .attrs = fan2_attributes },
1120 { .attrs = fan3_attributes },
1121 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001122};
1123
1124/*
1125 * Temperature sensors sysfs entries.
1126 */
1127static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1128 applesmc_show_temperature, NULL, 0);
1129static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1130 applesmc_show_temperature, NULL, 1);
1131static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1132 applesmc_show_temperature, NULL, 2);
1133static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1134 applesmc_show_temperature, NULL, 3);
1135static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1136 applesmc_show_temperature, NULL, 4);
1137static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1138 applesmc_show_temperature, NULL, 5);
1139static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1140 applesmc_show_temperature, NULL, 6);
1141static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1142 applesmc_show_temperature, NULL, 7);
1143static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1144 applesmc_show_temperature, NULL, 8);
1145static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1146 applesmc_show_temperature, NULL, 9);
1147static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1148 applesmc_show_temperature, NULL, 10);
1149static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1150 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001151static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1152 applesmc_show_temperature, NULL, 12);
1153static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1154 applesmc_show_temperature, NULL, 13);
1155static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1156 applesmc_show_temperature, NULL, 14);
1157static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1158 applesmc_show_temperature, NULL, 15);
1159static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1160 applesmc_show_temperature, NULL, 16);
1161static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1162 applesmc_show_temperature, NULL, 17);
1163static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1164 applesmc_show_temperature, NULL, 18);
1165static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1166 applesmc_show_temperature, NULL, 19);
1167static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1168 applesmc_show_temperature, NULL, 20);
1169static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1170 applesmc_show_temperature, NULL, 21);
1171static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1172 applesmc_show_temperature, NULL, 22);
1173static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1174 applesmc_show_temperature, NULL, 23);
1175static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1176 applesmc_show_temperature, NULL, 24);
1177static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1178 applesmc_show_temperature, NULL, 25);
1179static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1180 applesmc_show_temperature, NULL, 26);
1181static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1182 applesmc_show_temperature, NULL, 27);
1183static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1184 applesmc_show_temperature, NULL, 28);
1185static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1186 applesmc_show_temperature, NULL, 29);
1187static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1188 applesmc_show_temperature, NULL, 30);
1189static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1190 applesmc_show_temperature, NULL, 31);
1191static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1192 applesmc_show_temperature, NULL, 32);
1193static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1194 applesmc_show_temperature, NULL, 33);
1195static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1196 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001197static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1198 applesmc_show_temperature, NULL, 35);
1199static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1200 applesmc_show_temperature, NULL, 36);
1201static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1202 applesmc_show_temperature, NULL, 37);
1203static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1204 applesmc_show_temperature, NULL, 38);
1205static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1206 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001207
1208static struct attribute *temperature_attributes[] = {
1209 &sensor_dev_attr_temp1_input.dev_attr.attr,
1210 &sensor_dev_attr_temp2_input.dev_attr.attr,
1211 &sensor_dev_attr_temp3_input.dev_attr.attr,
1212 &sensor_dev_attr_temp4_input.dev_attr.attr,
1213 &sensor_dev_attr_temp5_input.dev_attr.attr,
1214 &sensor_dev_attr_temp6_input.dev_attr.attr,
1215 &sensor_dev_attr_temp7_input.dev_attr.attr,
1216 &sensor_dev_attr_temp8_input.dev_attr.attr,
1217 &sensor_dev_attr_temp9_input.dev_attr.attr,
1218 &sensor_dev_attr_temp10_input.dev_attr.attr,
1219 &sensor_dev_attr_temp11_input.dev_attr.attr,
1220 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001221 &sensor_dev_attr_temp13_input.dev_attr.attr,
1222 &sensor_dev_attr_temp14_input.dev_attr.attr,
1223 &sensor_dev_attr_temp15_input.dev_attr.attr,
1224 &sensor_dev_attr_temp16_input.dev_attr.attr,
1225 &sensor_dev_attr_temp17_input.dev_attr.attr,
1226 &sensor_dev_attr_temp18_input.dev_attr.attr,
1227 &sensor_dev_attr_temp19_input.dev_attr.attr,
1228 &sensor_dev_attr_temp20_input.dev_attr.attr,
1229 &sensor_dev_attr_temp21_input.dev_attr.attr,
1230 &sensor_dev_attr_temp22_input.dev_attr.attr,
1231 &sensor_dev_attr_temp23_input.dev_attr.attr,
1232 &sensor_dev_attr_temp24_input.dev_attr.attr,
1233 &sensor_dev_attr_temp25_input.dev_attr.attr,
1234 &sensor_dev_attr_temp26_input.dev_attr.attr,
1235 &sensor_dev_attr_temp27_input.dev_attr.attr,
1236 &sensor_dev_attr_temp28_input.dev_attr.attr,
1237 &sensor_dev_attr_temp29_input.dev_attr.attr,
1238 &sensor_dev_attr_temp30_input.dev_attr.attr,
1239 &sensor_dev_attr_temp31_input.dev_attr.attr,
1240 &sensor_dev_attr_temp32_input.dev_attr.attr,
1241 &sensor_dev_attr_temp33_input.dev_attr.attr,
1242 &sensor_dev_attr_temp34_input.dev_attr.attr,
1243 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001244 &sensor_dev_attr_temp36_input.dev_attr.attr,
1245 &sensor_dev_attr_temp37_input.dev_attr.attr,
1246 &sensor_dev_attr_temp38_input.dev_attr.attr,
1247 &sensor_dev_attr_temp39_input.dev_attr.attr,
1248 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001249 NULL
1250};
1251
1252static const struct attribute_group temperature_attributes_group =
1253 { .attrs = temperature_attributes };
1254
1255/* Module stuff */
1256
1257/*
1258 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1259 */
Jeff Garzik18552562007-10-03 15:15:40 -04001260static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001261{
1262 int i = 0;
1263 struct dmi_match_data* dmi_data = id->driver_data;
1264 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1265 applesmc_accelerometer = dmi_data->accelerometer;
1266 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1267 applesmc_accelerometer ? "with" : "without");
1268 applesmc_light = dmi_data->light;
1269 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1270 applesmc_light ? "with" : "without");
1271
1272 applesmc_temperature_set = dmi_data->temperature_set;
1273 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1274 i++;
1275 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1276 return 1;
1277}
1278
1279/* Create accelerometer ressources */
1280static int applesmc_create_accelerometer(void)
1281{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001282 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001283 int ret;
1284
1285 ret = sysfs_create_group(&pdev->dev.kobj,
1286 &accelerometer_attributes_group);
1287 if (ret)
1288 goto out;
1289
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001290 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001291 if (!applesmc_idev) {
1292 ret = -ENOMEM;
1293 goto out_sysfs;
1294 }
1295
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001296 applesmc_idev->poll = applesmc_idev_poll;
1297 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1298
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001299 /* initial calibrate for the input device */
1300 applesmc_calibrate();
1301
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001302 /* initialize the input device */
1303 idev = applesmc_idev->input;
1304 idev->name = "applesmc";
1305 idev->id.bustype = BUS_HOST;
1306 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001307 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001308 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001309 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001310 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001311 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1312
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001313 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001314 if (ret)
1315 goto out_idev;
1316
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001317 return 0;
1318
1319out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001320 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001321
1322out_sysfs:
1323 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1324
1325out:
1326 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1327 return ret;
1328}
1329
1330/* Release all ressources used by the accelerometer */
1331static void applesmc_release_accelerometer(void)
1332{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001333 input_unregister_polled_device(applesmc_idev);
1334 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001335 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1336}
1337
1338static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1339/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1340 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001341/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001342 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001343/* MacBook: accelerometer and temperature set 2 */
1344 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1345/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001346 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001347/* MacPro: temperature set 4 */
1348 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001349/* iMac: temperature set 5 */
1350 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001351/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001352 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001353/* MacBook Air: accelerometer, backlight and temperature set 7 */
1354 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001355/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1356 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001357/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1358 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001359/* iMac 5: light sensor only, temperature set 10 */
1360 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a2008-11-06 12:53:20 -08001361/* MacBook 5: accelerometer, backlight and temperature set 11 */
1362 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001363/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1364 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001365/* iMac 8: light sensor only, temperature set 13 */
1366 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001367/* iMac 6: light sensor only, temperature set 14 */
1368 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001369/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1370 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001371/* MacPro3,1: temperature set 16 */
1372 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Justin P. Mattocke1741712010-04-14 16:14:10 +02001373/* iMac 9,1: light sensor only, temperature set 17 */
1374 { .accelerometer = 0, .light = 0, .temperature_set = 17 },
1375/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
1376 { .accelerometer = 1, .light = 1, .temperature_set = 18 },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001377/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
1378 { .accelerometer = 1, .light = 1, .temperature_set = 19 },
1379/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
1380 { .accelerometer = 1, .light = 1, .temperature_set = 20 },
Bernhard Froemel872bad52010-05-27 19:58:52 +02001381/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
1382 { .accelerometer = 1, .light = 1, .temperature_set = 21 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001383};
1384
1385/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1386 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1387static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001388 { applesmc_dmi_match, "Apple MacBook Air 2", {
1389 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1390 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1391 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001392 { applesmc_dmi_match, "Apple MacBook Air", {
1393 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1394 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001395 &applesmc_dmi_data[7]},
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001396 { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
1397 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1398 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
1399 &applesmc_dmi_data[20]},
1400 { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
1401 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1402 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
1403 &applesmc_dmi_data[19]},
Bernhard Froemel872bad52010-05-27 19:58:52 +02001404 { applesmc_dmi_match, "Apple MacBook Pro 6", {
1405 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1406 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
1407 &applesmc_dmi_data[21]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001408 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1409 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1410 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1411 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001412 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1413 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1414 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1415 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001416 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1417 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1418 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1419 &applesmc_dmi_data[9]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001420 { applesmc_dmi_match, "Apple MacBook Pro 2,2", {
1421 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Computer, Inc."),
1422 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2") },
1423 &applesmc_dmi_data[18]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001424 { applesmc_dmi_match, "Apple MacBook Pro", {
1425 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1426 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001427 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001428 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001429 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001430 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001431 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001432 { applesmc_dmi_match, "Apple MacBook (v3)", {
1433 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1434 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001435 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001436 { applesmc_dmi_match, "Apple MacBook 4", {
1437 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1438 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1439 &applesmc_dmi_data[6]},
Henrik Rydberg181209a2008-11-06 12:53:20 -08001440 { applesmc_dmi_match, "Apple MacBook 5", {
1441 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1442 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1443 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001444 { applesmc_dmi_match, "Apple MacBook", {
1445 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1446 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001447 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001448 { applesmc_dmi_match, "Apple Macmini", {
1449 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1450 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001451 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001452 { applesmc_dmi_match, "Apple MacPro2", {
1453 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1454 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001455 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001456 { applesmc_dmi_match, "Apple MacPro3", {
1457 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1458 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1459 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001460 { applesmc_dmi_match, "Apple MacPro", {
1461 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1462 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1463 &applesmc_dmi_data[4]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001464 { applesmc_dmi_match, "Apple iMac 9,1", {
1465 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
1466 DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1") },
1467 &applesmc_dmi_data[17]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001468 { applesmc_dmi_match, "Apple iMac 8", {
1469 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1470 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1471 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001472 { applesmc_dmi_match, "Apple iMac 6", {
1473 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1474 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1475 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001476 { applesmc_dmi_match, "Apple iMac 5", {
1477 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1478 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1479 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001480 { applesmc_dmi_match, "Apple iMac", {
1481 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1482 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001483 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001484 { .ident = NULL }
1485};
1486
1487static int __init applesmc_init(void)
1488{
1489 int ret;
1490 int count;
1491 int i;
1492
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001493 if (!dmi_check_system(applesmc_whitelist)) {
1494 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1495 ret = -ENODEV;
1496 goto out;
1497 }
1498
1499 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1500 "applesmc")) {
1501 ret = -ENXIO;
1502 goto out;
1503 }
1504
1505 ret = platform_driver_register(&applesmc_driver);
1506 if (ret)
1507 goto out_region;
1508
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001509 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1510 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001511 if (IS_ERR(pdev)) {
1512 ret = PTR_ERR(pdev);
1513 goto out_driver;
1514 }
1515
Nicolas Boichatfa744192007-05-23 13:58:13 -07001516 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001517 if (ret)
1518 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001519
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001520 /* Create key enumeration sysfs files */
1521 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1522 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001523 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001524
1525 /* create fan files */
1526 count = applesmc_get_fan_count();
Henrik Rydberg0559a532010-05-11 09:17:47 +02001527 if (count < 0)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001528 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001529 else
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001530 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1531
Henrik Rydberg0559a532010-05-11 09:17:47 +02001532 if (count > 4) {
1533 count = 4;
1534 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1535 " but at most 4 fans are supported"
1536 " by the driver.\n");
1537 }
1538
1539 while (fans_handled < count) {
1540 ret = sysfs_create_group(&pdev->dev.kobj,
1541 &fan_attribute_groups[fans_handled]);
1542 if (ret)
1543 goto out_fans;
1544 fans_handled++;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001545 }
1546
1547 for (i = 0;
1548 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1549 i++) {
1550 if (temperature_attributes[i] == NULL) {
1551 printk(KERN_ERR "applesmc: More temperature sensors "
1552 "in temperature_sensors_sets (at least %i)"
1553 "than available sysfs files in "
1554 "temperature_attributes (%i), please report "
1555 "this bug.\n", i, i-1);
1556 goto out_temperature;
1557 }
1558 ret = sysfs_create_file(&pdev->dev.kobj,
1559 temperature_attributes[i]);
1560 if (ret)
1561 goto out_temperature;
1562 }
1563
1564 if (applesmc_accelerometer) {
1565 ret = applesmc_create_accelerometer();
1566 if (ret)
1567 goto out_temperature;
1568 }
1569
1570 if (applesmc_light) {
1571 /* Add light sensor file */
1572 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1573 if (ret)
1574 goto out_accelerometer;
1575
1576 /* Create the workqueue */
1577 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1578 if (!applesmc_led_wq) {
1579 ret = -ENOMEM;
1580 goto out_light_sysfs;
1581 }
1582
1583 /* register as a led device */
1584 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1585 if (ret < 0)
1586 goto out_light_wq;
1587 }
1588
Tony Jones1beeffe2007-08-20 13:46:20 -07001589 hwmon_dev = hwmon_device_register(&pdev->dev);
1590 if (IS_ERR(hwmon_dev)) {
1591 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001592 goto out_light_ledclass;
1593 }
1594
1595 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1596
1597 return 0;
1598
1599out_light_ledclass:
1600 if (applesmc_light)
1601 led_classdev_unregister(&applesmc_backlight);
1602out_light_wq:
1603 if (applesmc_light)
1604 destroy_workqueue(applesmc_led_wq);
1605out_light_sysfs:
1606 if (applesmc_light)
1607 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1608out_accelerometer:
1609 if (applesmc_accelerometer)
1610 applesmc_release_accelerometer();
1611out_temperature:
1612 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001613out_fans:
1614 while (fans_handled)
1615 sysfs_remove_group(&pdev->dev.kobj,
1616 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001617 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001618out_name:
1619 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001620out_device:
1621 platform_device_unregister(pdev);
1622out_driver:
1623 platform_driver_unregister(&applesmc_driver);
1624out_region:
1625 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1626out:
1627 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1628 return ret;
1629}
1630
1631static void __exit applesmc_exit(void)
1632{
Tony Jones1beeffe2007-08-20 13:46:20 -07001633 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001634 if (applesmc_light) {
1635 led_classdev_unregister(&applesmc_backlight);
1636 destroy_workqueue(applesmc_led_wq);
1637 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1638 }
1639 if (applesmc_accelerometer)
1640 applesmc_release_accelerometer();
1641 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001642 while (fans_handled)
1643 sysfs_remove_group(&pdev->dev.kobj,
1644 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001645 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001646 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001647 platform_device_unregister(pdev);
1648 platform_driver_unregister(&applesmc_driver);
1649 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1650
1651 printk(KERN_INFO "applesmc: driver unloaded.\n");
1652}
1653
1654module_init(applesmc_init);
1655module_exit(applesmc_exit);
1656
1657MODULE_AUTHOR("Nicolas Boichat");
1658MODULE_DESCRIPTION("Apple SMC");
1659MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001660MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);