blob: f213997adaecd618bcdf4f7a313c94911a934d0a [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
Joe Perches1ee7c712010-11-09 15:15:03 +000029#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070031#include <linux/delay.h>
32#include <linux/platform_device.h>
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040033#include <linux/input-polldev.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070034#include <linux/kernel.h>
Henrik Rydberg58745832010-11-10 10:58:03 +000035#include <linux/slab.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070036#include <linux/module.h>
37#include <linux/timer.h>
38#include <linux/dmi.h>
39#include <linux/mutex.h>
40#include <linux/hwmon-sysfs.h>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020041#include <linux/io.h>
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070042#include <linux/leds.h>
43#include <linux/hwmon.h>
44#include <linux/workqueue.h>
45
46/* data port used by Apple SMC */
47#define APPLESMC_DATA_PORT 0x300
48/* command/status port used by Apple SMC */
49#define APPLESMC_CMD_PORT 0x304
50
51#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
52
53#define APPLESMC_MAX_DATA_LENGTH 32
54
Henrik Rydberg58745832010-11-10 10:58:03 +000055/* wait up to 32 ms for a status change. */
Henrik Rydberg8c9398d2008-10-18 20:27:43 -070056#define APPLESMC_MIN_WAIT 0x0040
57#define APPLESMC_MAX_WAIT 0x8000
58
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070059#define APPLESMC_STATUS_MASK 0x0f
60#define APPLESMC_READ_CMD 0x10
61#define APPLESMC_WRITE_CMD 0x11
62#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
63#define APPLESMC_GET_KEY_TYPE_CMD 0x13
64
65#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */
66
Henrik Rydberg8bd1a122008-10-18 20:27:39 -070067#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */
68#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040069#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070070
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -040071#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070072
73#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */
74#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */
75#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */
76#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */
77
78#define FANS_COUNT "FNum" /* r-o ui8 */
79#define FANS_MANUAL "FS! " /* r-w ui16 */
80#define FAN_ACTUAL_SPEED "F0Ac" /* r-o fpe2 (2 bytes) */
81#define FAN_MIN_SPEED "F0Mn" /* r-o fpe2 (2 bytes) */
82#define FAN_MAX_SPEED "F0Mx" /* r-o fpe2 (2 bytes) */
83#define FAN_SAFE_SPEED "F0Sf" /* r-o fpe2 (2 bytes) */
84#define FAN_TARGET_SPEED "F0Tg" /* r-w fpe2 (2 bytes) */
85#define FAN_POSITION "F0ID" /* r-o char[16] */
86
87/*
88 * Temperature sensors keys (sp78 - 2 bytes).
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070089 */
Bharath Rameshfb9f88e2009-01-29 14:25:24 -080090static const char *temperature_sensors_sets[][41] = {
Martin Szulecki1bed24b2007-07-09 11:41:36 -070091/* Set 0: Macbook Pro */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -070092 { "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H",
93 "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -080094/* Set 1: Macbook2 set */
95 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "TTF0", "Th0H",
96 "Th0S", "Th1H", NULL },
97/* Set 2: Macbook set */
Martin Szulecki1bed24b2007-07-09 11:41:36 -070098 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "Th0H", "Th0S",
99 "Th1H", "Ts0P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -0800100/* Set 3: Macmini set */
René Rebe8de57702007-10-16 14:19:20 -0700101 { "TC0D", "TC0P", NULL },
Riki Oktariantocd19ba12008-02-04 23:41:58 -0800102/* Set 4: Mac Pro (2 x Quad-Core) */
René Rebe8de57702007-10-16 14:19:20 -0700103 { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P",
104 "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "THTG", "TH0P",
105 "TH1P", "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S",
106 "TM1P", "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P",
107 "TM9S", "TN0H", "TS0C", NULL },
Roberto De Ioris9f86f282008-08-15 00:40:30 -0700108/* Set 5: iMac */
109 { "TC0D", "TA0P", "TG0P", "TG0D", "TG0H", "TH0P", "Tm0P", "TO0P",
110 "Tp0C", NULL },
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -0700111/* Set 6: Macbook3 set */
112 { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H",
113 "Th0S", "Th1H", NULL },
Henrik Rydbergf5274c92008-10-18 20:27:40 -0700114/* Set 7: Macbook Air */
115 { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP",
116 "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL },
Henrik Rydbergd7549902008-10-18 20:27:41 -0700117/* Set 8: Macbook Pro 4,1 (Penryn) */
118 { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H",
119 "Th1H", "Th2H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -0700120/* Set 9: Macbook Pro 3,1 (Santa Rosa) */
121 { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P",
122 "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -0800123/* Set 10: iMac 5,1 */
124 { "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL },
Henrik Rydberg181209a12008-11-06 12:53:20 -0800125/* Set 11: Macbook 5,1 */
126 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P",
127 "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL },
Henrik Rydberga6660322008-11-06 12:53:21 -0800128/* Set 12: Macbook Pro 5,1 */
129 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
130 "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0",
131 "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL },
Henrik Rydbergeefc4882008-11-06 12:53:22 -0800132/* Set 13: iMac 8,1 */
133 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
134 "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -0800135/* Set 14: iMac 6,1 */
136 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
137 "TO0P", "Tp0P", NULL },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -0800138/* Set 15: MacBook Air 2,1 */
139 { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0",
140 "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P",
141 "Ts0S", NULL },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -0800142/* Set 16: Mac Pro 3,1 (2 x Quad-Core) */
143 { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P",
144 "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "TH0P", "TH1P",
145 "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", "TM1P",
146 "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", "TM9S",
147 "TN0C", "TN0D", "TN0H", "TS0C", "Tp0C", "Tp1C", "Tv0S", "Tv1S",
148 NULL },
Justin P. Mattocke1741712010-04-14 16:14:10 +0200149/* Set 17: iMac 9,1 */
150 { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TH0P", "TL0P",
151 "TN0D", "TN0H", "TN0P", "TO0P", "Tm0P", "Tp0P", NULL },
152/* Set 18: MacBook Pro 2,2 */
153 { "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "TM0P", "TTF0",
154 "Th0H", "Th1H", "Tm0P", "Ts0P", NULL },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +0200155/* Set 19: Macbook Pro 5,3 */
156 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
157 "TG0F", "TG0H", "TG0P", "TG0T", "TN0D", "TN0P", "TTF0", "Th2H",
158 "Tm0P", "Ts0P", "Ts0S", NULL },
159/* Set 20: MacBook Pro 5,4 */
160 { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TN0D",
161 "TN0P", "TTF0", "Th2H", "Ts0P", "Ts0S", NULL },
Bernhard Froemel872bad52010-05-27 19:58:52 +0200162/* Set 21: MacBook Pro 6,2 */
163 { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", "TC0P", "TC1C", "TG0D",
164 "TG0P", "TG0T", "TMCD", "TP0P", "TPCD", "Th1H", "Th2H", "Tm0P",
165 "Ts0P", "Ts0S", NULL },
Henrik Rydberg405eaa12010-05-27 19:58:53 +0200166/* Set 22: MacBook Pro 7,1 */
167 { "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", "TN0D", "TN0P", "TN0S",
168 "TN1D", "TN1F", "TN1G", "TN1S", "Th1H", "Ts0P", "Ts0S", NULL },
Edgar Hucek132af032010-11-09 15:15:01 +0000169/* Set 23: MacBook Air 3,1 */
170 { "TB0T", "TB1T", "TB2T", "TC0D", "TC0E", "TC0P", "TC1E", "TCZ3",
171 "TCZ4", "TCZ5", "TG0E", "TG1E", "TG2E", "TGZ3", "TGZ4", "TGZ5",
172 "TH0F", "TH0O", "TM0P" },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700173};
174
175/* List of keys used to read/write fan speeds */
176static const char* fan_speed_keys[] = {
177 FAN_ACTUAL_SPEED,
178 FAN_MIN_SPEED,
179 FAN_MAX_SPEED,
180 FAN_SAFE_SPEED,
181 FAN_TARGET_SPEED
182};
183
184#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
185#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
186
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400187#define APPLESMC_POLL_INTERVAL 50 /* msecs */
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700188#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
189#define APPLESMC_INPUT_FLAT 4
190
191#define SENSOR_X 0
192#define SENSOR_Y 1
193#define SENSOR_Z 2
194
195/* Structure to be passed to DMI_MATCH function */
196struct dmi_match_data {
197/* Indicates whether this computer has an accelerometer. */
198 int accelerometer;
199/* Indicates whether this computer has light sensors and keyboard backlight. */
200 int light;
201/* Indicates which temperature sensors set to use. */
202 int temperature_set;
203};
204
Henrik Rydberg58745832010-11-10 10:58:03 +0000205/* AppleSMC entry - cached register information */
206struct applesmc_entry {
207 char key[5]; /* four-letter key code */
208 u8 valid; /* set when entry is successfully read once */
209 u8 len; /* bounded by APPLESMC_MAX_DATA_LENGTH */
210 char type[5]; /* four-letter type code */
211 u8 flags; /* 0x10: func; 0x40: write; 0x80: read */
212};
213
214/* Register lookup and registers common to all SMCs */
215static struct applesmc_registers {
216 struct mutex mutex; /* register read/write mutex */
217 unsigned int key_count; /* number of SMC registers */
218 bool init_complete; /* true when fully initialized */
219 struct applesmc_entry *cache; /* cached key entries */
220} smcreg = {
221 .mutex = __MUTEX_INITIALIZER(smcreg.mutex),
222};
223
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700224static const int debug;
225static struct platform_device *pdev;
226static s16 rest_x;
227static s16 rest_y;
Henrik Rydberga976f152009-09-21 17:04:50 -0700228static u8 backlight_state[2];
229
Tony Jones1beeffe2007-08-20 13:46:20 -0700230static struct device *hwmon_dev;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400231static struct input_polled_dev *applesmc_idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700232
233/* Indicates whether this computer has an accelerometer. */
234static unsigned int applesmc_accelerometer;
235
236/* Indicates whether this computer has light sensors and keyboard backlight. */
237static unsigned int applesmc_light;
238
Henrik Rydberg0559a532010-05-11 09:17:47 +0200239/* The number of fans handled by the driver */
240static unsigned int fans_handled;
241
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700242/* Indicates which temperature sensors set to use. */
243static unsigned int applesmc_temperature_set;
244
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700245/*
246 * Last index written to key_at_index sysfs file, and value to use for all other
247 * key_at_index_* sysfs files.
248 */
249static unsigned int key_at_index;
250
251static struct workqueue_struct *applesmc_led_wq;
252
253/*
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700254 * __wait_status - Wait up to 32ms for the status port to get a certain value
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700255 * (masked with 0x0f), returning zero if the value is obtained. Callers must
256 * hold applesmc_lock.
257 */
258static int __wait_status(u8 val)
259{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700260 int us;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700261
262 val = val & APPLESMC_STATUS_MASK;
263
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700264 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
265 udelay(us);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700266 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700267 return 0;
268 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700269 }
270
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700271 return -EIO;
272}
273
274/*
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700275 * special treatment of command port - on newer macbooks, it seems necessary
276 * to resend the command byte before polling the status again. Callers must
277 * hold applesmc_lock.
278 */
279static int send_command(u8 cmd)
280{
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700281 int us;
282 for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700283 outb(cmd, APPLESMC_CMD_PORT);
Henrik Rydberg8c9398d2008-10-18 20:27:43 -0700284 udelay(us);
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700285 if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
286 return 0;
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700287 }
Henrik Rydberg84d2d7f2008-10-18 20:27:38 -0700288 return -EIO;
289}
290
Henrik Rydberg58745832010-11-10 10:58:03 +0000291static int send_argument(const char *key)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700292{
293 int i;
294
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700295 for (i = 0; i < 4; i++) {
296 outb(key[i], APPLESMC_DATA_PORT);
297 if (__wait_status(0x04))
298 return -EIO;
299 }
Henrik Rydberg58745832010-11-10 10:58:03 +0000300 return 0;
301}
302
303static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
304{
305 int i;
306
307 if (send_command(cmd) || send_argument(key)) {
308 pr_warn("%s: read arg fail\n", key);
309 return -EIO;
310 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700311
312 outb(len, APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700313
314 for (i = 0; i < len; i++) {
Henrik Rydberg58745832010-11-10 10:58:03 +0000315 if (__wait_status(0x05)) {
316 pr_warn("%s: read data fail\n", key);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700317 return -EIO;
Henrik Rydberg58745832010-11-10 10:58:03 +0000318 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700319 buffer[i] = inb(APPLESMC_DATA_PORT);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700320 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700321
322 return 0;
323}
324
Henrik Rydberg58745832010-11-10 10:58:03 +0000325static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700326{
327 int i;
328
Henrik Rydberg58745832010-11-10 10:58:03 +0000329 if (send_command(cmd) || send_argument(key)) {
330 pr_warn("%s: write arg fail\n", key);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700331 return -EIO;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700332 }
333
334 outb(len, APPLESMC_DATA_PORT);
335
336 for (i = 0; i < len; i++) {
Henrik Rydberg58745832010-11-10 10:58:03 +0000337 if (__wait_status(0x04)) {
338 pr_warn("%s: write data fail\n", key);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700339 return -EIO;
Henrik Rydberg58745832010-11-10 10:58:03 +0000340 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700341 outb(buffer[i], APPLESMC_DATA_PORT);
342 }
343
344 return 0;
345}
346
Henrik Rydberg58745832010-11-10 10:58:03 +0000347static int read_register_count(unsigned int *count)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700348{
Henrik Rydberg58745832010-11-10 10:58:03 +0000349 __be32 be;
350 int ret;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700351
Henrik Rydberg58745832010-11-10 10:58:03 +0000352 ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
353 if (ret)
354 return ret;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700355
Henrik Rydberg58745832010-11-10 10:58:03 +0000356 *count = be32_to_cpu(be);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700357 return 0;
358}
359
360/*
Henrik Rydberg58745832010-11-10 10:58:03 +0000361 * Serialized I/O
362 *
363 * Returns zero on success or a negative error on failure.
364 * All functions below are concurrency safe - callers should NOT hold lock.
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700365 */
Henrik Rydberg58745832010-11-10 10:58:03 +0000366
367static int applesmc_read_entry(const struct applesmc_entry *entry,
368 u8 *buf, u8 len)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700369{
Henrik Rydberg58745832010-11-10 10:58:03 +0000370 int ret;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700371
Henrik Rydberg58745832010-11-10 10:58:03 +0000372 if (entry->len != len)
373 return -EINVAL;
374 mutex_lock(&smcreg.mutex);
375 ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len);
376 mutex_unlock(&smcreg.mutex);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700377
Henrik Rydberg58745832010-11-10 10:58:03 +0000378 return ret;
379}
380
381static int applesmc_write_entry(const struct applesmc_entry *entry,
382 const u8 *buf, u8 len)
383{
384 int ret;
385
386 if (entry->len != len)
387 return -EINVAL;
388 mutex_lock(&smcreg.mutex);
389 ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len);
390 mutex_unlock(&smcreg.mutex);
391 return ret;
392}
393
394static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
395{
396 struct applesmc_entry *cache = &smcreg.cache[index];
397 u8 key[4], info[6];
398 __be32 be;
399 int ret = 0;
400
401 if (cache->valid)
402 return cache;
403
404 mutex_lock(&smcreg.mutex);
405
406 if (cache->valid)
407 goto out;
408 be = cpu_to_be32(index);
409 ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
410 if (ret)
411 goto out;
412 ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
413 if (ret)
414 goto out;
415
416 memcpy(cache->key, key, 4);
417 cache->len = info[0];
418 memcpy(cache->type, &info[1], 4);
419 cache->flags = info[5];
420 cache->valid = 1;
421
422out:
423 mutex_unlock(&smcreg.mutex);
424 if (ret)
425 return ERR_PTR(ret);
426 return cache;
427}
428
429static int applesmc_get_lower_bound(unsigned int *lo, const char *key)
430{
431 int begin = 0, end = smcreg.key_count;
432 const struct applesmc_entry *entry;
433
434 while (begin != end) {
435 int middle = begin + (end - begin) / 2;
436 entry = applesmc_get_entry_by_index(middle);
437 if (IS_ERR(entry))
438 return PTR_ERR(entry);
439 if (strcmp(entry->key, key) < 0)
440 begin = middle + 1;
441 else
442 end = middle;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700443 }
444
Henrik Rydberg58745832010-11-10 10:58:03 +0000445 *lo = begin;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700446 return 0;
447}
448
Henrik Rydberg58745832010-11-10 10:58:03 +0000449static int applesmc_get_upper_bound(unsigned int *hi, const char *key)
450{
451 int begin = 0, end = smcreg.key_count;
452 const struct applesmc_entry *entry;
453
454 while (begin != end) {
455 int middle = begin + (end - begin) / 2;
456 entry = applesmc_get_entry_by_index(middle);
457 if (IS_ERR(entry))
458 return PTR_ERR(entry);
459 if (strcmp(key, entry->key) < 0)
460 end = middle;
461 else
462 begin = middle + 1;
463 }
464
465 *hi = begin;
466 return 0;
467}
468
469static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key)
470{
471 int begin, end;
472 int ret;
473
474 ret = applesmc_get_lower_bound(&begin, key);
475 if (ret)
476 return ERR_PTR(ret);
477 ret = applesmc_get_upper_bound(&end, key);
478 if (ret)
479 return ERR_PTR(ret);
480 if (end - begin != 1)
481 return ERR_PTR(-EINVAL);
482
483 return applesmc_get_entry_by_index(begin);
484}
485
486static int applesmc_read_key(const char *key, u8 *buffer, u8 len)
487{
488 const struct applesmc_entry *entry;
489
490 entry = applesmc_get_entry_by_key(key);
491 if (IS_ERR(entry))
492 return PTR_ERR(entry);
493
494 return applesmc_read_entry(entry, buffer, len);
495}
496
497static int applesmc_write_key(const char *key, const u8 *buffer, u8 len)
498{
499 const struct applesmc_entry *entry;
500
501 entry = applesmc_get_entry_by_key(key);
502 if (IS_ERR(entry))
503 return PTR_ERR(entry);
504
505 return applesmc_write_entry(entry, buffer, len);
506}
507
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700508/*
Henrik Rydberg58745832010-11-10 10:58:03 +0000509 * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z).
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700510 */
511static int applesmc_read_motion_sensor(int index, s16* value)
512{
513 u8 buffer[2];
514 int ret;
515
516 switch (index) {
517 case SENSOR_X:
518 ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
519 break;
520 case SENSOR_Y:
521 ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
522 break;
523 case SENSOR_Z:
524 ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
525 break;
526 default:
527 ret = -EINVAL;
528 }
529
530 *value = ((s16)buffer[0] << 8) | buffer[1];
531
532 return ret;
533}
534
535/*
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000536 * applesmc_device_init - initialize the accelerometer. Can sleep.
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700537 */
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000538static void applesmc_device_init(void)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700539{
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000540 int total;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700541 u8 buffer[2];
542
543 if (!applesmc_accelerometer)
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000544 return;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700545
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700546 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700547 if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000548 (buffer[0] != 0x00 || buffer[1] != 0x00))
Henrik Rydberg58745832010-11-10 10:58:03 +0000549 return;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700550 buffer[0] = 0xe0;
551 buffer[1] = 0x00;
552 applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
553 msleep(INIT_WAIT_MSECS);
554 }
555
Joe Perches1ee7c712010-11-09 15:15:03 +0000556 pr_warn("failed to init the device\n");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700557}
558
559/*
Henrik Rydberg58745832010-11-10 10:58:03 +0000560 * applesmc_get_fan_count - get the number of fans.
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700561 */
562static int applesmc_get_fan_count(void)
563{
564 int ret;
565 u8 buffer[1];
566
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700567 ret = applesmc_read_key(FANS_COUNT, buffer, 1);
568
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700569 if (ret)
570 return ret;
571 else
572 return buffer[0];
573}
574
Henrik Rydberg58745832010-11-10 10:58:03 +0000575/*
576 * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent.
577 */
578static int applesmc_init_smcreg_try(void)
579{
580 struct applesmc_registers *s = &smcreg;
581 int ret;
582
583 if (s->init_complete)
584 return 0;
585
586 ret = read_register_count(&s->key_count);
587 if (ret)
588 return ret;
589
590 if (!s->cache)
591 s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
592 if (!s->cache)
593 return -ENOMEM;
594
595 s->init_complete = true;
596
597 pr_info("key=%d\n", s->key_count);
598
599 return 0;
600}
601
602/*
603 * applesmc_init_smcreg - Initialize register cache.
604 *
605 * Retries until initialization is successful, or the operation times out.
606 *
607 */
608static int applesmc_init_smcreg(void)
609{
610 int ms, ret;
611
612 for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) {
613 ret = applesmc_init_smcreg_try();
614 if (!ret) {
615 if (ms)
616 pr_info("init_smcreg() took %d ms\n", ms);
617 return 0;
618 }
619 msleep(INIT_WAIT_MSECS);
620 }
621
622 kfree(smcreg.cache);
623 smcreg.cache = NULL;
624
625 return ret;
626}
627
628static void applesmc_destroy_smcreg(void)
629{
630 kfree(smcreg.cache);
631 smcreg.cache = NULL;
632 smcreg.init_complete = false;
633}
634
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700635/* Device model stuff */
636static int applesmc_probe(struct platform_device *dev)
637{
Henrik Rydberg58745832010-11-10 10:58:03 +0000638 int ret;
639
640 ret = applesmc_init_smcreg();
641 if (ret)
642 return ret;
643
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000644 applesmc_device_init();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700645
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700646 return 0;
647}
648
Henrik Rydberga976f152009-09-21 17:04:50 -0700649/* Synchronize device with memorized backlight state */
650static int applesmc_pm_resume(struct device *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700651{
Henrik Rydberga976f152009-09-21 17:04:50 -0700652 if (applesmc_light)
653 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Henrik Rydberga976f152009-09-21 17:04:50 -0700654 return 0;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700655}
656
Henrik Rydberga976f152009-09-21 17:04:50 -0700657/* Reinitialize device on resume from hibernation */
658static int applesmc_pm_restore(struct device *dev)
659{
Henrik Rydberg2344cd02010-11-09 15:15:02 +0000660 applesmc_device_init();
Henrik Rydberga976f152009-09-21 17:04:50 -0700661 return applesmc_pm_resume(dev);
662}
663
Alexey Dobriyan47145212009-12-14 18:00:08 -0800664static const struct dev_pm_ops applesmc_pm_ops = {
Henrik Rydberga976f152009-09-21 17:04:50 -0700665 .resume = applesmc_pm_resume,
666 .restore = applesmc_pm_restore,
667};
668
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700669static struct platform_driver applesmc_driver = {
670 .probe = applesmc_probe,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700671 .driver = {
672 .name = "applesmc",
673 .owner = THIS_MODULE,
Henrik Rydberga976f152009-09-21 17:04:50 -0700674 .pm = &applesmc_pm_ops,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700675 },
676};
677
678/*
679 * applesmc_calibrate - Set our "resting" values. Callers must
680 * hold applesmc_lock.
681 */
682static void applesmc_calibrate(void)
683{
684 applesmc_read_motion_sensor(SENSOR_X, &rest_x);
685 applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
686 rest_x = -rest_x;
687}
688
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400689static void applesmc_idev_poll(struct input_polled_dev *dev)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700690{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400691 struct input_dev *idev = dev->input;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700692 s16 x, y;
693
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700694 if (applesmc_read_motion_sensor(SENSOR_X, &x))
Henrik Rydberg58745832010-11-10 10:58:03 +0000695 return;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700696 if (applesmc_read_motion_sensor(SENSOR_Y, &y))
Henrik Rydberg58745832010-11-10 10:58:03 +0000697 return;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700698
699 x = -x;
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -0400700 input_report_abs(idev, ABS_X, x - rest_x);
701 input_report_abs(idev, ABS_Y, y - rest_y);
702 input_sync(idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700703}
704
705/* Sysfs Files */
706
Nicolas Boichatfa744192007-05-23 13:58:13 -0700707static ssize_t applesmc_name_show(struct device *dev,
708 struct device_attribute *attr, char *buf)
709{
710 return snprintf(buf, PAGE_SIZE, "applesmc\n");
711}
712
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700713static ssize_t applesmc_position_show(struct device *dev,
714 struct device_attribute *attr, char *buf)
715{
716 int ret;
717 s16 x, y, z;
718
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700719 ret = applesmc_read_motion_sensor(SENSOR_X, &x);
720 if (ret)
721 goto out;
722 ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
723 if (ret)
724 goto out;
725 ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
726 if (ret)
727 goto out;
728
729out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700730 if (ret)
731 return ret;
732 else
733 return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
734}
735
736static ssize_t applesmc_light_show(struct device *dev,
737 struct device_attribute *attr, char *sysfsbuf)
738{
Henrik Rydberg58745832010-11-10 10:58:03 +0000739 const struct applesmc_entry *entry;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700740 static int data_length;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700741 int ret;
742 u8 left = 0, right = 0;
Henrik Rydberg58745832010-11-10 10:58:03 +0000743 u8 buffer[10];
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700744
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700745 if (!data_length) {
Henrik Rydberg58745832010-11-10 10:58:03 +0000746 entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY);
747 if (IS_ERR(entry))
748 return PTR_ERR(entry);
749 if (entry->len > 10)
750 return -ENXIO;
751 data_length = entry->len;
Joe Perches1ee7c712010-11-09 15:15:03 +0000752 pr_info("light sensor data length set to %d\n", data_length);
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700753 }
754
755 ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
Alex Murrayc3d63622009-01-15 13:51:08 -0800756 /* newer macbooks report a single 10-bit bigendian value */
757 if (data_length == 10) {
758 left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
759 goto out;
760 }
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700761 left = buffer[2];
762 if (ret)
763 goto out;
Henrik Rydberg8bd1a122008-10-18 20:27:39 -0700764 ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700765 right = buffer[2];
766
767out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700768 if (ret)
769 return ret;
770 else
771 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
772}
773
Alex Murrayfa5575c2010-05-27 19:58:54 +0200774/* Displays sensor key as label */
775static ssize_t applesmc_show_sensor_label(struct device *dev,
776 struct device_attribute *devattr, char *sysfsbuf)
777{
778 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
779 const char *key =
780 temperature_sensors_sets[applesmc_temperature_set][attr->index];
781
782 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
783}
784
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700785/* Displays degree Celsius * 1000 */
786static ssize_t applesmc_show_temperature(struct device *dev,
787 struct device_attribute *devattr, char *sysfsbuf)
788{
789 int ret;
790 u8 buffer[2];
791 unsigned int temp;
792 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
793 const char* key =
794 temperature_sensors_sets[applesmc_temperature_set][attr->index];
795
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700796 ret = applesmc_read_key(key, buffer, 2);
797 temp = buffer[0]*1000;
798 temp += (buffer[1] >> 6) * 250;
799
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700800 if (ret)
801 return ret;
802 else
803 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
804}
805
806static ssize_t applesmc_show_fan_speed(struct device *dev,
807 struct device_attribute *attr, char *sysfsbuf)
808{
809 int ret;
810 unsigned int speed = 0;
811 char newkey[5];
812 u8 buffer[2];
813 struct sensor_device_attribute_2 *sensor_attr =
814 to_sensor_dev_attr_2(attr);
815
816 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
817 newkey[1] = '0' + sensor_attr->index;
818 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
819 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
820 newkey[4] = 0;
821
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700822 ret = applesmc_read_key(newkey, buffer, 2);
823 speed = ((buffer[0] << 8 | buffer[1]) >> 2);
824
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700825 if (ret)
826 return ret;
827 else
828 return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
829}
830
831static ssize_t applesmc_store_fan_speed(struct device *dev,
832 struct device_attribute *attr,
833 const char *sysfsbuf, size_t count)
834{
835 int ret;
836 u32 speed;
837 char newkey[5];
838 u8 buffer[2];
839 struct sensor_device_attribute_2 *sensor_attr =
840 to_sensor_dev_attr_2(attr);
841
842 speed = simple_strtoul(sysfsbuf, NULL, 10);
843
844 if (speed > 0x4000) /* Bigger than a 14-bit value */
845 return -EINVAL;
846
847 newkey[0] = fan_speed_keys[sensor_attr->nr][0];
848 newkey[1] = '0' + sensor_attr->index;
849 newkey[2] = fan_speed_keys[sensor_attr->nr][2];
850 newkey[3] = fan_speed_keys[sensor_attr->nr][3];
851 newkey[4] = 0;
852
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700853 buffer[0] = (speed >> 6) & 0xff;
854 buffer[1] = (speed << 2) & 0xff;
855 ret = applesmc_write_key(newkey, buffer, 2);
856
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700857 if (ret)
858 return ret;
859 else
860 return count;
861}
862
863static ssize_t applesmc_show_fan_manual(struct device *dev,
864 struct device_attribute *devattr, char *sysfsbuf)
865{
866 int ret;
867 u16 manual = 0;
868 u8 buffer[2];
869 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
870
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700871 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
872 manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
873
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700874 if (ret)
875 return ret;
876 else
877 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
878}
879
880static ssize_t applesmc_store_fan_manual(struct device *dev,
881 struct device_attribute *devattr,
882 const char *sysfsbuf, size_t count)
883{
884 int ret;
885 u8 buffer[2];
886 u32 input;
887 u16 val;
888 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
889
890 input = simple_strtoul(sysfsbuf, NULL, 10);
891
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700892 ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
893 val = (buffer[0] << 8 | buffer[1]);
894 if (ret)
895 goto out;
896
897 if (input)
898 val = val | (0x01 << attr->index);
899 else
900 val = val & ~(0x01 << attr->index);
901
902 buffer[0] = (val >> 8) & 0xFF;
903 buffer[1] = val & 0xFF;
904
905 ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
906
907out:
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700908 if (ret)
909 return ret;
910 else
911 return count;
912}
913
914static ssize_t applesmc_show_fan_position(struct device *dev,
915 struct device_attribute *attr, char *sysfsbuf)
916{
917 int ret;
918 char newkey[5];
919 u8 buffer[17];
920 struct sensor_device_attribute_2 *sensor_attr =
921 to_sensor_dev_attr_2(attr);
922
923 newkey[0] = FAN_POSITION[0];
924 newkey[1] = '0' + sensor_attr->index;
925 newkey[2] = FAN_POSITION[2];
926 newkey[3] = FAN_POSITION[3];
927 newkey[4] = 0;
928
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700929 ret = applesmc_read_key(newkey, buffer, 16);
930 buffer[16] = 0;
931
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700932 if (ret)
933 return ret;
934 else
935 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
936}
937
938static ssize_t applesmc_calibrate_show(struct device *dev,
939 struct device_attribute *attr, char *sysfsbuf)
940{
941 return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
942}
943
944static ssize_t applesmc_calibrate_store(struct device *dev,
945 struct device_attribute *attr, const char *sysfsbuf, size_t count)
946{
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700947 applesmc_calibrate();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700948
949 return count;
950}
951
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700952static void applesmc_backlight_set(struct work_struct *work)
953{
Henrik Rydberga976f152009-09-21 17:04:50 -0700954 applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700955}
956static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
957
958static void applesmc_brightness_set(struct led_classdev *led_cdev,
959 enum led_brightness value)
960{
961 int ret;
962
Henrik Rydberga976f152009-09-21 17:04:50 -0700963 backlight_state[0] = value;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700964 ret = queue_work(applesmc_led_wq, &backlight_work);
965
966 if (debug && (!ret))
967 printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
968}
969
970static ssize_t applesmc_key_count_show(struct device *dev,
971 struct device_attribute *attr, char *sysfsbuf)
972{
973 int ret;
974 u8 buffer[4];
975 u32 count;
976
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700977 ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
978 count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
979 ((u32)buffer[2]<<8) + buffer[3];
980
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700981 if (ret)
982 return ret;
983 else
984 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
985}
986
987static ssize_t applesmc_key_at_index_read_show(struct device *dev,
988 struct device_attribute *attr, char *sysfsbuf)
989{
Henrik Rydberg58745832010-11-10 10:58:03 +0000990 const struct applesmc_entry *entry;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700991 int ret;
992
Henrik Rydberg58745832010-11-10 10:58:03 +0000993 entry = applesmc_get_entry_by_index(key_at_index);
994 if (IS_ERR(entry))
995 return PTR_ERR(entry);
996 ret = applesmc_read_entry(entry, sysfsbuf, entry->len);
997 if (ret)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700998 return ret;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -0700999
Henrik Rydberg58745832010-11-10 10:58:03 +00001000 return entry->len;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001001}
1002
1003static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
1004 struct device_attribute *attr, char *sysfsbuf)
1005{
Henrik Rydberg58745832010-11-10 10:58:03 +00001006 const struct applesmc_entry *entry;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001007
Henrik Rydberg58745832010-11-10 10:58:03 +00001008 entry = applesmc_get_entry_by_index(key_at_index);
1009 if (IS_ERR(entry))
1010 return PTR_ERR(entry);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001011
Henrik Rydberg58745832010-11-10 10:58:03 +00001012 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001013}
1014
1015static ssize_t applesmc_key_at_index_type_show(struct device *dev,
1016 struct device_attribute *attr, char *sysfsbuf)
1017{
Henrik Rydberg58745832010-11-10 10:58:03 +00001018 const struct applesmc_entry *entry;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001019
Henrik Rydberg58745832010-11-10 10:58:03 +00001020 entry = applesmc_get_entry_by_index(key_at_index);
1021 if (IS_ERR(entry))
1022 return PTR_ERR(entry);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001023
Henrik Rydberg58745832010-11-10 10:58:03 +00001024 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001025}
1026
1027static ssize_t applesmc_key_at_index_name_show(struct device *dev,
1028 struct device_attribute *attr, char *sysfsbuf)
1029{
Henrik Rydberg58745832010-11-10 10:58:03 +00001030 const struct applesmc_entry *entry;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001031
Henrik Rydberg58745832010-11-10 10:58:03 +00001032 entry = applesmc_get_entry_by_index(key_at_index);
1033 if (IS_ERR(entry))
1034 return PTR_ERR(entry);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001035
Henrik Rydberg58745832010-11-10 10:58:03 +00001036 return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001037}
1038
1039static ssize_t applesmc_key_at_index_show(struct device *dev,
1040 struct device_attribute *attr, char *sysfsbuf)
1041{
1042 return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
1043}
1044
1045static ssize_t applesmc_key_at_index_store(struct device *dev,
1046 struct device_attribute *attr, const char *sysfsbuf, size_t count)
1047{
Henrik Rydberg58745832010-11-10 10:58:03 +00001048 unsigned long newkey;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001049
Henrik Rydberg58745832010-11-10 10:58:03 +00001050 if (strict_strtoul(sysfsbuf, 10, &newkey) < 0
1051 || newkey >= smcreg.key_count)
1052 return -EINVAL;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001053
Henrik Rydberg58745832010-11-10 10:58:03 +00001054 key_at_index = newkey;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001055 return count;
1056}
1057
1058static struct led_classdev applesmc_backlight = {
Richard Purdie6c152be2007-10-31 15:00:07 +01001059 .name = "smc::kbd_backlight",
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001060 .default_trigger = "nand-disk",
1061 .brightness_set = applesmc_brightness_set,
1062};
1063
Nicolas Boichatfa744192007-05-23 13:58:13 -07001064static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL);
1065
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001066static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
1067static DEVICE_ATTR(calibrate, 0644,
1068 applesmc_calibrate_show, applesmc_calibrate_store);
1069
1070static struct attribute *accelerometer_attributes[] = {
1071 &dev_attr_position.attr,
1072 &dev_attr_calibrate.attr,
1073 NULL
1074};
1075
1076static const struct attribute_group accelerometer_attributes_group =
1077 { .attrs = accelerometer_attributes };
1078
1079static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
1080
1081static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
1082static DEVICE_ATTR(key_at_index, 0644,
1083 applesmc_key_at_index_show, applesmc_key_at_index_store);
1084static DEVICE_ATTR(key_at_index_name, 0444,
1085 applesmc_key_at_index_name_show, NULL);
1086static DEVICE_ATTR(key_at_index_type, 0444,
1087 applesmc_key_at_index_type_show, NULL);
1088static DEVICE_ATTR(key_at_index_data_length, 0444,
1089 applesmc_key_at_index_data_length_show, NULL);
1090static DEVICE_ATTR(key_at_index_data, 0444,
1091 applesmc_key_at_index_read_show, NULL);
1092
1093static struct attribute *key_enumeration_attributes[] = {
1094 &dev_attr_key_count.attr,
1095 &dev_attr_key_at_index.attr,
1096 &dev_attr_key_at_index_name.attr,
1097 &dev_attr_key_at_index_type.attr,
1098 &dev_attr_key_at_index_data_length.attr,
1099 &dev_attr_key_at_index_data.attr,
1100 NULL
1101};
1102
1103static const struct attribute_group key_enumeration_group =
1104 { .attrs = key_enumeration_attributes };
1105
1106/*
1107 * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
1108 * - show actual speed
1109 * - show/store minimum speed
1110 * - show maximum speed
1111 * - show safe speed
1112 * - show/store target speed
1113 * - show/store manual mode
1114 */
1115#define sysfs_fan_speeds_offset(offset) \
1116static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
1117 applesmc_show_fan_speed, NULL, 0, offset-1); \
1118\
1119static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
1120 applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
1121\
1122static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
1123 applesmc_show_fan_speed, NULL, 2, offset-1); \
1124\
1125static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
1126 applesmc_show_fan_speed, NULL, 3, offset-1); \
1127\
1128static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
1129 applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
1130\
1131static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
1132 applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
1133\
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001134static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001135 applesmc_show_fan_position, NULL, offset-1); \
1136\
1137static struct attribute *fan##offset##_attributes[] = { \
1138 &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
1139 &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
1140 &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
1141 &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
1142 &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
1143 &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
Jean Delvareda4e8ca2007-05-08 20:27:05 -07001144 &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001145 NULL \
1146};
1147
1148/*
1149 * Create the needed functions for each fan using the macro defined above
René Rebe8de57702007-10-16 14:19:20 -07001150 * (4 fans are supported)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001151 */
1152sysfs_fan_speeds_offset(1);
1153sysfs_fan_speeds_offset(2);
René Rebe8de57702007-10-16 14:19:20 -07001154sysfs_fan_speeds_offset(3);
1155sysfs_fan_speeds_offset(4);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001156
1157static const struct attribute_group fan_attribute_groups[] = {
1158 { .attrs = fan1_attributes },
René Rebe8de57702007-10-16 14:19:20 -07001159 { .attrs = fan2_attributes },
1160 { .attrs = fan3_attributes },
1161 { .attrs = fan4_attributes },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001162};
1163
1164/*
1165 * Temperature sensors sysfs entries.
1166 */
Alex Murrayfa5575c2010-05-27 19:58:54 +02001167static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO,
1168 applesmc_show_sensor_label, NULL, 0);
1169static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO,
1170 applesmc_show_sensor_label, NULL, 1);
1171static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO,
1172 applesmc_show_sensor_label, NULL, 2);
1173static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO,
1174 applesmc_show_sensor_label, NULL, 3);
1175static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO,
1176 applesmc_show_sensor_label, NULL, 4);
1177static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO,
1178 applesmc_show_sensor_label, NULL, 5);
1179static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO,
1180 applesmc_show_sensor_label, NULL, 6);
1181static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO,
1182 applesmc_show_sensor_label, NULL, 7);
1183static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO,
1184 applesmc_show_sensor_label, NULL, 8);
1185static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO,
1186 applesmc_show_sensor_label, NULL, 9);
1187static SENSOR_DEVICE_ATTR(temp11_label, S_IRUGO,
1188 applesmc_show_sensor_label, NULL, 10);
1189static SENSOR_DEVICE_ATTR(temp12_label, S_IRUGO,
1190 applesmc_show_sensor_label, NULL, 11);
1191static SENSOR_DEVICE_ATTR(temp13_label, S_IRUGO,
1192 applesmc_show_sensor_label, NULL, 12);
1193static SENSOR_DEVICE_ATTR(temp14_label, S_IRUGO,
1194 applesmc_show_sensor_label, NULL, 13);
1195static SENSOR_DEVICE_ATTR(temp15_label, S_IRUGO,
1196 applesmc_show_sensor_label, NULL, 14);
1197static SENSOR_DEVICE_ATTR(temp16_label, S_IRUGO,
1198 applesmc_show_sensor_label, NULL, 15);
1199static SENSOR_DEVICE_ATTR(temp17_label, S_IRUGO,
1200 applesmc_show_sensor_label, NULL, 16);
1201static SENSOR_DEVICE_ATTR(temp18_label, S_IRUGO,
1202 applesmc_show_sensor_label, NULL, 17);
1203static SENSOR_DEVICE_ATTR(temp19_label, S_IRUGO,
1204 applesmc_show_sensor_label, NULL, 18);
1205static SENSOR_DEVICE_ATTR(temp20_label, S_IRUGO,
1206 applesmc_show_sensor_label, NULL, 19);
1207static SENSOR_DEVICE_ATTR(temp21_label, S_IRUGO,
1208 applesmc_show_sensor_label, NULL, 20);
1209static SENSOR_DEVICE_ATTR(temp22_label, S_IRUGO,
1210 applesmc_show_sensor_label, NULL, 21);
1211static SENSOR_DEVICE_ATTR(temp23_label, S_IRUGO,
1212 applesmc_show_sensor_label, NULL, 22);
1213static SENSOR_DEVICE_ATTR(temp24_label, S_IRUGO,
1214 applesmc_show_sensor_label, NULL, 23);
1215static SENSOR_DEVICE_ATTR(temp25_label, S_IRUGO,
1216 applesmc_show_sensor_label, NULL, 24);
1217static SENSOR_DEVICE_ATTR(temp26_label, S_IRUGO,
1218 applesmc_show_sensor_label, NULL, 25);
1219static SENSOR_DEVICE_ATTR(temp27_label, S_IRUGO,
1220 applesmc_show_sensor_label, NULL, 26);
1221static SENSOR_DEVICE_ATTR(temp28_label, S_IRUGO,
1222 applesmc_show_sensor_label, NULL, 27);
1223static SENSOR_DEVICE_ATTR(temp29_label, S_IRUGO,
1224 applesmc_show_sensor_label, NULL, 28);
1225static SENSOR_DEVICE_ATTR(temp30_label, S_IRUGO,
1226 applesmc_show_sensor_label, NULL, 29);
1227static SENSOR_DEVICE_ATTR(temp31_label, S_IRUGO,
1228 applesmc_show_sensor_label, NULL, 30);
1229static SENSOR_DEVICE_ATTR(temp32_label, S_IRUGO,
1230 applesmc_show_sensor_label, NULL, 31);
1231static SENSOR_DEVICE_ATTR(temp33_label, S_IRUGO,
1232 applesmc_show_sensor_label, NULL, 32);
1233static SENSOR_DEVICE_ATTR(temp34_label, S_IRUGO,
1234 applesmc_show_sensor_label, NULL, 33);
1235static SENSOR_DEVICE_ATTR(temp35_label, S_IRUGO,
1236 applesmc_show_sensor_label, NULL, 34);
1237static SENSOR_DEVICE_ATTR(temp36_label, S_IRUGO,
1238 applesmc_show_sensor_label, NULL, 35);
1239static SENSOR_DEVICE_ATTR(temp37_label, S_IRUGO,
1240 applesmc_show_sensor_label, NULL, 36);
1241static SENSOR_DEVICE_ATTR(temp38_label, S_IRUGO,
1242 applesmc_show_sensor_label, NULL, 37);
1243static SENSOR_DEVICE_ATTR(temp39_label, S_IRUGO,
1244 applesmc_show_sensor_label, NULL, 38);
1245static SENSOR_DEVICE_ATTR(temp40_label, S_IRUGO,
1246 applesmc_show_sensor_label, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001247static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
1248 applesmc_show_temperature, NULL, 0);
1249static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
1250 applesmc_show_temperature, NULL, 1);
1251static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO,
1252 applesmc_show_temperature, NULL, 2);
1253static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO,
1254 applesmc_show_temperature, NULL, 3);
1255static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO,
1256 applesmc_show_temperature, NULL, 4);
1257static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO,
1258 applesmc_show_temperature, NULL, 5);
1259static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO,
1260 applesmc_show_temperature, NULL, 6);
1261static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO,
1262 applesmc_show_temperature, NULL, 7);
1263static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO,
1264 applesmc_show_temperature, NULL, 8);
1265static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO,
1266 applesmc_show_temperature, NULL, 9);
1267static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO,
1268 applesmc_show_temperature, NULL, 10);
1269static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO,
1270 applesmc_show_temperature, NULL, 11);
René Rebe8de57702007-10-16 14:19:20 -07001271static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO,
1272 applesmc_show_temperature, NULL, 12);
1273static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO,
1274 applesmc_show_temperature, NULL, 13);
1275static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO,
1276 applesmc_show_temperature, NULL, 14);
1277static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO,
1278 applesmc_show_temperature, NULL, 15);
1279static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO,
1280 applesmc_show_temperature, NULL, 16);
1281static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO,
1282 applesmc_show_temperature, NULL, 17);
1283static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO,
1284 applesmc_show_temperature, NULL, 18);
1285static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO,
1286 applesmc_show_temperature, NULL, 19);
1287static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO,
1288 applesmc_show_temperature, NULL, 20);
1289static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO,
1290 applesmc_show_temperature, NULL, 21);
1291static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO,
1292 applesmc_show_temperature, NULL, 22);
1293static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO,
1294 applesmc_show_temperature, NULL, 23);
1295static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO,
1296 applesmc_show_temperature, NULL, 24);
1297static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO,
1298 applesmc_show_temperature, NULL, 25);
1299static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO,
1300 applesmc_show_temperature, NULL, 26);
1301static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO,
1302 applesmc_show_temperature, NULL, 27);
1303static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO,
1304 applesmc_show_temperature, NULL, 28);
1305static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO,
1306 applesmc_show_temperature, NULL, 29);
1307static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO,
1308 applesmc_show_temperature, NULL, 30);
1309static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO,
1310 applesmc_show_temperature, NULL, 31);
1311static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO,
1312 applesmc_show_temperature, NULL, 32);
1313static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO,
1314 applesmc_show_temperature, NULL, 33);
1315static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO,
1316 applesmc_show_temperature, NULL, 34);
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001317static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO,
1318 applesmc_show_temperature, NULL, 35);
1319static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO,
1320 applesmc_show_temperature, NULL, 36);
1321static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO,
1322 applesmc_show_temperature, NULL, 37);
1323static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
1324 applesmc_show_temperature, NULL, 38);
1325static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
1326 applesmc_show_temperature, NULL, 39);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001327
Alex Murrayfa5575c2010-05-27 19:58:54 +02001328static struct attribute *label_attributes[] = {
1329 &sensor_dev_attr_temp1_label.dev_attr.attr,
1330 &sensor_dev_attr_temp2_label.dev_attr.attr,
1331 &sensor_dev_attr_temp3_label.dev_attr.attr,
1332 &sensor_dev_attr_temp4_label.dev_attr.attr,
1333 &sensor_dev_attr_temp5_label.dev_attr.attr,
1334 &sensor_dev_attr_temp6_label.dev_attr.attr,
1335 &sensor_dev_attr_temp7_label.dev_attr.attr,
1336 &sensor_dev_attr_temp8_label.dev_attr.attr,
1337 &sensor_dev_attr_temp9_label.dev_attr.attr,
1338 &sensor_dev_attr_temp10_label.dev_attr.attr,
1339 &sensor_dev_attr_temp11_label.dev_attr.attr,
1340 &sensor_dev_attr_temp12_label.dev_attr.attr,
1341 &sensor_dev_attr_temp13_label.dev_attr.attr,
1342 &sensor_dev_attr_temp14_label.dev_attr.attr,
1343 &sensor_dev_attr_temp15_label.dev_attr.attr,
1344 &sensor_dev_attr_temp16_label.dev_attr.attr,
1345 &sensor_dev_attr_temp17_label.dev_attr.attr,
1346 &sensor_dev_attr_temp18_label.dev_attr.attr,
1347 &sensor_dev_attr_temp19_label.dev_attr.attr,
1348 &sensor_dev_attr_temp20_label.dev_attr.attr,
1349 &sensor_dev_attr_temp21_label.dev_attr.attr,
1350 &sensor_dev_attr_temp22_label.dev_attr.attr,
1351 &sensor_dev_attr_temp23_label.dev_attr.attr,
1352 &sensor_dev_attr_temp24_label.dev_attr.attr,
1353 &sensor_dev_attr_temp25_label.dev_attr.attr,
1354 &sensor_dev_attr_temp26_label.dev_attr.attr,
1355 &sensor_dev_attr_temp27_label.dev_attr.attr,
1356 &sensor_dev_attr_temp28_label.dev_attr.attr,
1357 &sensor_dev_attr_temp29_label.dev_attr.attr,
1358 &sensor_dev_attr_temp30_label.dev_attr.attr,
1359 &sensor_dev_attr_temp31_label.dev_attr.attr,
1360 &sensor_dev_attr_temp32_label.dev_attr.attr,
1361 &sensor_dev_attr_temp33_label.dev_attr.attr,
1362 &sensor_dev_attr_temp34_label.dev_attr.attr,
1363 &sensor_dev_attr_temp35_label.dev_attr.attr,
1364 &sensor_dev_attr_temp36_label.dev_attr.attr,
1365 &sensor_dev_attr_temp37_label.dev_attr.attr,
1366 &sensor_dev_attr_temp38_label.dev_attr.attr,
1367 &sensor_dev_attr_temp39_label.dev_attr.attr,
1368 &sensor_dev_attr_temp40_label.dev_attr.attr,
1369 NULL
1370};
1371
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001372static struct attribute *temperature_attributes[] = {
1373 &sensor_dev_attr_temp1_input.dev_attr.attr,
1374 &sensor_dev_attr_temp2_input.dev_attr.attr,
1375 &sensor_dev_attr_temp3_input.dev_attr.attr,
1376 &sensor_dev_attr_temp4_input.dev_attr.attr,
1377 &sensor_dev_attr_temp5_input.dev_attr.attr,
1378 &sensor_dev_attr_temp6_input.dev_attr.attr,
1379 &sensor_dev_attr_temp7_input.dev_attr.attr,
1380 &sensor_dev_attr_temp8_input.dev_attr.attr,
1381 &sensor_dev_attr_temp9_input.dev_attr.attr,
1382 &sensor_dev_attr_temp10_input.dev_attr.attr,
1383 &sensor_dev_attr_temp11_input.dev_attr.attr,
1384 &sensor_dev_attr_temp12_input.dev_attr.attr,
René Rebe8de57702007-10-16 14:19:20 -07001385 &sensor_dev_attr_temp13_input.dev_attr.attr,
1386 &sensor_dev_attr_temp14_input.dev_attr.attr,
1387 &sensor_dev_attr_temp15_input.dev_attr.attr,
1388 &sensor_dev_attr_temp16_input.dev_attr.attr,
1389 &sensor_dev_attr_temp17_input.dev_attr.attr,
1390 &sensor_dev_attr_temp18_input.dev_attr.attr,
1391 &sensor_dev_attr_temp19_input.dev_attr.attr,
1392 &sensor_dev_attr_temp20_input.dev_attr.attr,
1393 &sensor_dev_attr_temp21_input.dev_attr.attr,
1394 &sensor_dev_attr_temp22_input.dev_attr.attr,
1395 &sensor_dev_attr_temp23_input.dev_attr.attr,
1396 &sensor_dev_attr_temp24_input.dev_attr.attr,
1397 &sensor_dev_attr_temp25_input.dev_attr.attr,
1398 &sensor_dev_attr_temp26_input.dev_attr.attr,
1399 &sensor_dev_attr_temp27_input.dev_attr.attr,
1400 &sensor_dev_attr_temp28_input.dev_attr.attr,
1401 &sensor_dev_attr_temp29_input.dev_attr.attr,
1402 &sensor_dev_attr_temp30_input.dev_attr.attr,
1403 &sensor_dev_attr_temp31_input.dev_attr.attr,
1404 &sensor_dev_attr_temp32_input.dev_attr.attr,
1405 &sensor_dev_attr_temp33_input.dev_attr.attr,
1406 &sensor_dev_attr_temp34_input.dev_attr.attr,
1407 &sensor_dev_attr_temp35_input.dev_attr.attr,
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001408 &sensor_dev_attr_temp36_input.dev_attr.attr,
1409 &sensor_dev_attr_temp37_input.dev_attr.attr,
1410 &sensor_dev_attr_temp38_input.dev_attr.attr,
1411 &sensor_dev_attr_temp39_input.dev_attr.attr,
1412 &sensor_dev_attr_temp40_input.dev_attr.attr,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001413 NULL
1414};
1415
1416static const struct attribute_group temperature_attributes_group =
1417 { .attrs = temperature_attributes };
1418
Alex Murrayfa5575c2010-05-27 19:58:54 +02001419static const struct attribute_group label_attributes_group = {
1420 .attrs = label_attributes
1421};
1422
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001423/* Module stuff */
1424
1425/*
1426 * applesmc_dmi_match - found a match. return one, short-circuiting the hunt.
1427 */
Jeff Garzik18552562007-10-03 15:15:40 -04001428static int applesmc_dmi_match(const struct dmi_system_id *id)
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001429{
1430 int i = 0;
1431 struct dmi_match_data* dmi_data = id->driver_data;
Joe Perches1ee7c712010-11-09 15:15:03 +00001432 pr_info("%s detected:\n", id->ident);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001433 applesmc_accelerometer = dmi_data->accelerometer;
Joe Perches1ee7c712010-11-09 15:15:03 +00001434 pr_info(" - Model %s accelerometer\n",
1435 applesmc_accelerometer ? "with" : "without");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001436 applesmc_light = dmi_data->light;
Joe Perches1ee7c712010-11-09 15:15:03 +00001437 pr_info(" - Model %s light sensors and backlight\n",
1438 applesmc_light ? "with" : "without");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001439
1440 applesmc_temperature_set = dmi_data->temperature_set;
1441 while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
1442 i++;
Joe Perches1ee7c712010-11-09 15:15:03 +00001443 pr_info(" - Model with %d temperature sensors\n", i);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001444 return 1;
1445}
1446
1447/* Create accelerometer ressources */
1448static int applesmc_create_accelerometer(void)
1449{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001450 struct input_dev *idev;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001451 int ret;
1452
1453 ret = sysfs_create_group(&pdev->dev.kobj,
1454 &accelerometer_attributes_group);
1455 if (ret)
1456 goto out;
1457
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001458 applesmc_idev = input_allocate_polled_device();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001459 if (!applesmc_idev) {
1460 ret = -ENOMEM;
1461 goto out_sysfs;
1462 }
1463
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001464 applesmc_idev->poll = applesmc_idev_poll;
1465 applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
1466
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001467 /* initial calibrate for the input device */
1468 applesmc_calibrate();
1469
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001470 /* initialize the input device */
1471 idev = applesmc_idev->input;
1472 idev->name = "applesmc";
1473 idev->id.bustype = BUS_HOST;
1474 idev->dev.parent = &pdev->dev;
Jiri Slaby7b19ada2007-10-18 23:40:32 -07001475 idev->evbit[0] = BIT_MASK(EV_ABS);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001476 input_set_abs_params(idev, ABS_X,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001477 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001478 input_set_abs_params(idev, ABS_Y,
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001479 -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
1480
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001481 ret = input_register_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001482 if (ret)
1483 goto out_idev;
1484
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001485 return 0;
1486
1487out_idev:
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001488 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001489
1490out_sysfs:
1491 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1492
1493out:
Joe Perches1ee7c712010-11-09 15:15:03 +00001494 pr_warn("driver init failed (ret=%d)!\n", ret);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001495 return ret;
1496}
1497
1498/* Release all ressources used by the accelerometer */
1499static void applesmc_release_accelerometer(void)
1500{
Dmitry Torokhovd5cf2b92007-09-26 00:01:35 -04001501 input_unregister_polled_device(applesmc_idev);
1502 input_free_polled_device(applesmc_idev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001503 sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
1504}
1505
1506static __initdata struct dmi_match_data applesmc_dmi_data[] = {
1507/* MacBook Pro: accelerometer, backlight and temperature set 0 */
1508 { .accelerometer = 1, .light = 1, .temperature_set = 0 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001509/* MacBook2: accelerometer and temperature set 1 */
Martin Szulecki1bed24b2007-07-09 11:41:36 -07001510 { .accelerometer = 1, .light = 0, .temperature_set = 1 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001511/* MacBook: accelerometer and temperature set 2 */
1512 { .accelerometer = 1, .light = 0, .temperature_set = 2 },
1513/* MacMini: temperature set 3 */
René Rebe8de57702007-10-16 14:19:20 -07001514 { .accelerometer = 0, .light = 0, .temperature_set = 3 },
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001515/* MacPro: temperature set 4 */
1516 { .accelerometer = 0, .light = 0, .temperature_set = 4 },
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001517/* iMac: temperature set 5 */
1518 { .accelerometer = 0, .light = 0, .temperature_set = 5 },
Henrik Rydberg468cc032008-11-12 13:24:58 -08001519/* MacBook3, MacBook4: accelerometer and temperature set 6 */
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001520 { .accelerometer = 1, .light = 0, .temperature_set = 6 },
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001521/* MacBook Air: accelerometer, backlight and temperature set 7 */
1522 { .accelerometer = 1, .light = 1, .temperature_set = 7 },
Henrik Rydbergd7549902008-10-18 20:27:41 -07001523/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */
1524 { .accelerometer = 1, .light = 1, .temperature_set = 8 },
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001525/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
1526 { .accelerometer = 1, .light = 1, .temperature_set = 9 },
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001527/* iMac 5: light sensor only, temperature set 10 */
1528 { .accelerometer = 0, .light = 0, .temperature_set = 10 },
Henrik Rydberg181209a12008-11-06 12:53:20 -08001529/* MacBook 5: accelerometer, backlight and temperature set 11 */
1530 { .accelerometer = 1, .light = 1, .temperature_set = 11 },
Henrik Rydberga6660322008-11-06 12:53:21 -08001531/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
1532 { .accelerometer = 1, .light = 1, .temperature_set = 12 },
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001533/* iMac 8: light sensor only, temperature set 13 */
1534 { .accelerometer = 0, .light = 0, .temperature_set = 13 },
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001535/* iMac 6: light sensor only, temperature set 14 */
1536 { .accelerometer = 0, .light = 0, .temperature_set = 14 },
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001537/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */
1538 { .accelerometer = 1, .light = 1, .temperature_set = 15 },
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001539/* MacPro3,1: temperature set 16 */
1540 { .accelerometer = 0, .light = 0, .temperature_set = 16 },
Justin P. Mattocke1741712010-04-14 16:14:10 +02001541/* iMac 9,1: light sensor only, temperature set 17 */
1542 { .accelerometer = 0, .light = 0, .temperature_set = 17 },
1543/* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
1544 { .accelerometer = 1, .light = 1, .temperature_set = 18 },
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001545/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
1546 { .accelerometer = 1, .light = 1, .temperature_set = 19 },
1547/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
1548 { .accelerometer = 1, .light = 1, .temperature_set = 20 },
Bernhard Froemel872bad52010-05-27 19:58:52 +02001549/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
1550 { .accelerometer = 1, .light = 1, .temperature_set = 21 },
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001551/* MacBook Pro 7,1: accelerometer, backlight and temperature set 22 */
1552 { .accelerometer = 1, .light = 1, .temperature_set = 22 },
Edgar Hucek132af032010-11-09 15:15:01 +00001553/* MacBook Air 3,1: accelerometer, backlight and temperature set 23 */
1554 { .accelerometer = 0, .light = 0, .temperature_set = 23 },
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001555};
1556
1557/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
1558 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
1559static __initdata struct dmi_system_id applesmc_whitelist[] = {
Edgar Hucek132af032010-11-09 15:15:01 +00001560 { applesmc_dmi_match, "Apple MacBook Air 3", {
1561 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1562 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3") },
1563 &applesmc_dmi_data[23]},
Henrik Rydberg85e0e5a2009-01-06 14:41:36 -08001564 { applesmc_dmi_match, "Apple MacBook Air 2", {
1565 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1566 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") },
1567 &applesmc_dmi_data[15]},
Henrik Rydbergf5274c92008-10-18 20:27:40 -07001568 { applesmc_dmi_match, "Apple MacBook Air", {
1569 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1570 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001571 &applesmc_dmi_data[7]},
Henrik Rydberg405eaa12010-05-27 19:58:53 +02001572 { applesmc_dmi_match, "Apple MacBook Pro 7", {
1573 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1574 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro7") },
1575 &applesmc_dmi_data[22]},
Henrik Rydberg4e4a99d2010-05-27 19:58:50 +02001576 { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
1577 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1578 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
1579 &applesmc_dmi_data[20]},
1580 { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
1581 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1582 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
1583 &applesmc_dmi_data[19]},
Bernhard Froemel872bad52010-05-27 19:58:52 +02001584 { applesmc_dmi_match, "Apple MacBook Pro 6", {
1585 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1586 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
1587 &applesmc_dmi_data[21]},
Henrik Rydberga6660322008-11-06 12:53:21 -08001588 { applesmc_dmi_match, "Apple MacBook Pro 5", {
1589 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1590 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
1591 &applesmc_dmi_data[12]},
Henrik Rydbergd7549902008-10-18 20:27:41 -07001592 { applesmc_dmi_match, "Apple MacBook Pro 4", {
1593 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1594 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
1595 &applesmc_dmi_data[8]},
Henrik Rydberg07e8dbd2008-10-18 20:27:42 -07001596 { applesmc_dmi_match, "Apple MacBook Pro 3", {
1597 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1598 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") },
1599 &applesmc_dmi_data[9]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001600 { applesmc_dmi_match, "Apple MacBook Pro 2,2", {
1601 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Computer, Inc."),
1602 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2") },
1603 &applesmc_dmi_data[18]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001604 { applesmc_dmi_match, "Apple MacBook Pro", {
1605 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1606 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001607 &applesmc_dmi_data[0]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001608 { applesmc_dmi_match, "Apple MacBook (v2)", {
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001609 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001610 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001611 &applesmc_dmi_data[1]},
Guilherme M. Schroederf91a79f2008-08-15 00:40:32 -07001612 { applesmc_dmi_match, "Apple MacBook (v3)", {
1613 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1614 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001615 &applesmc_dmi_data[6]},
Henrik Rydberg468cc032008-11-12 13:24:58 -08001616 { applesmc_dmi_match, "Apple MacBook 4", {
1617 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1618 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") },
1619 &applesmc_dmi_data[6]},
Henrik Rydberg181209a12008-11-06 12:53:20 -08001620 { applesmc_dmi_match, "Apple MacBook 5", {
1621 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1622 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
1623 &applesmc_dmi_data[11]},
Riki Oktariantocd19ba12008-02-04 23:41:58 -08001624 { applesmc_dmi_match, "Apple MacBook", {
1625 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1626 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001627 &applesmc_dmi_data[2]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001628 { applesmc_dmi_match, "Apple Macmini", {
1629 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1630 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001631 &applesmc_dmi_data[3]},
René Rebe8de57702007-10-16 14:19:20 -07001632 { applesmc_dmi_match, "Apple MacPro2", {
1633 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1634 DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001635 &applesmc_dmi_data[4]},
Bharath Rameshfb9f88e2009-01-29 14:25:24 -08001636 { applesmc_dmi_match, "Apple MacPro3", {
1637 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1638 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") },
1639 &applesmc_dmi_data[16]},
Henrik Rydberg45a3a362008-11-19 15:36:42 -08001640 { applesmc_dmi_match, "Apple MacPro", {
1641 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1642 DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
1643 &applesmc_dmi_data[4]},
Justin P. Mattocke1741712010-04-14 16:14:10 +02001644 { applesmc_dmi_match, "Apple iMac 9,1", {
1645 DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
1646 DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1") },
1647 &applesmc_dmi_data[17]},
Henrik Rydbergeefc4882008-11-06 12:53:22 -08001648 { applesmc_dmi_match, "Apple iMac 8", {
1649 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1650 DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
1651 &applesmc_dmi_data[13]},
Henrik Rydberg9ca791b2008-11-19 15:36:06 -08001652 { applesmc_dmi_match, "Apple iMac 6", {
1653 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1654 DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") },
1655 &applesmc_dmi_data[14]},
Henrik Rydberg6e3530f2008-11-06 12:53:19 -08001656 { applesmc_dmi_match, "Apple iMac 5", {
1657 DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
1658 DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
1659 &applesmc_dmi_data[10]},
Roberto De Ioris9f86f282008-08-15 00:40:30 -07001660 { applesmc_dmi_match, "Apple iMac", {
1661 DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
1662 DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
Andrew Morton7b5e3cb2008-10-18 20:27:41 -07001663 &applesmc_dmi_data[5]},
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001664 { .ident = NULL }
1665};
1666
1667static int __init applesmc_init(void)
1668{
1669 int ret;
1670 int count;
1671 int i;
1672
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001673 if (!dmi_check_system(applesmc_whitelist)) {
Joe Perches1ee7c712010-11-09 15:15:03 +00001674 pr_warn("supported laptop not found!\n");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001675 ret = -ENODEV;
1676 goto out;
1677 }
1678
1679 if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
1680 "applesmc")) {
1681 ret = -ENXIO;
1682 goto out;
1683 }
1684
1685 ret = platform_driver_register(&applesmc_driver);
1686 if (ret)
1687 goto out_region;
1688
Jean Delvareddfbf2a2007-05-08 20:27:04 -07001689 pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
1690 NULL, 0);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001691 if (IS_ERR(pdev)) {
1692 ret = PTR_ERR(pdev);
1693 goto out_driver;
1694 }
1695
Henrik Rydberg58745832010-11-10 10:58:03 +00001696 /* create register cache */
1697 ret = applesmc_init_smcreg();
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001698 if (ret)
1699 goto out_device;
Nicolas Boichatfa744192007-05-23 13:58:13 -07001700
Henrik Rydberg58745832010-11-10 10:58:03 +00001701 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr);
1702 if (ret)
1703 goto out_smcreg;
1704
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001705 /* Create key enumeration sysfs files */
1706 ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
1707 if (ret)
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001708 goto out_name;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001709
1710 /* create fan files */
1711 count = applesmc_get_fan_count();
Henrik Rydberg0559a532010-05-11 09:17:47 +02001712 if (count < 0)
Joe Perches1ee7c712010-11-09 15:15:03 +00001713 pr_err("Cannot get the number of fans\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001714 else
Joe Perches1ee7c712010-11-09 15:15:03 +00001715 pr_info("%d fans found\n", count);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001716
Henrik Rydberg0559a532010-05-11 09:17:47 +02001717 if (count > 4) {
1718 count = 4;
Joe Perches1ee7c712010-11-09 15:15:03 +00001719 pr_warn("A maximum of 4 fans are supported by this driver\n");
Henrik Rydberg0559a532010-05-11 09:17:47 +02001720 }
1721
1722 while (fans_handled < count) {
1723 ret = sysfs_create_group(&pdev->dev.kobj,
1724 &fan_attribute_groups[fans_handled]);
1725 if (ret)
1726 goto out_fans;
1727 fans_handled++;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001728 }
1729
1730 for (i = 0;
1731 temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
1732 i++) {
Alex Murrayfa5575c2010-05-27 19:58:54 +02001733 if (temperature_attributes[i] == NULL ||
1734 label_attributes[i] == NULL) {
Joe Perches1ee7c712010-11-09 15:15:03 +00001735 pr_err("More temperature sensors in temperature_sensors_sets (at least %i) than available sysfs files in temperature_attributes (%i), please report this bug\n",
1736 i, i-1);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001737 goto out_temperature;
1738 }
1739 ret = sysfs_create_file(&pdev->dev.kobj,
1740 temperature_attributes[i]);
1741 if (ret)
1742 goto out_temperature;
Alex Murrayfa5575c2010-05-27 19:58:54 +02001743 ret = sysfs_create_file(&pdev->dev.kobj,
1744 label_attributes[i]);
1745 if (ret)
1746 goto out_temperature;
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001747 }
1748
1749 if (applesmc_accelerometer) {
1750 ret = applesmc_create_accelerometer();
1751 if (ret)
1752 goto out_temperature;
1753 }
1754
1755 if (applesmc_light) {
1756 /* Add light sensor file */
1757 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
1758 if (ret)
1759 goto out_accelerometer;
1760
1761 /* Create the workqueue */
1762 applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
1763 if (!applesmc_led_wq) {
1764 ret = -ENOMEM;
1765 goto out_light_sysfs;
1766 }
1767
1768 /* register as a led device */
1769 ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
1770 if (ret < 0)
1771 goto out_light_wq;
1772 }
1773
Tony Jones1beeffe2007-08-20 13:46:20 -07001774 hwmon_dev = hwmon_device_register(&pdev->dev);
1775 if (IS_ERR(hwmon_dev)) {
1776 ret = PTR_ERR(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001777 goto out_light_ledclass;
1778 }
1779
Joe Perches1ee7c712010-11-09 15:15:03 +00001780 pr_info("driver successfully loaded\n");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001781
1782 return 0;
1783
1784out_light_ledclass:
1785 if (applesmc_light)
1786 led_classdev_unregister(&applesmc_backlight);
1787out_light_wq:
1788 if (applesmc_light)
1789 destroy_workqueue(applesmc_led_wq);
1790out_light_sysfs:
1791 if (applesmc_light)
1792 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1793out_accelerometer:
1794 if (applesmc_accelerometer)
1795 applesmc_release_accelerometer();
1796out_temperature:
Alex Murrayfa5575c2010-05-27 19:58:54 +02001797 sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001798 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001799out_fans:
1800 while (fans_handled)
1801 sysfs_remove_group(&pdev->dev.kobj,
1802 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001803 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001804out_name:
1805 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Henrik Rydberg58745832010-11-10 10:58:03 +00001806out_smcreg:
1807 applesmc_destroy_smcreg();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001808out_device:
1809 platform_device_unregister(pdev);
1810out_driver:
1811 platform_driver_unregister(&applesmc_driver);
1812out_region:
1813 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1814out:
Joe Perches1ee7c712010-11-09 15:15:03 +00001815 pr_warn("driver init failed (ret=%d)!\n", ret);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001816 return ret;
1817}
1818
1819static void __exit applesmc_exit(void)
1820{
Tony Jones1beeffe2007-08-20 13:46:20 -07001821 hwmon_device_unregister(hwmon_dev);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001822 if (applesmc_light) {
1823 led_classdev_unregister(&applesmc_backlight);
1824 destroy_workqueue(applesmc_led_wq);
1825 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
1826 }
1827 if (applesmc_accelerometer)
1828 applesmc_release_accelerometer();
Alex Murrayfa5575c2010-05-27 19:58:54 +02001829 sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001830 sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
Henrik Rydberg0559a532010-05-11 09:17:47 +02001831 while (fans_handled)
1832 sysfs_remove_group(&pdev->dev.kobj,
1833 &fan_attribute_groups[--fans_handled]);
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001834 sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
Nicolas Boichat6996abf2007-05-27 22:17:43 +02001835 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr);
Henrik Rydberg58745832010-11-10 10:58:03 +00001836 applesmc_destroy_smcreg();
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001837 platform_device_unregister(pdev);
1838 platform_driver_unregister(&applesmc_driver);
1839 release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
1840
Joe Perches1ee7c712010-11-09 15:15:03 +00001841 pr_info("driver unloaded\n");
Nicolas Boichat6f2fad72007-05-08 00:24:52 -07001842}
1843
1844module_init(applesmc_init);
1845module_exit(applesmc_exit);
1846
1847MODULE_AUTHOR("Nicolas Boichat");
1848MODULE_DESCRIPTION("Apple SMC");
1849MODULE_LICENSE("GPL v2");
Henrik Rydbergdc924ef2008-12-01 13:13:49 -08001850MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);