blob: be33094468dbf083df15d4ba202bf8e26eb082e2 [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 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700158};
159
160/* List of keys used to read/write fan speeds */
161static const char* fan_speed_keys[] = {
162 FAN_ACTUAL_SPEED,
163 FAN_MIN_SPEED,
164 FAN_MAX_SPEED,
165 FAN_SAFE_SPEED,
166 FAN_TARGET_SPEED
167};
168
169#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
170#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
171
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400172#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700173#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
174#define APPLESMC_INPUT_FLAT 4
175
176#define SENSOR_X 0
177#define SENSOR_Y 1
178#define SENSOR_Z 2
179
180/* Structure to be passed to DMI_MATCH function */
181struct dmi_match_data {
182/* Indicates whether this computer has an accelerometer. */
183 int accelerometer;
184/* Indicates whether this computer has light sensors and keyboard backlight. */
185 int light;
186/* Indicates which temperature sensors set to use. */
187 int temperature_set;
188};
189
190static const int debug;
191static struct platform_device *pdev;
192static s16 rest_x;
193static s16 rest_y;
Henrik Rydberga976f152009-09-21 17:04:50 -0700194static u8 backlight_state[2];
195
Tony Jones1beeffe2007-08-20 13:46:20 -0700196static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400197static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700198
199/* Indicates whether this computer has an accelerometer. */
200static unsigned int applesmc_accelerometer;
201
202/* Indicates whether this computer has light sensors and keyboard backlight. */
203static unsigned int applesmc_light;
204
Henrik Rydberg0559a532010-05-11 09:17:47 +0200205/* The number of fans handled by the driver */
206static unsigned int fans_handled;
207
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700208/* Indicates which temperature sensors set to use. */
209static unsigned int applesmc_temperature_set;
210
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400211static DEFINE_MUTEX(applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700212
213/*
214 * Last index written to key_at_index sysfs file, and value to use for all other
215 * key_at_index_* sysfs files.
216 */
217static unsigned int key_at_index;
218
219static struct workqueue_struct *applesmc_led_wq;
220
221/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700222 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700223 * (masked with 0x0f), returning zero if the value is obtained. Callers must
224 * hold applesmc_lock.
225 */
226static int __wait_status(u8 val)
227{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700228 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700229
230 val = val & APPLESMC_STATUS_MASK;
231
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700232 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
233 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700234 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
235 if (debug)
236 printk(KERN_DEBUG
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700237 "Waited %d us for status %x\n",
238 2 * us - APPLESMC_MIN_WAIT, val);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700239 return 0;
240 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700241 }
242
243 printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
244 val, inb(APPLESMC_CMD_PORT));
245
246 return -EIO;
247}
248
249/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700250 * special treatment of command port - on newer macbooks, it seems necessary
251 * to resend the command byte before polling the status again. Callers must
252 * hold applesmc_lock.
253 */
254static int send_command(u8 cmd)
255{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700256 int us;
257 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700258 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700259 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700260 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
261 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700262 }
263 printk(KERN_WARNING "applesmc: command failed: %x -> %x\n",
264 cmd, inb(APPLESMC_CMD_PORT));
265 return -EIO;
266}
267
268/*
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700269 * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
270 * Returns zero on success or a negative error on failure. Callers must
271 * hold applesmc_lock.
272 */
273static int applesmc_read_key(const char* key, u8* buffer, u8 len)
274{
275 int i;
276
277 if (len > APPLESMC_MAX_DATA_LENGTH) {
278 printk(KERN_ERR "applesmc_read_key: cannot read more than "
279 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
280 return -EINVAL;
281 }
282
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700283 if (send_command(APPLESMC_READ_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700284 return -EIO;
285
286 for (i = 0; i < 4; i++) {
287 outb(key[i], APPLESMC_DATA_PORT);
288 if (__wait_status(0x04))
289 return -EIO;
290 }
291 if (debug)
292 printk(KERN_DEBUG "<%s", key);
293
294 outb(len, APPLESMC_DATA_PORT);
295 if (debug)
296 printk(KERN_DEBUG ">%x", len);
297
298 for (i = 0; i < len; i++) {
299 if (__wait_status(0x05))
300 return -EIO;
301 buffer[i] = inb(APPLESMC_DATA_PORT);
302 if (debug)
303 printk(KERN_DEBUG "<%x", buffer[i]);
304 }
305 if (debug)
306 printk(KERN_DEBUG "\n");
307
308 return 0;
309}
310
311/*
312 * applesmc_write_key - writes len bytes from buffer to a given key.
313 * Returns zero on success or a negative error on failure. Callers must
314 * hold applesmc_lock.
315 */
316static int applesmc_write_key(const char* key, u8* buffer, u8 len)
317{
318 int i;
319
320 if (len > APPLESMC_MAX_DATA_LENGTH) {
321 printk(KERN_ERR "applesmc_write_key: cannot write more than "
322 "%d bytes\n", APPLESMC_MAX_DATA_LENGTH);
323 return -EINVAL;
324 }
325
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700326 if (send_command(APPLESMC_WRITE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700327 return -EIO;
328
329 for (i = 0; i < 4; i++) {
330 outb(key[i], APPLESMC_DATA_PORT);
331 if (__wait_status(0x04))
332 return -EIO;
333 }
334
335 outb(len, APPLESMC_DATA_PORT);
336
337 for (i = 0; i < len; i++) {
338 if (__wait_status(0x04))
339 return -EIO;
340 outb(buffer[i], APPLESMC_DATA_PORT);
341 }
342
343 return 0;
344}
345
346/*
347 * applesmc_get_key_at_index - get key at index, and put the result in key
348 * (char[6]). Returns zero on success or a negative error on failure. Callers
349 * must hold applesmc_lock.
350 */
351static int applesmc_get_key_at_index(int index, char* key)
352{
353 int i;
354 u8 readkey[4];
355 readkey[0] = index >> 24;
356 readkey[1] = index >> 16;
357 readkey[2] = index >> 8;
358 readkey[3] = index;
359
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700360 if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700361 return -EIO;
362
363 for (i = 0; i < 4; i++) {
364 outb(readkey[i], APPLESMC_DATA_PORT);
365 if (__wait_status(0x04))
366 return -EIO;
367 }
368
369 outb(4, APPLESMC_DATA_PORT);
370
371 for (i = 0; i < 4; i++) {
372 if (__wait_status(0x05))
373 return -EIO;
374 key[i] = inb(APPLESMC_DATA_PORT);
375 }
376 key[4] = 0;
377
378 return 0;
379}
380
381/*
382 * applesmc_get_key_type - get key type, and put the result in type (char[6]).
383 * Returns zero on success or a negative error on failure. Callers must
384 * hold applesmc_lock.
385 */
386static int applesmc_get_key_type(char* key, char* type)
387{
388 int i;
389
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700390 if (send_command(APPLESMC_GET_KEY_TYPE_CMD))
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700391 return -EIO;
392
393 for (i = 0; i < 4; i++) {
394 outb(key[i], APPLESMC_DATA_PORT);
395 if (__wait_status(0x04))
396 return -EIO;
397 }
398
Henrik Rydberg05224092008-10-18 20:27:35 -0700399 outb(6, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700400
401 for (i = 0; i < 6; i++) {
402 if (__wait_status(0x05))
403 return -EIO;
404 type[i] = inb(APPLESMC_DATA_PORT);
405 }
406 type[5] = 0;
407
408 return 0;
409}
410
411/*
412 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
413 * hold applesmc_lock.
414 */
415static int applesmc_read_motion_sensor(int index, s16* value)
416{
417 u8 buffer[2];
418 int ret;
419
420 switch (index) {
421 case SENSOR_X:
422 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
423 break;
424 case SENSOR_Y:
425 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
426 break;
427 case SENSOR_Z:
428 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
429 break;
430 default:
431 ret = -EINVAL;
432 }
433
434 *value = ((s16)buffer[0] << 8) | buffer[1];
435
436 return ret;
437}
438
439/*
440 * applesmc_device_init - initialize the accelerometer. Returns zero on success
441 * and negative error code on failure. Can sleep.
442 */
443static int applesmc_device_init(void)
444{
445 int total, ret = -ENXIO;
446 u8 buffer[2];
447
448 if (!applesmc_accelerometer)
449 return 0;
450
451 mutex_lock(&applesmc_lock);
452
453 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
454 if (debug)
455 printk(KERN_DEBUG "applesmc try %d\n", total);
456 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
457 (buffer[0] != 0x00 || buffer[1] != 0x00)) {
458 if (total == INIT_TIMEOUT_MSECS) {
459 printk(KERN_DEBUG "applesmc: device has"
460 " already been initialized"
461 " (0x%02x, 0x%02x).\n",
462 buffer[0], buffer[1]);
463 } else {
464 printk(KERN_DEBUG "applesmc: device"
465 " successfully initialized"
466 " (0x%02x, 0x%02x).\n",
467 buffer[0], buffer[1]);
468 }
469 ret = 0;
470 goto out;
471 }
472 buffer[0] = 0xe0;
473 buffer[1] = 0x00;
474 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
475 msleep(INIT_WAIT_MSECS);
476 }
477
478 printk(KERN_WARNING "applesmc: failed to init the device\n");
479
480out:
481 mutex_unlock(&applesmc_lock);
482 return ret;
483}
484
485/*
486 * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
487 * applesmc_lock.
488 */
489static int applesmc_get_fan_count(void)
490{
491 int ret;
492 u8 buffer[1];
493
494 mutex_lock(&applesmc_lock);
495
496 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
497
498 mutex_unlock(&applesmc_lock);
499 if (ret)
500 return ret;
501 else
502 return buffer[0];
503}
504
505/* Device model stuff */
506static int applesmc_probe(struct platform_device *dev)
507{
508 int ret;
509
510 ret = applesmc_device_init();
511 if (ret)
512 return ret;
513
514 printk(KERN_INFO "applesmc: device successfully initialized.\n");
515 return 0;
516}
517
Henrik Rydberga976f152009-09-21 17:04:50 -0700518/* Synchronize device with memorized backlight state */
519static int applesmc_pm_resume(struct device *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700520{
Henrik Rydberga976f152009-09-21 17:04:50 -0700521 mutex_lock(&applesmc_lock);
522 if (applesmc_light)
523 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
524 mutex_unlock(&applesmc_lock);
525 return 0;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700526}
527
Henrik Rydberga976f152009-09-21 17:04:50 -0700528/* Reinitialize device on resume from hibernation */
529static int applesmc_pm_restore(struct device *dev)
530{
531 int ret = applesmc_device_init();
532 if (ret)
533 return ret;
534 return applesmc_pm_resume(dev);
535}
536
Alexey Dobriyan47145212009-12-14 18:00:08 -0800537static const struct dev_pm_ops applesmc_pm_ops = {
Henrik Rydberga976f152009-09-21 17:04:50 -0700538 .resume = applesmc_pm_resume,
539 .restore = applesmc_pm_restore,
540};
541
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700542static struct platform_driver applesmc_driver = {
543 .probe = applesmc_probe,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700544 .driver = {
545 .name = "applesmc",
546 .owner = THIS_MODULE,
Henrik Rydberga976f152009-09-21 17:04:50 -0700547 .pm = &applesmc_pm_ops,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700548 },
549};
550
551/*
552 * applesmc_calibrate - Set our "resting" values. Callers must
553 * hold applesmc_lock.
554 */
555static void applesmc_calibrate(void)
556{
557 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
558 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
559 rest_x = -rest_x;
560}
561
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400562static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700563{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400564 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700565 s16 x, y;
566
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400567 mutex_lock(&applesmc_lock);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700568
569 if (applesmc_read_motion_sensor(SENSOR_X, &x))
570 goto out;
571 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
572 goto out;
573
574 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400575 input_report_abs(idev, ABS_X, x - rest_x);
576 input_report_abs(idev, ABS_Y, y - rest_y);
577 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700578
579out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700580 mutex_unlock(&applesmc_lock);
581}
582
583/* Sysfs Files */
584
Nicolas Boichatfa744192007-05-23 13:58:13 -0700585static ssize_t applesmc_name_show(struct device *dev,
586 struct device_attribute *attr, char *buf)
587{
588 return snprintf(buf, PAGE_SIZE, "applesmc\n");
589}
590
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700591static ssize_t applesmc_position_show(struct device *dev,
592 struct device_attribute *attr, char *buf)
593{
594 int ret;
595 s16 x, y, z;
596
597 mutex_lock(&applesmc_lock);
598
599 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
600 if (ret)
601 goto out;
602 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
603 if (ret)
604 goto out;
605 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
606 if (ret)
607 goto out;
608
609out:
610 mutex_unlock(&applesmc_lock);
611 if (ret)
612 return ret;
613 else
614 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
615}
616
617static ssize_t applesmc_light_show(struct device *dev,
618 struct device_attribute *attr, char *sysfsbuf)
619{
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700620 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700621 int ret;
622 u8 left = 0, right = 0;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700623 u8 buffer[10], query[6];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700624
625 mutex_lock(&applesmc_lock);
626
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700627 if (!data_length) {
628 ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query);
629 if (ret)
630 goto out;
631 data_length = clamp_val(query[0], 0, 10);
632 printk(KERN_INFO "applesmc: light sensor data length set to "
633 "%d\n", data_length);
634 }
635
636 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800637 /* newer macbooks report a single 10-bit bigendian value */
638 if (data_length == 10) {
639 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
640 goto out;
641 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700642 left = buffer[2];
643 if (ret)
644 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700645 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700646 right = buffer[2];
647
648out:
649 mutex_unlock(&applesmc_lock);
650 if (ret)
651 return ret;
652 else
653 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
654}
655
656/* Displays degree Celsius * 1000 */
657static ssize_t applesmc_show_temperature(struct device *dev,
658 struct device_attribute *devattr, char *sysfsbuf)
659{
660 int ret;
661 u8 buffer[2];
662 unsigned int temp;
663 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
664 const char* key =
665 temperature_sensors_sets[applesmc_temperature_set][attr->index];
666
667 mutex_lock(&applesmc_lock);
668
669 ret = applesmc_read_key(key, buffer, 2);
670 temp = buffer[0]*1000;
671 temp += (buffer[1] >> 6) * 250;
672
673 mutex_unlock(&applesmc_lock);
674
675 if (ret)
676 return ret;
677 else
678 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
679}
680
681static ssize_t applesmc_show_fan_speed(struct device *dev,
682 struct device_attribute *attr, char *sysfsbuf)
683{
684 int ret;
685 unsigned int speed = 0;
686 char newkey[5];
687 u8 buffer[2];
688 struct sensor_device_attribute_2 *sensor_attr =
689 to_sensor_dev_attr_2(attr);
690
691 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
692 newkey[1] = '0' + sensor_attr->index;
693 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
694 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
695 newkey[4] = 0;
696
697 mutex_lock(&applesmc_lock);
698
699 ret = applesmc_read_key(newkey, buffer, 2);
700 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
701
702 mutex_unlock(&applesmc_lock);
703 if (ret)
704 return ret;
705 else
706 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
707}
708
709static ssize_t applesmc_store_fan_speed(struct device *dev,
710 struct device_attribute *attr,
711 const char *sysfsbuf, size_t count)
712{
713 int ret;
714 u32 speed;
715 char newkey[5];
716 u8 buffer[2];
717 struct sensor_device_attribute_2 *sensor_attr =
718 to_sensor_dev_attr_2(attr);
719
720 speed = simple_strtoul(sysfsbuf, NULL, 10);
721
722 if (speed > 0x4000) /* Bigger than a 14-bit value */
723 return -EINVAL;
724
725 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
726 newkey[1] = '0' + sensor_attr->index;
727 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
728 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
729 newkey[4] = 0;
730
731 mutex_lock(&applesmc_lock);
732
733 buffer[0] = (speed >> 6) & 0xff;
734 buffer[1] = (speed << 2) & 0xff;
735 ret = applesmc_write_key(newkey, buffer, 2);
736
737 mutex_unlock(&applesmc_lock);
738 if (ret)
739 return ret;
740 else
741 return count;
742}
743
744static ssize_t applesmc_show_fan_manual(struct device *dev,
745 struct device_attribute *devattr, char *sysfsbuf)
746{
747 int ret;
748 u16 manual = 0;
749 u8 buffer[2];
750 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
751
752 mutex_lock(&applesmc_lock);
753
754 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
755 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
756
757 mutex_unlock(&applesmc_lock);
758 if (ret)
759 return ret;
760 else
761 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
762}
763
764static ssize_t applesmc_store_fan_manual(struct device *dev,
765 struct device_attribute *devattr,
766 const char *sysfsbuf, size_t count)
767{
768 int ret;
769 u8 buffer[2];
770 u32 input;
771 u16 val;
772 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
773
774 input = simple_strtoul(sysfsbuf, NULL, 10);
775
776 mutex_lock(&applesmc_lock);
777
778 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
779 val = (buffer[0] << 8 | buffer[1]);
780 if (ret)
781 goto out;
782
783 if (input)
784 val = val | (0x01 << attr->index);
785 else
786 val = val & ~(0x01 << attr->index);
787
788 buffer[0] = (val >> 8) & 0xFF;
789 buffer[1] = val & 0xFF;
790
791 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
792
793out:
794 mutex_unlock(&applesmc_lock);
795 if (ret)
796 return ret;
797 else
798 return count;
799}
800
801static ssize_t applesmc_show_fan_position(struct device *dev,
802 struct device_attribute *attr, char *sysfsbuf)
803{
804 int ret;
805 char newkey[5];
806 u8 buffer[17];
807 struct sensor_device_attribute_2 *sensor_attr =
808 to_sensor_dev_attr_2(attr);
809
810 newkey[0] = FAN_POSITION[0];
811 newkey[1] = '0' + sensor_attr->index;
812 newkey[2] = FAN_POSITION[2];
813 newkey[3] = FAN_POSITION[3];
814 newkey[4] = 0;
815
816 mutex_lock(&applesmc_lock);
817
818 ret = applesmc_read_key(newkey, buffer, 16);
819 buffer[16] = 0;
820
821 mutex_unlock(&applesmc_lock);
822 if (ret)
823 return ret;
824 else
825 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
826}
827
828static ssize_t applesmc_calibrate_show(struct device *dev,
829 struct device_attribute *attr, char *sysfsbuf)
830{
831 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
832}
833
834static ssize_t applesmc_calibrate_store(struct device *dev,
835 struct device_attribute *attr, const char *sysfsbuf, size_t count)
836{
837 mutex_lock(&applesmc_lock);
838 applesmc_calibrate();
839 mutex_unlock(&applesmc_lock);
840
841 return count;
842}
843
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700844static void applesmc_backlight_set(struct work_struct *work)
845{
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700846 mutex_lock(&applesmc_lock);
Henrik Rydberga976f152009-09-21 17:04:50 -0700847 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700848 mutex_unlock(&applesmc_lock);
849}
850static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
851
852static void applesmc_brightness_set(struct led_classdev *led_cdev,
853 enum led_brightness value)
854{
855 int ret;
856
Henrik Rydberga976f152009-09-21 17:04:50 -0700857 backlight_state[0] = value;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700858 ret = queue_work(applesmc_led_wq, &backlight_work);
859
860 if (debug && (!ret))
861 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
862}
863
864static ssize_t applesmc_key_count_show(struct device *dev,
865 struct device_attribute *attr, char *sysfsbuf)
866{
867 int ret;
868 u8 buffer[4];
869 u32 count;
870
871 mutex_lock(&applesmc_lock);
872
873 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
874 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
875 ((u32)buffer[2]<<8) + buffer[3];
876
877 mutex_unlock(&applesmc_lock);
878 if (ret)
879 return ret;
880 else
881 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
882}
883
884static ssize_t applesmc_key_at_index_read_show(struct device *dev,
885 struct device_attribute *attr, char *sysfsbuf)
886{
887 char key[5];
888 char info[6];
889 int ret;
890
891 mutex_lock(&applesmc_lock);
892
893 ret = applesmc_get_key_at_index(key_at_index, key);
894
895 if (ret || !key[0]) {
896 mutex_unlock(&applesmc_lock);
897
898 return -EINVAL;
899 }
900
901 ret = applesmc_get_key_type(key, info);
902
903 if (ret) {
904 mutex_unlock(&applesmc_lock);
905
906 return ret;
907 }
908
909 /*
910 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
911 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
912 */
913 ret = applesmc_read_key(key, sysfsbuf, info[0]);
914
915 mutex_unlock(&applesmc_lock);
916
917 if (!ret) {
918 return info[0];
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400919 } else {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700920 return ret;
921 }
922}
923
924static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
925 struct device_attribute *attr, char *sysfsbuf)
926{
927 char key[5];
928 char info[6];
929 int ret;
930
931 mutex_lock(&applesmc_lock);
932
933 ret = applesmc_get_key_at_index(key_at_index, key);
934
935 if (ret || !key[0]) {
936 mutex_unlock(&applesmc_lock);
937
938 return -EINVAL;
939 }
940
941 ret = applesmc_get_key_type(key, info);
942
943 mutex_unlock(&applesmc_lock);
944
945 if (!ret)
946 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
947 else
948 return ret;
949}
950
951static ssize_t applesmc_key_at_index_type_show(struct device *dev,
952 struct device_attribute *attr, char *sysfsbuf)
953{
954 char key[5];
955 char info[6];
956 int ret;
957
958 mutex_lock(&applesmc_lock);
959
960 ret = applesmc_get_key_at_index(key_at_index, key);
961
962 if (ret || !key[0]) {
963 mutex_unlock(&applesmc_lock);
964
965 return -EINVAL;
966 }
967
968 ret = applesmc_get_key_type(key, info);
969
970 mutex_unlock(&applesmc_lock);
971
972 if (!ret)
973 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
974 else
975 return ret;
976}
977
978static ssize_t applesmc_key_at_index_name_show(struct device *dev,
979 struct device_attribute *attr, char *sysfsbuf)
980{
981 char key[5];
982 int ret;
983
984 mutex_lock(&applesmc_lock);
985
986 ret = applesmc_get_key_at_index(key_at_index, key);
987
988 mutex_unlock(&applesmc_lock);
989
990 if (!ret && key[0])
991 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
992 else
993 return -EINVAL;
994}
995
996static ssize_t applesmc_key_at_index_show(struct device *dev,
997 struct device_attribute *attr, char *sysfsbuf)
998{
999 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
1000}
1001
1002static ssize_t applesmc_key_at_index_store(struct device *dev,
1003 struct device_attribute *attr, const char *sysfsbuf, size_t count)
1004{
1005 mutex_lock(&applesmc_lock);
1006
1007 key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
1008
1009 mutex_unlock(&applesmc_lock);
1010
1011 return count;
1012}
1013
1014static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +01001015 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001016 .default_trigger = "nand-disk",
1017 .brightness_set = applesmc_brightness_set,
1018};
1019
Nicolas Boichatfa744192007-05-23 13:58:13 -07001020static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
1021
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001022static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
1023static DEVICE_ATTR(calibrate, 0644,
1024 applesmc_calibrate_show, applesmc_calibrate_store);
1025
1026static struct attribute *accelerometer_attributes[] = {
1027 &dev_attr_position.attr,
1028 &dev_attr_calibrate.attr,
1029 NULL
1030};
1031
1032static const struct attribute_group accelerometer_attributes_group =
1033 { .attrs = accelerometer_attributes };
1034
1035static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1036
1037static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1038static DEVICE_ATTR(key_at_index, 0644,
1039 applesmc_key_at_index_show, applesmc_key_at_index_store);
1040static DEVICE_ATTR(key_at_index_name, 0444,
1041 applesmc_key_at_index_name_show, NULL);
1042static DEVICE_ATTR(key_at_index_type, 0444,
1043 applesmc_key_at_index_type_show, NULL);
1044static DEVICE_ATTR(key_at_index_data_length, 0444,
1045 applesmc_key_at_index_data_length_show, NULL);
1046static DEVICE_ATTR(key_at_index_data, 0444,
1047 applesmc_key_at_index_read_show, NULL);
1048
1049static struct attribute *key_enumeration_attributes[] = {
1050 &dev_attr_key_count.attr,
1051 &dev_attr_key_at_index.attr,
1052 &dev_attr_key_at_index_name.attr,
1053 &dev_attr_key_at_index_type.attr,
1054 &dev_attr_key_at_index_data_length.attr,
1055 &dev_attr_key_at_index_data.attr,
1056 NULL
1057};
1058
1059static const struct attribute_group key_enumeration_group =
1060 { .attrs = key_enumeration_attributes };
1061
1062/*
1063 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1064 * - show actual speed
1065 * - show/store minimum speed
1066 * - show maximum speed
1067 * - show safe speed
1068 * - show/store target speed
1069 * - show/store manual mode
1070 */
1071#define sysfs_fan_speeds_offset(offset) \
1072static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1073 applesmc_show_fan_speed, NULL, 0, offset-1); \
1074\
1075static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1076 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1077\
1078static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1079 applesmc_show_fan_speed, NULL, 2, offset-1); \
1080\
1081static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1082 applesmc_show_fan_speed, NULL, 3, offset-1); \
1083\
1084static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1085 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1086\
1087static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1088 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1089\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001090static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001091 applesmc_show_fan_position, NULL, offset-1); \
1092\
1093static struct attribute *fan##offset##_attributes[] = { \
1094 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1095 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1096 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1097 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1098 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1099 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001100 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001101 NULL \
1102};
1103
1104/*
1105 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001106 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001107 */
1108sysfs_fan_speeds_offset(1);
1109sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001110sysfs_fan_speeds_offset(3);
1111sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001112
1113static const struct attribute_group fan_attribute_groups[] = {
1114 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001115 { .attrs = fan2_attributes },
1116 { .attrs = fan3_attributes },
1117 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001118};
1119
1120/*
1121 * Temperature sensors sysfs entries.
1122 */
1123static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1124 applesmc_show_temperature, NULL, 0);
1125static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1126 applesmc_show_temperature, NULL, 1);
1127static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1128 applesmc_show_temperature, NULL, 2);
1129static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1130 applesmc_show_temperature, NULL, 3);
1131static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1132 applesmc_show_temperature, NULL, 4);
1133static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1134 applesmc_show_temperature, NULL, 5);
1135static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1136 applesmc_show_temperature, NULL, 6);
1137static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1138 applesmc_show_temperature, NULL, 7);
1139static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1140 applesmc_show_temperature, NULL, 8);
1141static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1142 applesmc_show_temperature, NULL, 9);
1143static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1144 applesmc_show_temperature, NULL, 10);
1145static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1146 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001147static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1148 applesmc_show_temperature, NULL, 12);
1149static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1150 applesmc_show_temperature, NULL, 13);
1151static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1152 applesmc_show_temperature, NULL, 14);
1153static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1154 applesmc_show_temperature, NULL, 15);
1155static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1156 applesmc_show_temperature, NULL, 16);
1157static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1158 applesmc_show_temperature, NULL, 17);
1159static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1160 applesmc_show_temperature, NULL, 18);
1161static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1162 applesmc_show_temperature, NULL, 19);
1163static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1164 applesmc_show_temperature, NULL, 20);
1165static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1166 applesmc_show_temperature, NULL, 21);
1167static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1168 applesmc_show_temperature, NULL, 22);
1169static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1170 applesmc_show_temperature, NULL, 23);
1171static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1172 applesmc_show_temperature, NULL, 24);
1173static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1174 applesmc_show_temperature, NULL, 25);
1175static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1176 applesmc_show_temperature, NULL, 26);
1177static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1178 applesmc_show_temperature, NULL, 27);
1179static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1180 applesmc_show_temperature, NULL, 28);
1181static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1182 applesmc_show_temperature, NULL, 29);
1183static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1184 applesmc_show_temperature, NULL, 30);
1185static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1186 applesmc_show_temperature, NULL, 31);
1187static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1188 applesmc_show_temperature, NULL, 32);
1189static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1190 applesmc_show_temperature, NULL, 33);
1191static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1192 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001193static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1194 applesmc_show_temperature, NULL, 35);
1195static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1196 applesmc_show_temperature, NULL, 36);
1197static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1198 applesmc_show_temperature, NULL, 37);
1199static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1200 applesmc_show_temperature, NULL, 38);
1201static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1202 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001203
1204static struct attribute *temperature_attributes[] = {
1205 &sensor_dev_attr_temp1_input.dev_attr.attr,
1206 &sensor_dev_attr_temp2_input.dev_attr.attr,
1207 &sensor_dev_attr_temp3_input.dev_attr.attr,
1208 &sensor_dev_attr_temp4_input.dev_attr.attr,
1209 &sensor_dev_attr_temp5_input.dev_attr.attr,
1210 &sensor_dev_attr_temp6_input.dev_attr.attr,
1211 &sensor_dev_attr_temp7_input.dev_attr.attr,
1212 &sensor_dev_attr_temp8_input.dev_attr.attr,
1213 &sensor_dev_attr_temp9_input.dev_attr.attr,
1214 &sensor_dev_attr_temp10_input.dev_attr.attr,
1215 &sensor_dev_attr_temp11_input.dev_attr.attr,
1216 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001217 &sensor_dev_attr_temp13_input.dev_attr.attr,
1218 &sensor_dev_attr_temp14_input.dev_attr.attr,
1219 &sensor_dev_attr_temp15_input.dev_attr.attr,
1220 &sensor_dev_attr_temp16_input.dev_attr.attr,
1221 &sensor_dev_attr_temp17_input.dev_attr.attr,
1222 &sensor_dev_attr_temp18_input.dev_attr.attr,
1223 &sensor_dev_attr_temp19_input.dev_attr.attr,
1224 &sensor_dev_attr_temp20_input.dev_attr.attr,
1225 &sensor_dev_attr_temp21_input.dev_attr.attr,
1226 &sensor_dev_attr_temp22_input.dev_attr.attr,
1227 &sensor_dev_attr_temp23_input.dev_attr.attr,
1228 &sensor_dev_attr_temp24_input.dev_attr.attr,
1229 &sensor_dev_attr_temp25_input.dev_attr.attr,
1230 &sensor_dev_attr_temp26_input.dev_attr.attr,
1231 &sensor_dev_attr_temp27_input.dev_attr.attr,
1232 &sensor_dev_attr_temp28_input.dev_attr.attr,
1233 &sensor_dev_attr_temp29_input.dev_attr.attr,
1234 &sensor_dev_attr_temp30_input.dev_attr.attr,
1235 &sensor_dev_attr_temp31_input.dev_attr.attr,
1236 &sensor_dev_attr_temp32_input.dev_attr.attr,
1237 &sensor_dev_attr_temp33_input.dev_attr.attr,
1238 &sensor_dev_attr_temp34_input.dev_attr.attr,
1239 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001240 &sensor_dev_attr_temp36_input.dev_attr.attr,
1241 &sensor_dev_attr_temp37_input.dev_attr.attr,
1242 &sensor_dev_attr_temp38_input.dev_attr.attr,
1243 &sensor_dev_attr_temp39_input.dev_attr.attr,
1244 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001245 NULL
1246};
1247
1248static const struct attribute_group temperature_attributes_group =
1249 { .attrs = temperature_attributes };
1250
1251/* Module stuff */
1252
1253/*
1254 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1255 */
Jeff Garzik18552562007-10-03 15:15:40 -04001256static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001257{
1258 int i = 0;
1259 struct dmi_match_data* dmi_data = id->driver_data;
1260 printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
1261 applesmc_accelerometer = dmi_data->accelerometer;
1262 printk(KERN_INFO "applesmc: - Model %s accelerometer\n",
1263 applesmc_accelerometer ? "with" : "without");
1264 applesmc_light = dmi_data->light;
1265 printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n",
1266 applesmc_light ? "with" : "without");
1267
1268 applesmc_temperature_set = dmi_data->temperature_set;
1269 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1270 i++;
1271 printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i);
1272 return 1;
1273}
1274
1275/* Create accelerometer ressources */
1276static int applesmc_create_accelerometer(void)
1277{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001278 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001279 int ret;
1280
1281 ret = sysfs_create_group(&pdev->dev.kobj,
1282 &accelerometer_attributes_group);
1283 if (ret)
1284 goto out;
1285
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001286 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001287 if (!applesmc_idev) {
1288 ret = -ENOMEM;
1289 goto out_sysfs;
1290 }
1291
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001292 applesmc_idev->poll = applesmc_idev_poll;
1293 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1294
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001295 /* initial calibrate for the input device */
1296 applesmc_calibrate();
1297
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001298 /* initialize the input device */
1299 idev = applesmc_idev->input;
1300 idev->name = "applesmc";
1301 idev->id.bustype = BUS_HOST;
1302 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001303 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001304 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001305 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001306 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001307 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1308
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001309 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001310 if (ret)
1311 goto out_idev;
1312
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001313 return 0;
1314
1315out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001316 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001317
1318out_sysfs:
1319 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1320
1321out:
1322 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1323 return ret;
1324}
1325
1326/* Release all ressources used by the accelerometer */
1327static void applesmc_release_accelerometer(void)
1328{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001329 input_unregister_polled_device(applesmc_idev);
1330 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001331 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1332}
1333
1334static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1335/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1336 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001337/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001338 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001339/* MacBook: accelerometer and temperature set 2 */
1340 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1341/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001342 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001343/* MacPro: temperature set 4 */
1344 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001345/* iMac: temperature set 5 */
1346 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001347/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001348 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001349/* MacBook Air: accelerometer, backlight and temperature set 7 */
1350 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001351/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1352 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001353/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1354 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001355/* iMac 5: light sensor only, temperature set 10 */
1356 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a2008-11-06 12:53:20 -08001357/* MacBook 5: accelerometer, backlight and temperature set 11 */
1358 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001359/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1360 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001361/* iMac 8: light sensor only, temperature set 13 */
1362 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001363/* iMac 6: light sensor only, temperature set 14 */
1364 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001365/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1366 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001367/* MacPro3,1: temperature set 16 */
1368 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Justin P. Mattocke1741712010-04-14 16:14:10 +02001369/* iMac 9,1: light sensor only, temperature set 17 */
1370 { .accelerometer = 0, .light = 0, .temperature_set = 17 },
1371/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
1372 { .accelerometer = 1, .light = 1, .temperature_set = 18 },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001373/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
1374 { .accelerometer = 1, .light = 1, .temperature_set = 19 },
1375/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
1376 { .accelerometer = 1, .light = 1, .temperature_set = 20 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001377};
1378
1379/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1380 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1381static __initdata struct dmi_system_id applesmc_whitelist[] = {
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001382 { applesmc_dmi_match, "Apple MacBook Air 2", {
1383 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1384 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1385 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001386 { applesmc_dmi_match, "Apple MacBook Air", {
1387 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1388 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001389 &applesmc_dmi_data[7]},
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001390 { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
1391 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1392 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
1393 &applesmc_dmi_data[20]},
1394 { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
1395 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1396 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
1397 &applesmc_dmi_data[19]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001398 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1399 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1400 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1401 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001402 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1403 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1404 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1405 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001406 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1407 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1408 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1409 &applesmc_dmi_data[9]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001410 { applesmc_dmi_match, "Apple MacBook Pro 2,2", {
1411 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Computer, Inc."),
1412 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2") },
1413 &applesmc_dmi_data[18]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001414 { applesmc_dmi_match, "Apple MacBook Pro", {
1415 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1416 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001417 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001418 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001419 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001420 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001421 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001422 { applesmc_dmi_match, "Apple MacBook (v3)", {
1423 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1424 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001425 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001426 { applesmc_dmi_match, "Apple MacBook 4", {
1427 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1428 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1429 &applesmc_dmi_data[6]},
Henrik Rydberg181209a2008-11-06 12:53:20 -08001430 { applesmc_dmi_match, "Apple MacBook 5", {
1431 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1432 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1433 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001434 { applesmc_dmi_match, "Apple MacBook", {
1435 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1436 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001437 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001438 { applesmc_dmi_match, "Apple Macmini", {
1439 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1440 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001441 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001442 { applesmc_dmi_match, "Apple MacPro2", {
1443 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1444 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001445 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001446 { applesmc_dmi_match, "Apple MacPro3", {
1447 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1448 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1449 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001450 { applesmc_dmi_match, "Apple MacPro", {
1451 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1452 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1453 &applesmc_dmi_data[4]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001454 { applesmc_dmi_match, "Apple iMac 9,1", {
1455 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
1456 DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1") },
1457 &applesmc_dmi_data[17]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001458 { applesmc_dmi_match, "Apple iMac 8", {
1459 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1460 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1461 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001462 { applesmc_dmi_match, "Apple iMac 6", {
1463 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1464 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1465 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001466 { applesmc_dmi_match, "Apple iMac 5", {
1467 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1468 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1469 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001470 { applesmc_dmi_match, "Apple iMac", {
1471 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1472 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001473 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001474 { .ident = NULL }
1475};
1476
1477static int __init applesmc_init(void)
1478{
1479 int ret;
1480 int count;
1481 int i;
1482
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001483 if (!dmi_check_system(applesmc_whitelist)) {
1484 printk(KERN_WARNING "applesmc: supported laptop not found!\n");
1485 ret = -ENODEV;
1486 goto out;
1487 }
1488
1489 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1490 "applesmc")) {
1491 ret = -ENXIO;
1492 goto out;
1493 }
1494
1495 ret = platform_driver_register(&applesmc_driver);
1496 if (ret)
1497 goto out_region;
1498
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001499 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1500 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001501 if (IS_ERR(pdev)) {
1502 ret = PTR_ERR(pdev);
1503 goto out_driver;
1504 }
1505
Nicolas Boichatfa744192007-05-23 13:58:13 -07001506 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001507 if (ret)
1508 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001509
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001510 /* Create key enumeration sysfs files */
1511 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1512 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001513 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001514
1515 /* create fan files */
1516 count = applesmc_get_fan_count();
Henrik Rydberg0559a532010-05-11 09:17:47 +02001517 if (count < 0)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001518 printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001519 else
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001520 printk(KERN_INFO "applesmc: %d fans found.\n", count);
1521
Henrik Rydberg0559a532010-05-11 09:17:47 +02001522 if (count > 4) {
1523 count = 4;
1524 printk(KERN_WARNING "applesmc: More than 4 fans found,"
1525 " but at most 4 fans are supported"
1526 " by the driver.\n");
1527 }
1528
1529 while (fans_handled < count) {
1530 ret = sysfs_create_group(&pdev->dev.kobj,
1531 &fan_attribute_groups[fans_handled]);
1532 if (ret)
1533 goto out_fans;
1534 fans_handled++;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001535 }
1536
1537 for (i = 0;
1538 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1539 i++) {
1540 if (temperature_attributes[i] == NULL) {
1541 printk(KERN_ERR "applesmc: More temperature sensors "
1542 "in temperature_sensors_sets (at least %i)"
1543 "than available sysfs files in "
1544 "temperature_attributes (%i), please report "
1545 "this bug.\n", i, i-1);
1546 goto out_temperature;
1547 }
1548 ret = sysfs_create_file(&pdev->dev.kobj,
1549 temperature_attributes[i]);
1550 if (ret)
1551 goto out_temperature;
1552 }
1553
1554 if (applesmc_accelerometer) {
1555 ret = applesmc_create_accelerometer();
1556 if (ret)
1557 goto out_temperature;
1558 }
1559
1560 if (applesmc_light) {
1561 /* Add light sensor file */
1562 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1563 if (ret)
1564 goto out_accelerometer;
1565
1566 /* Create the workqueue */
1567 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1568 if (!applesmc_led_wq) {
1569 ret = -ENOMEM;
1570 goto out_light_sysfs;
1571 }
1572
1573 /* register as a led device */
1574 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1575 if (ret < 0)
1576 goto out_light_wq;
1577 }
1578
Tony Jones1beeffe2007-08-20 13:46:20 -07001579 hwmon_dev = hwmon_device_register(&pdev->dev);
1580 if (IS_ERR(hwmon_dev)) {
1581 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001582 goto out_light_ledclass;
1583 }
1584
1585 printk(KERN_INFO "applesmc: driver successfully loaded.\n");
1586
1587 return 0;
1588
1589out_light_ledclass:
1590 if (applesmc_light)
1591 led_classdev_unregister(&applesmc_backlight);
1592out_light_wq:
1593 if (applesmc_light)
1594 destroy_workqueue(applesmc_led_wq);
1595out_light_sysfs:
1596 if (applesmc_light)
1597 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1598out_accelerometer:
1599 if (applesmc_accelerometer)
1600 applesmc_release_accelerometer();
1601out_temperature:
1602 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001603out_fans:
1604 while (fans_handled)
1605 sysfs_remove_group(&pdev->dev.kobj,
1606 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001607 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001608out_name:
1609 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001610out_device:
1611 platform_device_unregister(pdev);
1612out_driver:
1613 platform_driver_unregister(&applesmc_driver);
1614out_region:
1615 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1616out:
1617 printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
1618 return ret;
1619}
1620
1621static void __exit applesmc_exit(void)
1622{
Tony Jones1beeffe2007-08-20 13:46:20 -07001623 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001624 if (applesmc_light) {
1625 led_classdev_unregister(&applesmc_backlight);
1626 destroy_workqueue(applesmc_led_wq);
1627 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1628 }
1629 if (applesmc_accelerometer)
1630 applesmc_release_accelerometer();
1631 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001632 while (fans_handled)
1633 sysfs_remove_group(&pdev->dev.kobj,
1634 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001635 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001636 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001637 platform_device_unregister(pdev);
1638 platform_driver_unregister(&applesmc_driver);
1639 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1640
1641 printk(KERN_INFO "applesmc: driver unloaded.\n");
1642}
1643
1644module_init(applesmc_init);
1645module_exit(applesmc_exit);
1646
1647MODULE_AUTHOR("Nicolas Boichat");
1648MODULE_DESCRIPTION("Apple SMC");
1649MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001650MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);