| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1 | /* | 
 | 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 Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 31 | #include <linux/input-polldev.h> | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 32 | #include <linux/kernel.h> | 
 | 33 | #include <linux/module.h> | 
 | 34 | #include <linux/timer.h> | 
 | 35 | #include <linux/dmi.h> | 
 | 36 | #include <linux/mutex.h> | 
 | 37 | #include <linux/hwmon-sysfs.h> | 
 | 38 | #include <asm/io.h> | 
 | 39 | #include <linux/leds.h> | 
 | 40 | #include <linux/hwmon.h> | 
 | 41 | #include <linux/workqueue.h> | 
 | 42 |  | 
 | 43 | /* data port used by Apple SMC */ | 
 | 44 | #define APPLESMC_DATA_PORT	0x300 | 
 | 45 | /* command/status port used by Apple SMC */ | 
 | 46 | #define APPLESMC_CMD_PORT	0x304 | 
 | 47 |  | 
 | 48 | #define APPLESMC_NR_PORTS	32 /* 0x300-0x31f */ | 
 | 49 |  | 
 | 50 | #define APPLESMC_MAX_DATA_LENGTH 32 | 
 | 51 |  | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 52 | #define APPLESMC_MIN_WAIT	0x0040 | 
 | 53 | #define APPLESMC_MAX_WAIT	0x8000 | 
 | 54 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 55 | #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 Rydberg | 8bd1a12 | 2008-10-18 20:27:39 -0700 | [diff] [blame] | 63 | #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 Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 65 | #define BACKLIGHT_KEY		"LKSB" /* w-o {lkb (2 bytes) */ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 66 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 67 | #define CLAMSHELL_KEY		"MSLD" /* r-o ui8 (unused) */ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 68 |  | 
 | 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 Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 85 |  */ | 
| Bharath Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 86 | static const char *temperature_sensors_sets[][41] = { | 
| Martin Szulecki | 1bed24b | 2007-07-09 11:41:36 -0700 | [diff] [blame] | 87 | /* Set 0: Macbook Pro */ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 88 | 	{ "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H", | 
 | 89 | 	  "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 90 | /* Set 1: Macbook2 set */ | 
 | 91 | 	{ "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "TTF0", "Th0H", | 
 | 92 | 	  "Th0S", "Th1H", NULL }, | 
 | 93 | /* Set 2: Macbook set */ | 
| Martin Szulecki | 1bed24b | 2007-07-09 11:41:36 -0700 | [diff] [blame] | 94 | 	{ "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "Th0H", "Th0S", | 
 | 95 | 	  "Th1H", "Ts0P", NULL }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 96 | /* Set 3: Macmini set */ | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 97 | 	{ "TC0D", "TC0P", NULL }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 98 | /* Set 4: Mac Pro (2 x Quad-Core) */ | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 99 | 	{ "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 Ioris | 9f86f28 | 2008-08-15 00:40:30 -0700 | [diff] [blame] | 104 | /* Set 5: iMac */ | 
 | 105 | 	{ "TC0D", "TA0P", "TG0P", "TG0D", "TG0H", "TH0P", "Tm0P", "TO0P", | 
 | 106 | 	  "Tp0C", NULL }, | 
| Guilherme M. Schroeder | f91a79f | 2008-08-15 00:40:32 -0700 | [diff] [blame] | 107 | /* Set 6: Macbook3 set */ | 
 | 108 | 	{ "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", | 
 | 109 | 	  "Th0S", "Th1H", NULL }, | 
| Henrik Rydberg | f5274c9 | 2008-10-18 20:27:40 -0700 | [diff] [blame] | 110 | /* Set 7: Macbook Air */ | 
 | 111 | 	{ "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", | 
 | 112 | 	  "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, | 
| Henrik Rydberg | d754990 | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 113 | /* Set 8: Macbook Pro 4,1 (Penryn) */ | 
 | 114 | 	{ "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", | 
 | 115 | 	  "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, | 
| Henrik Rydberg | 07e8dbd | 2008-10-18 20:27:42 -0700 | [diff] [blame] | 116 | /* 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 Rydberg | 6e3530f | 2008-11-06 12:53:19 -0800 | [diff] [blame] | 119 | /* Set 10: iMac 5,1 */ | 
 | 120 | 	{ "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL }, | 
| Henrik Rydberg | 181209a | 2008-11-06 12:53:20 -0800 | [diff] [blame] | 121 | /* Set 11: Macbook 5,1 */ | 
 | 122 | 	{ "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P", | 
 | 123 | 	  "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL }, | 
| Henrik Rydberg | a666032 | 2008-11-06 12:53:21 -0800 | [diff] [blame] | 124 | /* 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 Rydberg | eefc488 | 2008-11-06 12:53:22 -0800 | [diff] [blame] | 128 | /* Set 13: iMac 8,1 */ | 
 | 129 | 	{ "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", | 
 | 130 | 	  "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL }, | 
| Henrik Rydberg | 9ca791b | 2008-11-19 15:36:06 -0800 | [diff] [blame] | 131 | /* Set 14: iMac 6,1 */ | 
 | 132 | 	{ "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", | 
 | 133 | 	  "TO0P", "Tp0P", NULL }, | 
| Henrik Rydberg | 85e0e5a | 2009-01-06 14:41:36 -0800 | [diff] [blame] | 134 | /* 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 Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 138 | /* 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 }, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 145 | }; | 
 | 146 |  | 
 | 147 | /* List of keys used to read/write fan speeds */ | 
 | 148 | static const char* fan_speed_keys[] = { | 
 | 149 | 	FAN_ACTUAL_SPEED, | 
 | 150 | 	FAN_MIN_SPEED, | 
 | 151 | 	FAN_MAX_SPEED, | 
 | 152 | 	FAN_SAFE_SPEED, | 
 | 153 | 	FAN_TARGET_SPEED | 
 | 154 | }; | 
 | 155 |  | 
 | 156 | #define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */ | 
 | 157 | #define INIT_WAIT_MSECS		50	/* ... in 50ms increments */ | 
 | 158 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 159 | #define APPLESMC_POLL_INTERVAL	50	/* msecs */ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 160 | #define APPLESMC_INPUT_FUZZ	4	/* input event threshold */ | 
 | 161 | #define APPLESMC_INPUT_FLAT	4 | 
 | 162 |  | 
 | 163 | #define SENSOR_X 0 | 
 | 164 | #define SENSOR_Y 1 | 
 | 165 | #define SENSOR_Z 2 | 
 | 166 |  | 
 | 167 | /* Structure to be passed to DMI_MATCH function */ | 
 | 168 | struct dmi_match_data { | 
 | 169 | /* Indicates whether this computer has an accelerometer. */ | 
 | 170 | 	int accelerometer; | 
 | 171 | /* Indicates whether this computer has light sensors and keyboard backlight. */ | 
 | 172 | 	int light; | 
 | 173 | /* Indicates which temperature sensors set to use. */ | 
 | 174 | 	int temperature_set; | 
 | 175 | }; | 
 | 176 |  | 
 | 177 | static const int debug; | 
 | 178 | static struct platform_device *pdev; | 
 | 179 | static s16 rest_x; | 
 | 180 | static s16 rest_y; | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 181 | static struct device *hwmon_dev; | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 182 | static struct input_polled_dev *applesmc_idev; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 183 |  | 
 | 184 | /* Indicates whether this computer has an accelerometer. */ | 
 | 185 | static unsigned int applesmc_accelerometer; | 
 | 186 |  | 
 | 187 | /* Indicates whether this computer has light sensors and keyboard backlight. */ | 
 | 188 | static unsigned int applesmc_light; | 
 | 189 |  | 
 | 190 | /* Indicates which temperature sensors set to use. */ | 
 | 191 | static unsigned int applesmc_temperature_set; | 
 | 192 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 193 | static DEFINE_MUTEX(applesmc_lock); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 194 |  | 
 | 195 | /* | 
 | 196 |  * Last index written to key_at_index sysfs file, and value to use for all other | 
 | 197 |  * key_at_index_* sysfs files. | 
 | 198 |  */ | 
 | 199 | static unsigned int key_at_index; | 
 | 200 |  | 
 | 201 | static struct workqueue_struct *applesmc_led_wq; | 
 | 202 |  | 
 | 203 | /* | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 204 |  * __wait_status - Wait up to 32ms for the status port to get a certain value | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 205 |  * (masked with 0x0f), returning zero if the value is obtained.  Callers must | 
 | 206 |  * hold applesmc_lock. | 
 | 207 |  */ | 
 | 208 | static int __wait_status(u8 val) | 
 | 209 | { | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 210 | 	int us; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 211 |  | 
 | 212 | 	val = val & APPLESMC_STATUS_MASK; | 
 | 213 |  | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 214 | 	for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { | 
 | 215 | 		udelay(us); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 216 | 		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { | 
 | 217 | 			if (debug) | 
 | 218 | 				printk(KERN_DEBUG | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 219 | 					"Waited %d us for status %x\n", | 
 | 220 | 					2 * us - APPLESMC_MIN_WAIT, val); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 221 | 			return 0; | 
 | 222 | 		} | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 223 | 	} | 
 | 224 |  | 
 | 225 | 	printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", | 
 | 226 | 						val, inb(APPLESMC_CMD_PORT)); | 
 | 227 |  | 
 | 228 | 	return -EIO; | 
 | 229 | } | 
 | 230 |  | 
 | 231 | /* | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 232 |  * special treatment of command port - on newer macbooks, it seems necessary | 
 | 233 |  * to resend the command byte before polling the status again. Callers must | 
 | 234 |  * hold applesmc_lock. | 
 | 235 |  */ | 
 | 236 | static int send_command(u8 cmd) | 
 | 237 | { | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 238 | 	int us; | 
 | 239 | 	for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 240 | 		outb(cmd, APPLESMC_CMD_PORT); | 
| Henrik Rydberg | 8c9398d | 2008-10-18 20:27:43 -0700 | [diff] [blame] | 241 | 		udelay(us); | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 242 | 		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) | 
 | 243 | 			return 0; | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 244 | 	} | 
 | 245 | 	printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", | 
 | 246 | 		cmd, inb(APPLESMC_CMD_PORT)); | 
 | 247 | 	return -EIO; | 
 | 248 | } | 
 | 249 |  | 
 | 250 | /* | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 251 |  * applesmc_read_key - reads len bytes from a given key, and put them in buffer. | 
 | 252 |  * Returns zero on success or a negative error on failure. Callers must | 
 | 253 |  * hold applesmc_lock. | 
 | 254 |  */ | 
 | 255 | static int applesmc_read_key(const char* key, u8* buffer, u8 len) | 
 | 256 | { | 
 | 257 | 	int i; | 
 | 258 |  | 
 | 259 | 	if (len > APPLESMC_MAX_DATA_LENGTH) { | 
 | 260 | 		printk(KERN_ERR	"applesmc_read_key: cannot read more than " | 
 | 261 | 					"%d bytes\n", APPLESMC_MAX_DATA_LENGTH); | 
 | 262 | 		return -EINVAL; | 
 | 263 | 	} | 
 | 264 |  | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 265 | 	if (send_command(APPLESMC_READ_CMD)) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 266 | 		return -EIO; | 
 | 267 |  | 
 | 268 | 	for (i = 0; i < 4; i++) { | 
 | 269 | 		outb(key[i], APPLESMC_DATA_PORT); | 
 | 270 | 		if (__wait_status(0x04)) | 
 | 271 | 			return -EIO; | 
 | 272 | 	} | 
 | 273 | 	if (debug) | 
 | 274 | 		printk(KERN_DEBUG "<%s", key); | 
 | 275 |  | 
 | 276 | 	outb(len, APPLESMC_DATA_PORT); | 
 | 277 | 	if (debug) | 
 | 278 | 		printk(KERN_DEBUG ">%x", len); | 
 | 279 |  | 
 | 280 | 	for (i = 0; i < len; i++) { | 
 | 281 | 		if (__wait_status(0x05)) | 
 | 282 | 			return -EIO; | 
 | 283 | 		buffer[i] = inb(APPLESMC_DATA_PORT); | 
 | 284 | 		if (debug) | 
 | 285 | 			printk(KERN_DEBUG "<%x", buffer[i]); | 
 | 286 | 	} | 
 | 287 | 	if (debug) | 
 | 288 | 		printk(KERN_DEBUG "\n"); | 
 | 289 |  | 
 | 290 | 	return 0; | 
 | 291 | } | 
 | 292 |  | 
 | 293 | /* | 
 | 294 |  * applesmc_write_key - writes len bytes from buffer to a given key. | 
 | 295 |  * Returns zero on success or a negative error on failure. Callers must | 
 | 296 |  * hold applesmc_lock. | 
 | 297 |  */ | 
 | 298 | static int applesmc_write_key(const char* key, u8* buffer, u8 len) | 
 | 299 | { | 
 | 300 | 	int i; | 
 | 301 |  | 
 | 302 | 	if (len > APPLESMC_MAX_DATA_LENGTH) { | 
 | 303 | 		printk(KERN_ERR	"applesmc_write_key: cannot write more than " | 
 | 304 | 					"%d bytes\n", APPLESMC_MAX_DATA_LENGTH); | 
 | 305 | 		return -EINVAL; | 
 | 306 | 	} | 
 | 307 |  | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 308 | 	if (send_command(APPLESMC_WRITE_CMD)) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 309 | 		return -EIO; | 
 | 310 |  | 
 | 311 | 	for (i = 0; i < 4; i++) { | 
 | 312 | 		outb(key[i], APPLESMC_DATA_PORT); | 
 | 313 | 		if (__wait_status(0x04)) | 
 | 314 | 			return -EIO; | 
 | 315 | 	} | 
 | 316 |  | 
 | 317 | 	outb(len, APPLESMC_DATA_PORT); | 
 | 318 |  | 
 | 319 | 	for (i = 0; i < len; i++) { | 
 | 320 | 		if (__wait_status(0x04)) | 
 | 321 | 			return -EIO; | 
 | 322 | 		outb(buffer[i], APPLESMC_DATA_PORT); | 
 | 323 | 	} | 
 | 324 |  | 
 | 325 | 	return 0; | 
 | 326 | } | 
 | 327 |  | 
 | 328 | /* | 
 | 329 |  * applesmc_get_key_at_index - get key at index, and put the result in key | 
 | 330 |  * (char[6]). Returns zero on success or a negative error on failure. Callers | 
 | 331 |  * must hold applesmc_lock. | 
 | 332 |  */ | 
 | 333 | static int applesmc_get_key_at_index(int index, char* key) | 
 | 334 | { | 
 | 335 | 	int i; | 
 | 336 | 	u8 readkey[4]; | 
 | 337 | 	readkey[0] = index >> 24; | 
 | 338 | 	readkey[1] = index >> 16; | 
 | 339 | 	readkey[2] = index >> 8; | 
 | 340 | 	readkey[3] = index; | 
 | 341 |  | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 342 | 	if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD)) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 343 | 		return -EIO; | 
 | 344 |  | 
 | 345 | 	for (i = 0; i < 4; i++) { | 
 | 346 | 		outb(readkey[i], APPLESMC_DATA_PORT); | 
 | 347 | 		if (__wait_status(0x04)) | 
 | 348 | 			return -EIO; | 
 | 349 | 	} | 
 | 350 |  | 
 | 351 | 	outb(4, APPLESMC_DATA_PORT); | 
 | 352 |  | 
 | 353 | 	for (i = 0; i < 4; i++) { | 
 | 354 | 		if (__wait_status(0x05)) | 
 | 355 | 			return -EIO; | 
 | 356 | 		key[i] = inb(APPLESMC_DATA_PORT); | 
 | 357 | 	} | 
 | 358 | 	key[4] = 0; | 
 | 359 |  | 
 | 360 | 	return 0; | 
 | 361 | } | 
 | 362 |  | 
 | 363 | /* | 
 | 364 |  * applesmc_get_key_type - get key type, and put the result in type (char[6]). | 
 | 365 |  * Returns zero on success or a negative error on failure. Callers must | 
 | 366 |  * hold applesmc_lock. | 
 | 367 |  */ | 
 | 368 | static int applesmc_get_key_type(char* key, char* type) | 
 | 369 | { | 
 | 370 | 	int i; | 
 | 371 |  | 
| Henrik Rydberg | 84d2d7f | 2008-10-18 20:27:38 -0700 | [diff] [blame] | 372 | 	if (send_command(APPLESMC_GET_KEY_TYPE_CMD)) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 373 | 		return -EIO; | 
 | 374 |  | 
 | 375 | 	for (i = 0; i < 4; i++) { | 
 | 376 | 		outb(key[i], APPLESMC_DATA_PORT); | 
 | 377 | 		if (__wait_status(0x04)) | 
 | 378 | 			return -EIO; | 
 | 379 | 	} | 
 | 380 |  | 
| Henrik Rydberg | 0522409 | 2008-10-18 20:27:35 -0700 | [diff] [blame] | 381 | 	outb(6, APPLESMC_DATA_PORT); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 382 |  | 
 | 383 | 	for (i = 0; i < 6; i++) { | 
 | 384 | 		if (__wait_status(0x05)) | 
 | 385 | 			return -EIO; | 
 | 386 | 		type[i] = inb(APPLESMC_DATA_PORT); | 
 | 387 | 	} | 
 | 388 | 	type[5] = 0; | 
 | 389 |  | 
 | 390 | 	return 0; | 
 | 391 | } | 
 | 392 |  | 
 | 393 | /* | 
 | 394 |  * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must | 
 | 395 |  * hold applesmc_lock. | 
 | 396 |  */ | 
 | 397 | static int applesmc_read_motion_sensor(int index, s16* value) | 
 | 398 | { | 
 | 399 | 	u8 buffer[2]; | 
 | 400 | 	int ret; | 
 | 401 |  | 
 | 402 | 	switch (index) { | 
 | 403 | 	case SENSOR_X: | 
 | 404 | 		ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2); | 
 | 405 | 		break; | 
 | 406 | 	case SENSOR_Y: | 
 | 407 | 		ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2); | 
 | 408 | 		break; | 
 | 409 | 	case SENSOR_Z: | 
 | 410 | 		ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2); | 
 | 411 | 		break; | 
 | 412 | 	default: | 
 | 413 | 		ret = -EINVAL; | 
 | 414 | 	} | 
 | 415 |  | 
 | 416 | 	*value = ((s16)buffer[0] << 8) | buffer[1]; | 
 | 417 |  | 
 | 418 | 	return ret; | 
 | 419 | } | 
 | 420 |  | 
 | 421 | /* | 
 | 422 |  * applesmc_device_init - initialize the accelerometer.  Returns zero on success | 
 | 423 |  * and negative error code on failure.  Can sleep. | 
 | 424 |  */ | 
 | 425 | static int applesmc_device_init(void) | 
 | 426 | { | 
 | 427 | 	int total, ret = -ENXIO; | 
 | 428 | 	u8 buffer[2]; | 
 | 429 |  | 
 | 430 | 	if (!applesmc_accelerometer) | 
 | 431 | 		return 0; | 
 | 432 |  | 
 | 433 | 	mutex_lock(&applesmc_lock); | 
 | 434 |  | 
 | 435 | 	for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { | 
 | 436 | 		if (debug) | 
 | 437 | 			printk(KERN_DEBUG "applesmc try %d\n", total); | 
 | 438 | 		if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) && | 
 | 439 | 				(buffer[0] != 0x00 || buffer[1] != 0x00)) { | 
 | 440 | 			if (total == INIT_TIMEOUT_MSECS) { | 
 | 441 | 				printk(KERN_DEBUG "applesmc: device has" | 
 | 442 | 						" already been initialized" | 
 | 443 | 						" (0x%02x, 0x%02x).\n", | 
 | 444 | 						buffer[0], buffer[1]); | 
 | 445 | 			} else { | 
 | 446 | 				printk(KERN_DEBUG "applesmc: device" | 
 | 447 | 						" successfully initialized" | 
 | 448 | 						" (0x%02x, 0x%02x).\n", | 
 | 449 | 						buffer[0], buffer[1]); | 
 | 450 | 			} | 
 | 451 | 			ret = 0; | 
 | 452 | 			goto out; | 
 | 453 | 		} | 
 | 454 | 		buffer[0] = 0xe0; | 
 | 455 | 		buffer[1] = 0x00; | 
 | 456 | 		applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2); | 
 | 457 | 		msleep(INIT_WAIT_MSECS); | 
 | 458 | 	} | 
 | 459 |  | 
 | 460 | 	printk(KERN_WARNING "applesmc: failed to init the device\n"); | 
 | 461 |  | 
 | 462 | out: | 
 | 463 | 	mutex_unlock(&applesmc_lock); | 
 | 464 | 	return ret; | 
 | 465 | } | 
 | 466 |  | 
 | 467 | /* | 
 | 468 |  * applesmc_get_fan_count - get the number of fans. Callers must NOT hold | 
 | 469 |  * applesmc_lock. | 
 | 470 |  */ | 
 | 471 | static int applesmc_get_fan_count(void) | 
 | 472 | { | 
 | 473 | 	int ret; | 
 | 474 | 	u8 buffer[1]; | 
 | 475 |  | 
 | 476 | 	mutex_lock(&applesmc_lock); | 
 | 477 |  | 
 | 478 | 	ret = applesmc_read_key(FANS_COUNT, buffer, 1); | 
 | 479 |  | 
 | 480 | 	mutex_unlock(&applesmc_lock); | 
 | 481 | 	if (ret) | 
 | 482 | 		return ret; | 
 | 483 | 	else | 
 | 484 | 		return buffer[0]; | 
 | 485 | } | 
 | 486 |  | 
 | 487 | /* Device model stuff */ | 
 | 488 | static int applesmc_probe(struct platform_device *dev) | 
 | 489 | { | 
 | 490 | 	int ret; | 
 | 491 |  | 
 | 492 | 	ret = applesmc_device_init(); | 
 | 493 | 	if (ret) | 
 | 494 | 		return ret; | 
 | 495 |  | 
 | 496 | 	printk(KERN_INFO "applesmc: device successfully initialized.\n"); | 
 | 497 | 	return 0; | 
 | 498 | } | 
 | 499 |  | 
 | 500 | static int applesmc_resume(struct platform_device *dev) | 
 | 501 | { | 
 | 502 | 	return applesmc_device_init(); | 
 | 503 | } | 
 | 504 |  | 
 | 505 | static struct platform_driver applesmc_driver = { | 
 | 506 | 	.probe = applesmc_probe, | 
 | 507 | 	.resume = applesmc_resume, | 
 | 508 | 	.driver	= { | 
 | 509 | 		.name = "applesmc", | 
 | 510 | 		.owner = THIS_MODULE, | 
 | 511 | 	}, | 
 | 512 | }; | 
 | 513 |  | 
 | 514 | /* | 
 | 515 |  * applesmc_calibrate - Set our "resting" values.  Callers must | 
 | 516 |  * hold applesmc_lock. | 
 | 517 |  */ | 
 | 518 | static void applesmc_calibrate(void) | 
 | 519 | { | 
 | 520 | 	applesmc_read_motion_sensor(SENSOR_X, &rest_x); | 
 | 521 | 	applesmc_read_motion_sensor(SENSOR_Y, &rest_y); | 
 | 522 | 	rest_x = -rest_x; | 
 | 523 | } | 
 | 524 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 525 | static void applesmc_idev_poll(struct input_polled_dev *dev) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 526 | { | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 527 | 	struct input_dev *idev = dev->input; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 528 | 	s16 x, y; | 
 | 529 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 530 | 	mutex_lock(&applesmc_lock); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 531 |  | 
 | 532 | 	if (applesmc_read_motion_sensor(SENSOR_X, &x)) | 
 | 533 | 		goto out; | 
 | 534 | 	if (applesmc_read_motion_sensor(SENSOR_Y, &y)) | 
 | 535 | 		goto out; | 
 | 536 |  | 
 | 537 | 	x = -x; | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 538 | 	input_report_abs(idev, ABS_X, x - rest_x); | 
 | 539 | 	input_report_abs(idev, ABS_Y, y - rest_y); | 
 | 540 | 	input_sync(idev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 541 |  | 
 | 542 | out: | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 543 | 	mutex_unlock(&applesmc_lock); | 
 | 544 | } | 
 | 545 |  | 
 | 546 | /* Sysfs Files */ | 
 | 547 |  | 
| Nicolas Boichat | fa74419 | 2007-05-23 13:58:13 -0700 | [diff] [blame] | 548 | static ssize_t applesmc_name_show(struct device *dev, | 
 | 549 | 				   struct device_attribute *attr, char *buf) | 
 | 550 | { | 
 | 551 | 	return snprintf(buf, PAGE_SIZE, "applesmc\n"); | 
 | 552 | } | 
 | 553 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 554 | static ssize_t applesmc_position_show(struct device *dev, | 
 | 555 | 				   struct device_attribute *attr, char *buf) | 
 | 556 | { | 
 | 557 | 	int ret; | 
 | 558 | 	s16 x, y, z; | 
 | 559 |  | 
 | 560 | 	mutex_lock(&applesmc_lock); | 
 | 561 |  | 
 | 562 | 	ret = applesmc_read_motion_sensor(SENSOR_X, &x); | 
 | 563 | 	if (ret) | 
 | 564 | 		goto out; | 
 | 565 | 	ret = applesmc_read_motion_sensor(SENSOR_Y, &y); | 
 | 566 | 	if (ret) | 
 | 567 | 		goto out; | 
 | 568 | 	ret = applesmc_read_motion_sensor(SENSOR_Z, &z); | 
 | 569 | 	if (ret) | 
 | 570 | 		goto out; | 
 | 571 |  | 
 | 572 | out: | 
 | 573 | 	mutex_unlock(&applesmc_lock); | 
 | 574 | 	if (ret) | 
 | 575 | 		return ret; | 
 | 576 | 	else | 
 | 577 | 		return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z); | 
 | 578 | } | 
 | 579 |  | 
 | 580 | static ssize_t applesmc_light_show(struct device *dev, | 
 | 581 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 582 | { | 
| Henrik Rydberg | 8bd1a12 | 2008-10-18 20:27:39 -0700 | [diff] [blame] | 583 | 	static int data_length; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 584 | 	int ret; | 
 | 585 | 	u8 left = 0, right = 0; | 
| Henrik Rydberg | 8bd1a12 | 2008-10-18 20:27:39 -0700 | [diff] [blame] | 586 | 	u8 buffer[10], query[6]; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 587 |  | 
 | 588 | 	mutex_lock(&applesmc_lock); | 
 | 589 |  | 
| Henrik Rydberg | 8bd1a12 | 2008-10-18 20:27:39 -0700 | [diff] [blame] | 590 | 	if (!data_length) { | 
 | 591 | 		ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query); | 
 | 592 | 		if (ret) | 
 | 593 | 			goto out; | 
 | 594 | 		data_length = clamp_val(query[0], 0, 10); | 
 | 595 | 		printk(KERN_INFO "applesmc: light sensor data length set to " | 
 | 596 | 			"%d\n", data_length); | 
 | 597 | 	} | 
 | 598 |  | 
 | 599 | 	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); | 
| Alex Murray | c3d6362 | 2009-01-15 13:51:08 -0800 | [diff] [blame] | 600 | 	/* newer macbooks report a single 10-bit bigendian value */ | 
 | 601 | 	if (data_length == 10) { | 
 | 602 | 		left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2; | 
 | 603 | 		goto out; | 
 | 604 | 	} | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 605 | 	left = buffer[2]; | 
 | 606 | 	if (ret) | 
 | 607 | 		goto out; | 
| Henrik Rydberg | 8bd1a12 | 2008-10-18 20:27:39 -0700 | [diff] [blame] | 608 | 	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 609 | 	right = buffer[2]; | 
 | 610 |  | 
 | 611 | out: | 
 | 612 | 	mutex_unlock(&applesmc_lock); | 
 | 613 | 	if (ret) | 
 | 614 | 		return ret; | 
 | 615 | 	else | 
 | 616 | 		return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right); | 
 | 617 | } | 
 | 618 |  | 
 | 619 | /* Displays degree Celsius * 1000 */ | 
 | 620 | static ssize_t applesmc_show_temperature(struct device *dev, | 
 | 621 | 			struct device_attribute *devattr, char *sysfsbuf) | 
 | 622 | { | 
 | 623 | 	int ret; | 
 | 624 | 	u8 buffer[2]; | 
 | 625 | 	unsigned int temp; | 
 | 626 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 627 | 	const char* key = | 
 | 628 | 		temperature_sensors_sets[applesmc_temperature_set][attr->index]; | 
 | 629 |  | 
 | 630 | 	mutex_lock(&applesmc_lock); | 
 | 631 |  | 
 | 632 | 	ret = applesmc_read_key(key, buffer, 2); | 
 | 633 | 	temp = buffer[0]*1000; | 
 | 634 | 	temp += (buffer[1] >> 6) * 250; | 
 | 635 |  | 
 | 636 | 	mutex_unlock(&applesmc_lock); | 
 | 637 |  | 
 | 638 | 	if (ret) | 
 | 639 | 		return ret; | 
 | 640 | 	else | 
 | 641 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp); | 
 | 642 | } | 
 | 643 |  | 
 | 644 | static ssize_t applesmc_show_fan_speed(struct device *dev, | 
 | 645 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 646 | { | 
 | 647 | 	int ret; | 
 | 648 | 	unsigned int speed = 0; | 
 | 649 | 	char newkey[5]; | 
 | 650 | 	u8 buffer[2]; | 
 | 651 | 	struct sensor_device_attribute_2 *sensor_attr = | 
 | 652 | 						to_sensor_dev_attr_2(attr); | 
 | 653 |  | 
 | 654 | 	newkey[0] = fan_speed_keys[sensor_attr->nr][0]; | 
 | 655 | 	newkey[1] = '0' + sensor_attr->index; | 
 | 656 | 	newkey[2] = fan_speed_keys[sensor_attr->nr][2]; | 
 | 657 | 	newkey[3] = fan_speed_keys[sensor_attr->nr][3]; | 
 | 658 | 	newkey[4] = 0; | 
 | 659 |  | 
 | 660 | 	mutex_lock(&applesmc_lock); | 
 | 661 |  | 
 | 662 | 	ret = applesmc_read_key(newkey, buffer, 2); | 
 | 663 | 	speed = ((buffer[0] << 8 | buffer[1]) >> 2); | 
 | 664 |  | 
 | 665 | 	mutex_unlock(&applesmc_lock); | 
 | 666 | 	if (ret) | 
 | 667 | 		return ret; | 
 | 668 | 	else | 
 | 669 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed); | 
 | 670 | } | 
 | 671 |  | 
 | 672 | static ssize_t applesmc_store_fan_speed(struct device *dev, | 
 | 673 | 					struct device_attribute *attr, | 
 | 674 | 					const char *sysfsbuf, size_t count) | 
 | 675 | { | 
 | 676 | 	int ret; | 
 | 677 | 	u32 speed; | 
 | 678 | 	char newkey[5]; | 
 | 679 | 	u8 buffer[2]; | 
 | 680 | 	struct sensor_device_attribute_2 *sensor_attr = | 
 | 681 | 						to_sensor_dev_attr_2(attr); | 
 | 682 |  | 
 | 683 | 	speed = simple_strtoul(sysfsbuf, NULL, 10); | 
 | 684 |  | 
 | 685 | 	if (speed > 0x4000) /* Bigger than a 14-bit value */ | 
 | 686 | 		return -EINVAL; | 
 | 687 |  | 
 | 688 | 	newkey[0] = fan_speed_keys[sensor_attr->nr][0]; | 
 | 689 | 	newkey[1] = '0' + sensor_attr->index; | 
 | 690 | 	newkey[2] = fan_speed_keys[sensor_attr->nr][2]; | 
 | 691 | 	newkey[3] = fan_speed_keys[sensor_attr->nr][3]; | 
 | 692 | 	newkey[4] = 0; | 
 | 693 |  | 
 | 694 | 	mutex_lock(&applesmc_lock); | 
 | 695 |  | 
 | 696 | 	buffer[0] = (speed >> 6) & 0xff; | 
 | 697 | 	buffer[1] = (speed << 2) & 0xff; | 
 | 698 | 	ret = applesmc_write_key(newkey, buffer, 2); | 
 | 699 |  | 
 | 700 | 	mutex_unlock(&applesmc_lock); | 
 | 701 | 	if (ret) | 
 | 702 | 		return ret; | 
 | 703 | 	else | 
 | 704 | 		return count; | 
 | 705 | } | 
 | 706 |  | 
 | 707 | static ssize_t applesmc_show_fan_manual(struct device *dev, | 
 | 708 | 			struct device_attribute *devattr, char *sysfsbuf) | 
 | 709 | { | 
 | 710 | 	int ret; | 
 | 711 | 	u16 manual = 0; | 
 | 712 | 	u8 buffer[2]; | 
 | 713 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 714 |  | 
 | 715 | 	mutex_lock(&applesmc_lock); | 
 | 716 |  | 
 | 717 | 	ret = applesmc_read_key(FANS_MANUAL, buffer, 2); | 
 | 718 | 	manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01; | 
 | 719 |  | 
 | 720 | 	mutex_unlock(&applesmc_lock); | 
 | 721 | 	if (ret) | 
 | 722 | 		return ret; | 
 | 723 | 	else | 
 | 724 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual); | 
 | 725 | } | 
 | 726 |  | 
 | 727 | static ssize_t applesmc_store_fan_manual(struct device *dev, | 
 | 728 | 					 struct device_attribute *devattr, | 
 | 729 | 					 const char *sysfsbuf, size_t count) | 
 | 730 | { | 
 | 731 | 	int ret; | 
 | 732 | 	u8 buffer[2]; | 
 | 733 | 	u32 input; | 
 | 734 | 	u16 val; | 
 | 735 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 736 |  | 
 | 737 | 	input = simple_strtoul(sysfsbuf, NULL, 10); | 
 | 738 |  | 
 | 739 | 	mutex_lock(&applesmc_lock); | 
 | 740 |  | 
 | 741 | 	ret = applesmc_read_key(FANS_MANUAL, buffer, 2); | 
 | 742 | 	val = (buffer[0] << 8 | buffer[1]); | 
 | 743 | 	if (ret) | 
 | 744 | 		goto out; | 
 | 745 |  | 
 | 746 | 	if (input) | 
 | 747 | 		val = val | (0x01 << attr->index); | 
 | 748 | 	else | 
 | 749 | 		val = val & ~(0x01 << attr->index); | 
 | 750 |  | 
 | 751 | 	buffer[0] = (val >> 8) & 0xFF; | 
 | 752 | 	buffer[1] = val & 0xFF; | 
 | 753 |  | 
 | 754 | 	ret = applesmc_write_key(FANS_MANUAL, buffer, 2); | 
 | 755 |  | 
 | 756 | out: | 
 | 757 | 	mutex_unlock(&applesmc_lock); | 
 | 758 | 	if (ret) | 
 | 759 | 		return ret; | 
 | 760 | 	else | 
 | 761 | 		return count; | 
 | 762 | } | 
 | 763 |  | 
 | 764 | static ssize_t applesmc_show_fan_position(struct device *dev, | 
 | 765 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 766 | { | 
 | 767 | 	int ret; | 
 | 768 | 	char newkey[5]; | 
 | 769 | 	u8 buffer[17]; | 
 | 770 | 	struct sensor_device_attribute_2 *sensor_attr = | 
 | 771 | 						to_sensor_dev_attr_2(attr); | 
 | 772 |  | 
 | 773 | 	newkey[0] = FAN_POSITION[0]; | 
 | 774 | 	newkey[1] = '0' + sensor_attr->index; | 
 | 775 | 	newkey[2] = FAN_POSITION[2]; | 
 | 776 | 	newkey[3] = FAN_POSITION[3]; | 
 | 777 | 	newkey[4] = 0; | 
 | 778 |  | 
 | 779 | 	mutex_lock(&applesmc_lock); | 
 | 780 |  | 
 | 781 | 	ret = applesmc_read_key(newkey, buffer, 16); | 
 | 782 | 	buffer[16] = 0; | 
 | 783 |  | 
 | 784 | 	mutex_unlock(&applesmc_lock); | 
 | 785 | 	if (ret) | 
 | 786 | 		return ret; | 
 | 787 | 	else | 
 | 788 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4); | 
 | 789 | } | 
 | 790 |  | 
 | 791 | static ssize_t applesmc_calibrate_show(struct device *dev, | 
 | 792 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 793 | { | 
 | 794 | 	return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y); | 
 | 795 | } | 
 | 796 |  | 
 | 797 | static ssize_t applesmc_calibrate_store(struct device *dev, | 
 | 798 | 	struct device_attribute *attr, const char *sysfsbuf, size_t count) | 
 | 799 | { | 
 | 800 | 	mutex_lock(&applesmc_lock); | 
 | 801 | 	applesmc_calibrate(); | 
 | 802 | 	mutex_unlock(&applesmc_lock); | 
 | 803 |  | 
 | 804 | 	return count; | 
 | 805 | } | 
 | 806 |  | 
 | 807 | /* Store the next backlight value to be written by the work */ | 
 | 808 | static unsigned int backlight_value; | 
 | 809 |  | 
 | 810 | static void applesmc_backlight_set(struct work_struct *work) | 
 | 811 | { | 
 | 812 | 	u8 buffer[2]; | 
 | 813 |  | 
 | 814 | 	mutex_lock(&applesmc_lock); | 
 | 815 | 	buffer[0] = backlight_value; | 
 | 816 | 	buffer[1] = 0x00; | 
 | 817 | 	applesmc_write_key(BACKLIGHT_KEY, buffer, 2); | 
 | 818 | 	mutex_unlock(&applesmc_lock); | 
 | 819 | } | 
 | 820 | static DECLARE_WORK(backlight_work, &applesmc_backlight_set); | 
 | 821 |  | 
 | 822 | static void applesmc_brightness_set(struct led_classdev *led_cdev, | 
 | 823 | 						enum led_brightness value) | 
 | 824 | { | 
 | 825 | 	int ret; | 
 | 826 |  | 
 | 827 | 	backlight_value = value; | 
 | 828 | 	ret = queue_work(applesmc_led_wq, &backlight_work); | 
 | 829 |  | 
 | 830 | 	if (debug && (!ret)) | 
 | 831 | 		printk(KERN_DEBUG "applesmc: work was already on the queue.\n"); | 
 | 832 | } | 
 | 833 |  | 
 | 834 | static ssize_t applesmc_key_count_show(struct device *dev, | 
 | 835 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 836 | { | 
 | 837 | 	int ret; | 
 | 838 | 	u8 buffer[4]; | 
 | 839 | 	u32 count; | 
 | 840 |  | 
 | 841 | 	mutex_lock(&applesmc_lock); | 
 | 842 |  | 
 | 843 | 	ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4); | 
 | 844 | 	count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + | 
 | 845 | 						((u32)buffer[2]<<8) + buffer[3]; | 
 | 846 |  | 
 | 847 | 	mutex_unlock(&applesmc_lock); | 
 | 848 | 	if (ret) | 
 | 849 | 		return ret; | 
 | 850 | 	else | 
 | 851 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count); | 
 | 852 | } | 
 | 853 |  | 
 | 854 | static ssize_t applesmc_key_at_index_read_show(struct device *dev, | 
 | 855 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 856 | { | 
 | 857 | 	char key[5]; | 
 | 858 | 	char info[6]; | 
 | 859 | 	int ret; | 
 | 860 |  | 
 | 861 | 	mutex_lock(&applesmc_lock); | 
 | 862 |  | 
 | 863 | 	ret = applesmc_get_key_at_index(key_at_index, key); | 
 | 864 |  | 
 | 865 | 	if (ret || !key[0]) { | 
 | 866 | 		mutex_unlock(&applesmc_lock); | 
 | 867 |  | 
 | 868 | 		return -EINVAL; | 
 | 869 | 	} | 
 | 870 |  | 
 | 871 | 	ret = applesmc_get_key_type(key, info); | 
 | 872 |  | 
 | 873 | 	if (ret) { | 
 | 874 | 		mutex_unlock(&applesmc_lock); | 
 | 875 |  | 
 | 876 | 		return ret; | 
 | 877 | 	} | 
 | 878 |  | 
 | 879 | 	/* | 
 | 880 | 	 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than | 
 | 881 | 	 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf. | 
 | 882 | 	 */ | 
 | 883 | 	ret = applesmc_read_key(key, sysfsbuf, info[0]); | 
 | 884 |  | 
 | 885 | 	mutex_unlock(&applesmc_lock); | 
 | 886 |  | 
 | 887 | 	if (!ret) { | 
 | 888 | 		return info[0]; | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 889 | 	} else { | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 890 | 		return ret; | 
 | 891 | 	} | 
 | 892 | } | 
 | 893 |  | 
 | 894 | static ssize_t applesmc_key_at_index_data_length_show(struct device *dev, | 
 | 895 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 896 | { | 
 | 897 | 	char key[5]; | 
 | 898 | 	char info[6]; | 
 | 899 | 	int ret; | 
 | 900 |  | 
 | 901 | 	mutex_lock(&applesmc_lock); | 
 | 902 |  | 
 | 903 | 	ret = applesmc_get_key_at_index(key_at_index, key); | 
 | 904 |  | 
 | 905 | 	if (ret || !key[0]) { | 
 | 906 | 		mutex_unlock(&applesmc_lock); | 
 | 907 |  | 
 | 908 | 		return -EINVAL; | 
 | 909 | 	} | 
 | 910 |  | 
 | 911 | 	ret = applesmc_get_key_type(key, info); | 
 | 912 |  | 
 | 913 | 	mutex_unlock(&applesmc_lock); | 
 | 914 |  | 
 | 915 | 	if (!ret) | 
 | 916 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]); | 
 | 917 | 	else | 
 | 918 | 		return ret; | 
 | 919 | } | 
 | 920 |  | 
 | 921 | static ssize_t applesmc_key_at_index_type_show(struct device *dev, | 
 | 922 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 923 | { | 
 | 924 | 	char key[5]; | 
 | 925 | 	char info[6]; | 
 | 926 | 	int ret; | 
 | 927 |  | 
 | 928 | 	mutex_lock(&applesmc_lock); | 
 | 929 |  | 
 | 930 | 	ret = applesmc_get_key_at_index(key_at_index, key); | 
 | 931 |  | 
 | 932 | 	if (ret || !key[0]) { | 
 | 933 | 		mutex_unlock(&applesmc_lock); | 
 | 934 |  | 
 | 935 | 		return -EINVAL; | 
 | 936 | 	} | 
 | 937 |  | 
 | 938 | 	ret = applesmc_get_key_type(key, info); | 
 | 939 |  | 
 | 940 | 	mutex_unlock(&applesmc_lock); | 
 | 941 |  | 
 | 942 | 	if (!ret) | 
 | 943 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1); | 
 | 944 | 	else | 
 | 945 | 		return ret; | 
 | 946 | } | 
 | 947 |  | 
 | 948 | static ssize_t applesmc_key_at_index_name_show(struct device *dev, | 
 | 949 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 950 | { | 
 | 951 | 	char key[5]; | 
 | 952 | 	int ret; | 
 | 953 |  | 
 | 954 | 	mutex_lock(&applesmc_lock); | 
 | 955 |  | 
 | 956 | 	ret = applesmc_get_key_at_index(key_at_index, key); | 
 | 957 |  | 
 | 958 | 	mutex_unlock(&applesmc_lock); | 
 | 959 |  | 
 | 960 | 	if (!ret && key[0]) | 
 | 961 | 		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); | 
 | 962 | 	else | 
 | 963 | 		return -EINVAL; | 
 | 964 | } | 
 | 965 |  | 
 | 966 | static ssize_t applesmc_key_at_index_show(struct device *dev, | 
 | 967 | 				struct device_attribute *attr, char *sysfsbuf) | 
 | 968 | { | 
 | 969 | 	return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index); | 
 | 970 | } | 
 | 971 |  | 
 | 972 | static ssize_t applesmc_key_at_index_store(struct device *dev, | 
 | 973 | 	struct device_attribute *attr, const char *sysfsbuf, size_t count) | 
 | 974 | { | 
 | 975 | 	mutex_lock(&applesmc_lock); | 
 | 976 |  | 
 | 977 | 	key_at_index = simple_strtoul(sysfsbuf, NULL, 10); | 
 | 978 |  | 
 | 979 | 	mutex_unlock(&applesmc_lock); | 
 | 980 |  | 
 | 981 | 	return count; | 
 | 982 | } | 
 | 983 |  | 
 | 984 | static struct led_classdev applesmc_backlight = { | 
| Richard Purdie | 6c152be | 2007-10-31 15:00:07 +0100 | [diff] [blame] | 985 | 	.name			= "smc::kbd_backlight", | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 986 | 	.default_trigger	= "nand-disk", | 
 | 987 | 	.brightness_set		= applesmc_brightness_set, | 
 | 988 | }; | 
 | 989 |  | 
| Nicolas Boichat | fa74419 | 2007-05-23 13:58:13 -0700 | [diff] [blame] | 990 | static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL); | 
 | 991 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 992 | static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL); | 
 | 993 | static DEVICE_ATTR(calibrate, 0644, | 
 | 994 | 			applesmc_calibrate_show, applesmc_calibrate_store); | 
 | 995 |  | 
 | 996 | static struct attribute *accelerometer_attributes[] = { | 
 | 997 | 	&dev_attr_position.attr, | 
 | 998 | 	&dev_attr_calibrate.attr, | 
 | 999 | 	NULL | 
 | 1000 | }; | 
 | 1001 |  | 
 | 1002 | static const struct attribute_group accelerometer_attributes_group = | 
 | 1003 | 	{ .attrs = accelerometer_attributes }; | 
 | 1004 |  | 
 | 1005 | static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL); | 
 | 1006 |  | 
 | 1007 | static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL); | 
 | 1008 | static DEVICE_ATTR(key_at_index, 0644, | 
 | 1009 | 		applesmc_key_at_index_show, applesmc_key_at_index_store); | 
 | 1010 | static DEVICE_ATTR(key_at_index_name, 0444, | 
 | 1011 | 					applesmc_key_at_index_name_show, NULL); | 
 | 1012 | static DEVICE_ATTR(key_at_index_type, 0444, | 
 | 1013 | 					applesmc_key_at_index_type_show, NULL); | 
 | 1014 | static DEVICE_ATTR(key_at_index_data_length, 0444, | 
 | 1015 | 				applesmc_key_at_index_data_length_show, NULL); | 
 | 1016 | static DEVICE_ATTR(key_at_index_data, 0444, | 
 | 1017 | 				applesmc_key_at_index_read_show, NULL); | 
 | 1018 |  | 
 | 1019 | static struct attribute *key_enumeration_attributes[] = { | 
 | 1020 | 	&dev_attr_key_count.attr, | 
 | 1021 | 	&dev_attr_key_at_index.attr, | 
 | 1022 | 	&dev_attr_key_at_index_name.attr, | 
 | 1023 | 	&dev_attr_key_at_index_type.attr, | 
 | 1024 | 	&dev_attr_key_at_index_data_length.attr, | 
 | 1025 | 	&dev_attr_key_at_index_data.attr, | 
 | 1026 | 	NULL | 
 | 1027 | }; | 
 | 1028 |  | 
 | 1029 | static const struct attribute_group key_enumeration_group = | 
 | 1030 | 	{ .attrs = key_enumeration_attributes }; | 
 | 1031 |  | 
 | 1032 | /* | 
 | 1033 |  * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries. | 
 | 1034 |  *  - show actual speed | 
 | 1035 |  *  - show/store minimum speed | 
 | 1036 |  *  - show maximum speed | 
 | 1037 |  *  - show safe speed | 
 | 1038 |  *  - show/store target speed | 
 | 1039 |  *  - show/store manual mode | 
 | 1040 |  */ | 
 | 1041 | #define sysfs_fan_speeds_offset(offset) \ | 
 | 1042 | static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \ | 
 | 1043 | 			applesmc_show_fan_speed, NULL, 0, offset-1); \ | 
 | 1044 | \ | 
 | 1045 | static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \ | 
 | 1046 | 	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \ | 
 | 1047 | \ | 
 | 1048 | static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \ | 
 | 1049 | 			applesmc_show_fan_speed, NULL, 2, offset-1); \ | 
 | 1050 | \ | 
 | 1051 | static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \ | 
 | 1052 | 			applesmc_show_fan_speed, NULL, 3, offset-1); \ | 
 | 1053 | \ | 
 | 1054 | static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \ | 
 | 1055 | 	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \ | 
 | 1056 | \ | 
 | 1057 | static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \ | 
 | 1058 | 	applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \ | 
 | 1059 | \ | 
| Jean Delvare | da4e8ca | 2007-05-08 20:27:05 -0700 | [diff] [blame] | 1060 | static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1061 | 	applesmc_show_fan_position, NULL, offset-1); \ | 
 | 1062 | \ | 
 | 1063 | static struct attribute *fan##offset##_attributes[] = { \ | 
 | 1064 | 	&sensor_dev_attr_fan##offset##_input.dev_attr.attr, \ | 
 | 1065 | 	&sensor_dev_attr_fan##offset##_min.dev_attr.attr, \ | 
 | 1066 | 	&sensor_dev_attr_fan##offset##_max.dev_attr.attr, \ | 
 | 1067 | 	&sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \ | 
 | 1068 | 	&sensor_dev_attr_fan##offset##_output.dev_attr.attr, \ | 
 | 1069 | 	&sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \ | 
| Jean Delvare | da4e8ca | 2007-05-08 20:27:05 -0700 | [diff] [blame] | 1070 | 	&sensor_dev_attr_fan##offset##_label.dev_attr.attr, \ | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1071 | 	NULL \ | 
 | 1072 | }; | 
 | 1073 |  | 
 | 1074 | /* | 
 | 1075 |  * Create the needed functions for each fan using the macro defined above | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1076 |  * (4 fans are supported) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1077 |  */ | 
 | 1078 | sysfs_fan_speeds_offset(1); | 
 | 1079 | sysfs_fan_speeds_offset(2); | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1080 | sysfs_fan_speeds_offset(3); | 
 | 1081 | sysfs_fan_speeds_offset(4); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1082 |  | 
 | 1083 | static const struct attribute_group fan_attribute_groups[] = { | 
 | 1084 | 	{ .attrs = fan1_attributes }, | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1085 | 	{ .attrs = fan2_attributes }, | 
 | 1086 | 	{ .attrs = fan3_attributes }, | 
 | 1087 | 	{ .attrs = fan4_attributes }, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1088 | }; | 
 | 1089 |  | 
 | 1090 | /* | 
 | 1091 |  * Temperature sensors sysfs entries. | 
 | 1092 |  */ | 
 | 1093 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, | 
 | 1094 | 					applesmc_show_temperature, NULL, 0); | 
 | 1095 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, | 
 | 1096 | 					applesmc_show_temperature, NULL, 1); | 
 | 1097 | static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, | 
 | 1098 | 					applesmc_show_temperature, NULL, 2); | 
 | 1099 | static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, | 
 | 1100 | 					applesmc_show_temperature, NULL, 3); | 
 | 1101 | static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, | 
 | 1102 | 					applesmc_show_temperature, NULL, 4); | 
 | 1103 | static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, | 
 | 1104 | 					applesmc_show_temperature, NULL, 5); | 
 | 1105 | static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, | 
 | 1106 | 					applesmc_show_temperature, NULL, 6); | 
 | 1107 | static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, | 
 | 1108 | 					applesmc_show_temperature, NULL, 7); | 
 | 1109 | static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, | 
 | 1110 | 					applesmc_show_temperature, NULL, 8); | 
 | 1111 | static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, | 
 | 1112 | 					applesmc_show_temperature, NULL, 9); | 
 | 1113 | static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO, | 
 | 1114 | 					applesmc_show_temperature, NULL, 10); | 
 | 1115 | static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO, | 
 | 1116 | 					applesmc_show_temperature, NULL, 11); | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1117 | static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO, | 
 | 1118 | 					applesmc_show_temperature, NULL, 12); | 
 | 1119 | static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO, | 
 | 1120 | 					applesmc_show_temperature, NULL, 13); | 
 | 1121 | static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO, | 
 | 1122 | 					applesmc_show_temperature, NULL, 14); | 
 | 1123 | static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO, | 
 | 1124 | 					applesmc_show_temperature, NULL, 15); | 
 | 1125 | static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO, | 
 | 1126 | 					applesmc_show_temperature, NULL, 16); | 
 | 1127 | static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO, | 
 | 1128 | 					applesmc_show_temperature, NULL, 17); | 
 | 1129 | static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO, | 
 | 1130 | 					applesmc_show_temperature, NULL, 18); | 
 | 1131 | static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO, | 
 | 1132 | 					applesmc_show_temperature, NULL, 19); | 
 | 1133 | static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO, | 
 | 1134 | 					applesmc_show_temperature, NULL, 20); | 
 | 1135 | static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO, | 
 | 1136 | 					applesmc_show_temperature, NULL, 21); | 
 | 1137 | static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO, | 
 | 1138 | 					applesmc_show_temperature, NULL, 22); | 
 | 1139 | static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO, | 
 | 1140 | 					applesmc_show_temperature, NULL, 23); | 
 | 1141 | static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO, | 
 | 1142 | 					applesmc_show_temperature, NULL, 24); | 
 | 1143 | static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO, | 
 | 1144 | 					applesmc_show_temperature, NULL, 25); | 
 | 1145 | static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO, | 
 | 1146 | 					applesmc_show_temperature, NULL, 26); | 
 | 1147 | static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO, | 
 | 1148 | 					applesmc_show_temperature, NULL, 27); | 
 | 1149 | static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO, | 
 | 1150 | 					applesmc_show_temperature, NULL, 28); | 
 | 1151 | static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO, | 
 | 1152 | 					applesmc_show_temperature, NULL, 29); | 
 | 1153 | static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO, | 
 | 1154 | 					applesmc_show_temperature, NULL, 30); | 
 | 1155 | static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO, | 
 | 1156 | 					applesmc_show_temperature, NULL, 31); | 
 | 1157 | static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO, | 
 | 1158 | 					applesmc_show_temperature, NULL, 32); | 
 | 1159 | static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO, | 
 | 1160 | 					applesmc_show_temperature, NULL, 33); | 
 | 1161 | static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO, | 
 | 1162 | 					applesmc_show_temperature, NULL, 34); | 
| Bharath Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 1163 | static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO, | 
 | 1164 | 					applesmc_show_temperature, NULL, 35); | 
 | 1165 | static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO, | 
 | 1166 | 					applesmc_show_temperature, NULL, 36); | 
 | 1167 | static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO, | 
 | 1168 | 					applesmc_show_temperature, NULL, 37); | 
 | 1169 | static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO, | 
 | 1170 | 					applesmc_show_temperature, NULL, 38); | 
 | 1171 | static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO, | 
 | 1172 | 					applesmc_show_temperature, NULL, 39); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1173 |  | 
 | 1174 | static struct attribute *temperature_attributes[] = { | 
 | 1175 | 	&sensor_dev_attr_temp1_input.dev_attr.attr, | 
 | 1176 | 	&sensor_dev_attr_temp2_input.dev_attr.attr, | 
 | 1177 | 	&sensor_dev_attr_temp3_input.dev_attr.attr, | 
 | 1178 | 	&sensor_dev_attr_temp4_input.dev_attr.attr, | 
 | 1179 | 	&sensor_dev_attr_temp5_input.dev_attr.attr, | 
 | 1180 | 	&sensor_dev_attr_temp6_input.dev_attr.attr, | 
 | 1181 | 	&sensor_dev_attr_temp7_input.dev_attr.attr, | 
 | 1182 | 	&sensor_dev_attr_temp8_input.dev_attr.attr, | 
 | 1183 | 	&sensor_dev_attr_temp9_input.dev_attr.attr, | 
 | 1184 | 	&sensor_dev_attr_temp10_input.dev_attr.attr, | 
 | 1185 | 	&sensor_dev_attr_temp11_input.dev_attr.attr, | 
 | 1186 | 	&sensor_dev_attr_temp12_input.dev_attr.attr, | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1187 | 	&sensor_dev_attr_temp13_input.dev_attr.attr, | 
 | 1188 | 	&sensor_dev_attr_temp14_input.dev_attr.attr, | 
 | 1189 | 	&sensor_dev_attr_temp15_input.dev_attr.attr, | 
 | 1190 | 	&sensor_dev_attr_temp16_input.dev_attr.attr, | 
 | 1191 | 	&sensor_dev_attr_temp17_input.dev_attr.attr, | 
 | 1192 | 	&sensor_dev_attr_temp18_input.dev_attr.attr, | 
 | 1193 | 	&sensor_dev_attr_temp19_input.dev_attr.attr, | 
 | 1194 | 	&sensor_dev_attr_temp20_input.dev_attr.attr, | 
 | 1195 | 	&sensor_dev_attr_temp21_input.dev_attr.attr, | 
 | 1196 | 	&sensor_dev_attr_temp22_input.dev_attr.attr, | 
 | 1197 | 	&sensor_dev_attr_temp23_input.dev_attr.attr, | 
 | 1198 | 	&sensor_dev_attr_temp24_input.dev_attr.attr, | 
 | 1199 | 	&sensor_dev_attr_temp25_input.dev_attr.attr, | 
 | 1200 | 	&sensor_dev_attr_temp26_input.dev_attr.attr, | 
 | 1201 | 	&sensor_dev_attr_temp27_input.dev_attr.attr, | 
 | 1202 | 	&sensor_dev_attr_temp28_input.dev_attr.attr, | 
 | 1203 | 	&sensor_dev_attr_temp29_input.dev_attr.attr, | 
 | 1204 | 	&sensor_dev_attr_temp30_input.dev_attr.attr, | 
 | 1205 | 	&sensor_dev_attr_temp31_input.dev_attr.attr, | 
 | 1206 | 	&sensor_dev_attr_temp32_input.dev_attr.attr, | 
 | 1207 | 	&sensor_dev_attr_temp33_input.dev_attr.attr, | 
 | 1208 | 	&sensor_dev_attr_temp34_input.dev_attr.attr, | 
 | 1209 | 	&sensor_dev_attr_temp35_input.dev_attr.attr, | 
| Bharath Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 1210 | 	&sensor_dev_attr_temp36_input.dev_attr.attr, | 
 | 1211 | 	&sensor_dev_attr_temp37_input.dev_attr.attr, | 
 | 1212 | 	&sensor_dev_attr_temp38_input.dev_attr.attr, | 
 | 1213 | 	&sensor_dev_attr_temp39_input.dev_attr.attr, | 
 | 1214 | 	&sensor_dev_attr_temp40_input.dev_attr.attr, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1215 | 	NULL | 
 | 1216 | }; | 
 | 1217 |  | 
 | 1218 | static const struct attribute_group temperature_attributes_group = | 
 | 1219 | 	{ .attrs = temperature_attributes }; | 
 | 1220 |  | 
 | 1221 | /* Module stuff */ | 
 | 1222 |  | 
 | 1223 | /* | 
 | 1224 |  * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt. | 
 | 1225 |  */ | 
| Jeff Garzik | 1855256 | 2007-10-03 15:15:40 -0400 | [diff] [blame] | 1226 | static int applesmc_dmi_match(const struct dmi_system_id *id) | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1227 | { | 
 | 1228 | 	int i = 0; | 
 | 1229 | 	struct dmi_match_data* dmi_data = id->driver_data; | 
 | 1230 | 	printk(KERN_INFO "applesmc: %s detected:\n", id->ident); | 
 | 1231 | 	applesmc_accelerometer = dmi_data->accelerometer; | 
 | 1232 | 	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n", | 
 | 1233 | 				applesmc_accelerometer ? "with" : "without"); | 
 | 1234 | 	applesmc_light = dmi_data->light; | 
 | 1235 | 	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n", | 
 | 1236 | 					applesmc_light ? "with" : "without"); | 
 | 1237 |  | 
 | 1238 | 	applesmc_temperature_set =  dmi_data->temperature_set; | 
 | 1239 | 	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL) | 
 | 1240 | 		i++; | 
 | 1241 | 	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i); | 
 | 1242 | 	return 1; | 
 | 1243 | } | 
 | 1244 |  | 
 | 1245 | /* Create accelerometer ressources */ | 
 | 1246 | static int applesmc_create_accelerometer(void) | 
 | 1247 | { | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1248 | 	struct input_dev *idev; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1249 | 	int ret; | 
 | 1250 |  | 
 | 1251 | 	ret = sysfs_create_group(&pdev->dev.kobj, | 
 | 1252 | 					&accelerometer_attributes_group); | 
 | 1253 | 	if (ret) | 
 | 1254 | 		goto out; | 
 | 1255 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1256 | 	applesmc_idev = input_allocate_polled_device(); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1257 | 	if (!applesmc_idev) { | 
 | 1258 | 		ret = -ENOMEM; | 
 | 1259 | 		goto out_sysfs; | 
 | 1260 | 	} | 
 | 1261 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1262 | 	applesmc_idev->poll = applesmc_idev_poll; | 
 | 1263 | 	applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL; | 
 | 1264 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1265 | 	/* initial calibrate for the input device */ | 
 | 1266 | 	applesmc_calibrate(); | 
 | 1267 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1268 | 	/* initialize the input device */ | 
 | 1269 | 	idev = applesmc_idev->input; | 
 | 1270 | 	idev->name = "applesmc"; | 
 | 1271 | 	idev->id.bustype = BUS_HOST; | 
 | 1272 | 	idev->dev.parent = &pdev->dev; | 
| Jiri Slaby | 7b19ada | 2007-10-18 23:40:32 -0700 | [diff] [blame] | 1273 | 	idev->evbit[0] = BIT_MASK(EV_ABS); | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1274 | 	input_set_abs_params(idev, ABS_X, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1275 | 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1276 | 	input_set_abs_params(idev, ABS_Y, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1277 | 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); | 
 | 1278 |  | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1279 | 	ret = input_register_polled_device(applesmc_idev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1280 | 	if (ret) | 
 | 1281 | 		goto out_idev; | 
 | 1282 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1283 | 	return 0; | 
 | 1284 |  | 
 | 1285 | out_idev: | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1286 | 	input_free_polled_device(applesmc_idev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1287 |  | 
 | 1288 | out_sysfs: | 
 | 1289 | 	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); | 
 | 1290 |  | 
 | 1291 | out: | 
 | 1292 | 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret); | 
 | 1293 | 	return ret; | 
 | 1294 | } | 
 | 1295 |  | 
 | 1296 | /* Release all ressources used by the accelerometer */ | 
 | 1297 | static void applesmc_release_accelerometer(void) | 
 | 1298 | { | 
| Dmitry Torokhov | d5cf2b9 | 2007-09-26 00:01:35 -0400 | [diff] [blame] | 1299 | 	input_unregister_polled_device(applesmc_idev); | 
 | 1300 | 	input_free_polled_device(applesmc_idev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1301 | 	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); | 
 | 1302 | } | 
 | 1303 |  | 
 | 1304 | static __initdata struct dmi_match_data applesmc_dmi_data[] = { | 
 | 1305 | /* MacBook Pro: accelerometer, backlight and temperature set 0 */ | 
 | 1306 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 0 }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 1307 | /* MacBook2: accelerometer and temperature set 1 */ | 
| Martin Szulecki | 1bed24b | 2007-07-09 11:41:36 -0700 | [diff] [blame] | 1308 | 	{ .accelerometer = 1, .light = 0, .temperature_set = 1 }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 1309 | /* MacBook: accelerometer and temperature set 2 */ | 
 | 1310 | 	{ .accelerometer = 1, .light = 0, .temperature_set = 2 }, | 
 | 1311 | /* MacMini: temperature set 3 */ | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1312 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 3 }, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 1313 | /* MacPro: temperature set 4 */ | 
 | 1314 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 4 }, | 
| Roberto De Ioris | 9f86f28 | 2008-08-15 00:40:30 -0700 | [diff] [blame] | 1315 | /* iMac: temperature set 5 */ | 
 | 1316 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 5 }, | 
| Henrik Rydberg | 468cc03 | 2008-11-12 13:24:58 -0800 | [diff] [blame] | 1317 | /* MacBook3, MacBook4: accelerometer and temperature set 6 */ | 
| Guilherme M. Schroeder | f91a79f | 2008-08-15 00:40:32 -0700 | [diff] [blame] | 1318 | 	{ .accelerometer = 1, .light = 0, .temperature_set = 6 }, | 
| Henrik Rydberg | f5274c9 | 2008-10-18 20:27:40 -0700 | [diff] [blame] | 1319 | /* MacBook Air: accelerometer, backlight and temperature set 7 */ | 
 | 1320 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 7 }, | 
| Henrik Rydberg | d754990 | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1321 | /* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ | 
 | 1322 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 8 }, | 
| Henrik Rydberg | 07e8dbd | 2008-10-18 20:27:42 -0700 | [diff] [blame] | 1323 | /* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ | 
 | 1324 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 9 }, | 
| Henrik Rydberg | 6e3530f | 2008-11-06 12:53:19 -0800 | [diff] [blame] | 1325 | /* iMac 5: light sensor only, temperature set 10 */ | 
 | 1326 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 10 }, | 
| Henrik Rydberg | 181209a | 2008-11-06 12:53:20 -0800 | [diff] [blame] | 1327 | /* MacBook 5: accelerometer, backlight and temperature set 11 */ | 
 | 1328 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 11 }, | 
| Henrik Rydberg | a666032 | 2008-11-06 12:53:21 -0800 | [diff] [blame] | 1329 | /* MacBook Pro 5: accelerometer, backlight and temperature set 12 */ | 
 | 1330 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 12 }, | 
| Henrik Rydberg | eefc488 | 2008-11-06 12:53:22 -0800 | [diff] [blame] | 1331 | /* iMac 8: light sensor only, temperature set 13 */ | 
 | 1332 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 13 }, | 
| Henrik Rydberg | 9ca791b | 2008-11-19 15:36:06 -0800 | [diff] [blame] | 1333 | /* iMac 6: light sensor only, temperature set 14 */ | 
 | 1334 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 14 }, | 
| Henrik Rydberg | 85e0e5a | 2009-01-06 14:41:36 -0800 | [diff] [blame] | 1335 | /* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */ | 
 | 1336 | 	{ .accelerometer = 1, .light = 1, .temperature_set = 15 }, | 
| Bharath Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 1337 | /* MacPro3,1: temperature set 16 */ | 
 | 1338 | 	{ .accelerometer = 0, .light = 0, .temperature_set = 16 }, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1339 | }; | 
 | 1340 |  | 
 | 1341 | /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". | 
 | 1342 |  * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ | 
 | 1343 | static __initdata struct dmi_system_id applesmc_whitelist[] = { | 
| Henrik Rydberg | 85e0e5a | 2009-01-06 14:41:36 -0800 | [diff] [blame] | 1344 | 	{ applesmc_dmi_match, "Apple MacBook Air 2", { | 
 | 1345 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1346 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") }, | 
 | 1347 | 		&applesmc_dmi_data[15]}, | 
| Henrik Rydberg | f5274c9 | 2008-10-18 20:27:40 -0700 | [diff] [blame] | 1348 | 	{ applesmc_dmi_match, "Apple MacBook Air", { | 
 | 1349 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1350 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1351 | 		&applesmc_dmi_data[7]}, | 
| Henrik Rydberg | a666032 | 2008-11-06 12:53:21 -0800 | [diff] [blame] | 1352 | 	{ applesmc_dmi_match, "Apple MacBook Pro 5", { | 
 | 1353 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1354 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") }, | 
 | 1355 | 		&applesmc_dmi_data[12]}, | 
| Henrik Rydberg | d754990 | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1356 | 	{ applesmc_dmi_match, "Apple MacBook Pro 4", { | 
 | 1357 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1358 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, | 
 | 1359 | 		&applesmc_dmi_data[8]}, | 
| Henrik Rydberg | 07e8dbd | 2008-10-18 20:27:42 -0700 | [diff] [blame] | 1360 | 	{ applesmc_dmi_match, "Apple MacBook Pro 3", { | 
 | 1361 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1362 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") }, | 
 | 1363 | 		&applesmc_dmi_data[9]}, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1364 | 	{ applesmc_dmi_match, "Apple MacBook Pro", { | 
 | 1365 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1366 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1367 | 		&applesmc_dmi_data[0]}, | 
| Guilherme M. Schroeder | f91a79f | 2008-08-15 00:40:32 -0700 | [diff] [blame] | 1368 | 	{ applesmc_dmi_match, "Apple MacBook (v2)", { | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1369 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 1370 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1371 | 		&applesmc_dmi_data[1]}, | 
| Guilherme M. Schroeder | f91a79f | 2008-08-15 00:40:32 -0700 | [diff] [blame] | 1372 | 	{ applesmc_dmi_match, "Apple MacBook (v3)", { | 
 | 1373 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1374 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1375 | 		&applesmc_dmi_data[6]}, | 
| Henrik Rydberg | 468cc03 | 2008-11-12 13:24:58 -0800 | [diff] [blame] | 1376 | 	{ applesmc_dmi_match, "Apple MacBook 4", { | 
 | 1377 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1378 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") }, | 
 | 1379 | 		&applesmc_dmi_data[6]}, | 
| Henrik Rydberg | 181209a | 2008-11-06 12:53:20 -0800 | [diff] [blame] | 1380 | 	{ applesmc_dmi_match, "Apple MacBook 5", { | 
 | 1381 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1382 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") }, | 
 | 1383 | 		&applesmc_dmi_data[11]}, | 
| Riki Oktarianto | cd19ba1 | 2008-02-04 23:41:58 -0800 | [diff] [blame] | 1384 | 	{ applesmc_dmi_match, "Apple MacBook", { | 
 | 1385 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1386 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1387 | 		&applesmc_dmi_data[2]}, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1388 | 	{ applesmc_dmi_match, "Apple Macmini", { | 
 | 1389 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1390 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1391 | 		&applesmc_dmi_data[3]}, | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1392 | 	{ applesmc_dmi_match, "Apple MacPro2", { | 
 | 1393 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1394 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1395 | 		&applesmc_dmi_data[4]}, | 
| Bharath Ramesh | fb9f88e | 2009-01-29 14:25:24 -0800 | [diff] [blame] | 1396 | 	{ applesmc_dmi_match, "Apple MacPro3", { | 
 | 1397 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1398 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") }, | 
 | 1399 | 		&applesmc_dmi_data[16]}, | 
| Henrik Rydberg | 45a3a36 | 2008-11-19 15:36:42 -0800 | [diff] [blame] | 1400 | 	{ applesmc_dmi_match, "Apple MacPro", { | 
 | 1401 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1402 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") }, | 
 | 1403 | 		&applesmc_dmi_data[4]}, | 
| Henrik Rydberg | eefc488 | 2008-11-06 12:53:22 -0800 | [diff] [blame] | 1404 | 	{ applesmc_dmi_match, "Apple iMac 8", { | 
 | 1405 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1406 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") }, | 
 | 1407 | 		&applesmc_dmi_data[13]}, | 
| Henrik Rydberg | 9ca791b | 2008-11-19 15:36:06 -0800 | [diff] [blame] | 1408 | 	{ applesmc_dmi_match, "Apple iMac 6", { | 
 | 1409 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1410 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") }, | 
 | 1411 | 		&applesmc_dmi_data[14]}, | 
| Henrik Rydberg | 6e3530f | 2008-11-06 12:53:19 -0800 | [diff] [blame] | 1412 | 	{ applesmc_dmi_match, "Apple iMac 5", { | 
 | 1413 | 	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | 
 | 1414 | 	  DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") }, | 
 | 1415 | 		&applesmc_dmi_data[10]}, | 
| Roberto De Ioris | 9f86f28 | 2008-08-15 00:40:30 -0700 | [diff] [blame] | 1416 | 	{ applesmc_dmi_match, "Apple iMac", { | 
 | 1417 | 	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 
 | 1418 | 	  DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, | 
| Andrew Morton | 7b5e3cb | 2008-10-18 20:27:41 -0700 | [diff] [blame] | 1419 | 		&applesmc_dmi_data[5]}, | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1420 | 	{ .ident = NULL } | 
 | 1421 | }; | 
 | 1422 |  | 
 | 1423 | static int __init applesmc_init(void) | 
 | 1424 | { | 
 | 1425 | 	int ret; | 
 | 1426 | 	int count; | 
 | 1427 | 	int i; | 
 | 1428 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1429 | 	if (!dmi_check_system(applesmc_whitelist)) { | 
 | 1430 | 		printk(KERN_WARNING "applesmc: supported laptop not found!\n"); | 
 | 1431 | 		ret = -ENODEV; | 
 | 1432 | 		goto out; | 
 | 1433 | 	} | 
 | 1434 |  | 
 | 1435 | 	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS, | 
 | 1436 | 								"applesmc")) { | 
 | 1437 | 		ret = -ENXIO; | 
 | 1438 | 		goto out; | 
 | 1439 | 	} | 
 | 1440 |  | 
 | 1441 | 	ret = platform_driver_register(&applesmc_driver); | 
 | 1442 | 	if (ret) | 
 | 1443 | 		goto out_region; | 
 | 1444 |  | 
| Jean Delvare | ddfbf2a | 2007-05-08 20:27:04 -0700 | [diff] [blame] | 1445 | 	pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT, | 
 | 1446 | 					       NULL, 0); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1447 | 	if (IS_ERR(pdev)) { | 
 | 1448 | 		ret = PTR_ERR(pdev); | 
 | 1449 | 		goto out_driver; | 
 | 1450 | 	} | 
 | 1451 |  | 
| Nicolas Boichat | fa74419 | 2007-05-23 13:58:13 -0700 | [diff] [blame] | 1452 | 	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr); | 
| Nicolas Boichat | 6996abf | 2007-05-27 22:17:43 +0200 | [diff] [blame] | 1453 | 	if (ret) | 
 | 1454 | 		goto out_device; | 
| Nicolas Boichat | fa74419 | 2007-05-23 13:58:13 -0700 | [diff] [blame] | 1455 |  | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1456 | 	/* Create key enumeration sysfs files */ | 
 | 1457 | 	ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group); | 
 | 1458 | 	if (ret) | 
| Nicolas Boichat | 6996abf | 2007-05-27 22:17:43 +0200 | [diff] [blame] | 1459 | 		goto out_name; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1460 |  | 
 | 1461 | 	/* create fan files */ | 
 | 1462 | 	count = applesmc_get_fan_count(); | 
 | 1463 | 	if (count < 0) { | 
 | 1464 | 		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n"); | 
 | 1465 | 	} else { | 
 | 1466 | 		printk(KERN_INFO "applesmc: %d fans found.\n", count); | 
 | 1467 |  | 
 | 1468 | 		switch (count) { | 
 | 1469 | 		default: | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1470 | 			printk(KERN_WARNING "applesmc: More than 4 fans found," | 
 | 1471 | 					" but at most 4 fans are supported" | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1472 | 						" by the driver.\n"); | 
| René Rebe | 8de5770 | 2007-10-16 14:19:20 -0700 | [diff] [blame] | 1473 | 		case 4: | 
 | 1474 | 			ret = sysfs_create_group(&pdev->dev.kobj, | 
 | 1475 | 						 &fan_attribute_groups[3]); | 
 | 1476 | 			if (ret) | 
 | 1477 | 				goto out_key_enumeration; | 
 | 1478 | 		case 3: | 
 | 1479 | 			ret = sysfs_create_group(&pdev->dev.kobj, | 
 | 1480 | 						 &fan_attribute_groups[2]); | 
 | 1481 | 			if (ret) | 
 | 1482 | 				goto out_key_enumeration; | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1483 | 		case 2: | 
 | 1484 | 			ret = sysfs_create_group(&pdev->dev.kobj, | 
 | 1485 | 						 &fan_attribute_groups[1]); | 
 | 1486 | 			if (ret) | 
 | 1487 | 				goto out_key_enumeration; | 
 | 1488 | 		case 1: | 
 | 1489 | 			ret = sysfs_create_group(&pdev->dev.kobj, | 
 | 1490 | 						 &fan_attribute_groups[0]); | 
 | 1491 | 			if (ret) | 
 | 1492 | 				goto out_fan_1; | 
 | 1493 | 		case 0: | 
 | 1494 | 			; | 
 | 1495 | 		} | 
 | 1496 | 	} | 
 | 1497 |  | 
 | 1498 | 	for (i = 0; | 
 | 1499 | 	     temperature_sensors_sets[applesmc_temperature_set][i] != NULL; | 
 | 1500 | 	     i++) { | 
 | 1501 | 		if (temperature_attributes[i] == NULL) { | 
 | 1502 | 			printk(KERN_ERR "applesmc: More temperature sensors " | 
 | 1503 | 				"in temperature_sensors_sets (at least %i)" | 
 | 1504 | 				"than available sysfs files in " | 
 | 1505 | 				"temperature_attributes (%i), please report " | 
 | 1506 | 				"this bug.\n", i, i-1); | 
 | 1507 | 			goto out_temperature; | 
 | 1508 | 		} | 
 | 1509 | 		ret = sysfs_create_file(&pdev->dev.kobj, | 
 | 1510 | 						temperature_attributes[i]); | 
 | 1511 | 		if (ret) | 
 | 1512 | 			goto out_temperature; | 
 | 1513 | 	} | 
 | 1514 |  | 
 | 1515 | 	if (applesmc_accelerometer) { | 
 | 1516 | 		ret = applesmc_create_accelerometer(); | 
 | 1517 | 		if (ret) | 
 | 1518 | 			goto out_temperature; | 
 | 1519 | 	} | 
 | 1520 |  | 
 | 1521 | 	if (applesmc_light) { | 
 | 1522 | 		/* Add light sensor file */ | 
 | 1523 | 		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr); | 
 | 1524 | 		if (ret) | 
 | 1525 | 			goto out_accelerometer; | 
 | 1526 |  | 
 | 1527 | 		/* Create the workqueue */ | 
 | 1528 | 		applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); | 
 | 1529 | 		if (!applesmc_led_wq) { | 
 | 1530 | 			ret = -ENOMEM; | 
 | 1531 | 			goto out_light_sysfs; | 
 | 1532 | 		} | 
 | 1533 |  | 
 | 1534 | 		/* register as a led device */ | 
 | 1535 | 		ret = led_classdev_register(&pdev->dev, &applesmc_backlight); | 
 | 1536 | 		if (ret < 0) | 
 | 1537 | 			goto out_light_wq; | 
 | 1538 | 	} | 
 | 1539 |  | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1540 | 	hwmon_dev = hwmon_device_register(&pdev->dev); | 
 | 1541 | 	if (IS_ERR(hwmon_dev)) { | 
 | 1542 | 		ret = PTR_ERR(hwmon_dev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1543 | 		goto out_light_ledclass; | 
 | 1544 | 	} | 
 | 1545 |  | 
 | 1546 | 	printk(KERN_INFO "applesmc: driver successfully loaded.\n"); | 
 | 1547 |  | 
 | 1548 | 	return 0; | 
 | 1549 |  | 
 | 1550 | out_light_ledclass: | 
 | 1551 | 	if (applesmc_light) | 
 | 1552 | 		led_classdev_unregister(&applesmc_backlight); | 
 | 1553 | out_light_wq: | 
 | 1554 | 	if (applesmc_light) | 
 | 1555 | 		destroy_workqueue(applesmc_led_wq); | 
 | 1556 | out_light_sysfs: | 
 | 1557 | 	if (applesmc_light) | 
 | 1558 | 		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr); | 
 | 1559 | out_accelerometer: | 
 | 1560 | 	if (applesmc_accelerometer) | 
 | 1561 | 		applesmc_release_accelerometer(); | 
 | 1562 | out_temperature: | 
 | 1563 | 	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group); | 
 | 1564 | 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]); | 
 | 1565 | out_fan_1: | 
 | 1566 | 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]); | 
 | 1567 | out_key_enumeration: | 
 | 1568 | 	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group); | 
| Nicolas Boichat | 6996abf | 2007-05-27 22:17:43 +0200 | [diff] [blame] | 1569 | out_name: | 
 | 1570 | 	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1571 | out_device: | 
 | 1572 | 	platform_device_unregister(pdev); | 
 | 1573 | out_driver: | 
 | 1574 | 	platform_driver_unregister(&applesmc_driver); | 
 | 1575 | out_region: | 
 | 1576 | 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); | 
 | 1577 | out: | 
 | 1578 | 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret); | 
 | 1579 | 	return ret; | 
 | 1580 | } | 
 | 1581 |  | 
 | 1582 | static void __exit applesmc_exit(void) | 
 | 1583 | { | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1584 | 	hwmon_device_unregister(hwmon_dev); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1585 | 	if (applesmc_light) { | 
 | 1586 | 		led_classdev_unregister(&applesmc_backlight); | 
 | 1587 | 		destroy_workqueue(applesmc_led_wq); | 
 | 1588 | 		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr); | 
 | 1589 | 	} | 
 | 1590 | 	if (applesmc_accelerometer) | 
 | 1591 | 		applesmc_release_accelerometer(); | 
 | 1592 | 	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group); | 
 | 1593 | 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]); | 
 | 1594 | 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]); | 
 | 1595 | 	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group); | 
| Nicolas Boichat | 6996abf | 2007-05-27 22:17:43 +0200 | [diff] [blame] | 1596 | 	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr); | 
| Nicolas Boichat | 6f2fad7 | 2007-05-08 00:24:52 -0700 | [diff] [blame] | 1597 | 	platform_device_unregister(pdev); | 
 | 1598 | 	platform_driver_unregister(&applesmc_driver); | 
 | 1599 | 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); | 
 | 1600 |  | 
 | 1601 | 	printk(KERN_INFO "applesmc: driver unloaded.\n"); | 
 | 1602 | } | 
 | 1603 |  | 
 | 1604 | module_init(applesmc_init); | 
 | 1605 | module_exit(applesmc_exit); | 
 | 1606 |  | 
 | 1607 | MODULE_AUTHOR("Nicolas Boichat"); | 
 | 1608 | MODULE_DESCRIPTION("Apple SMC"); | 
 | 1609 | MODULE_LICENSE("GPL v2"); | 
| Henrik Rydberg | dc924ef | 2008-12-01 13:13:49 -0800 | [diff] [blame] | 1610 | MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); |