diff options
Diffstat (limited to 'drivers/hwmon/applesmc.c')
-rw-r--r-- | drivers/hwmon/applesmc.c | 109 |
1 files changed, 82 insertions, 27 deletions
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b06b8e090a27..bc011da79e14 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c | |||
@@ -49,6 +49,9 @@ | |||
49 | 49 | ||
50 | #define APPLESMC_MAX_DATA_LENGTH 32 | 50 | #define APPLESMC_MAX_DATA_LENGTH 32 |
51 | 51 | ||
52 | #define APPLESMC_MIN_WAIT 0x0040 | ||
53 | #define APPLESMC_MAX_WAIT 0x8000 | ||
54 | |||
52 | #define APPLESMC_STATUS_MASK 0x0f | 55 | #define APPLESMC_STATUS_MASK 0x0f |
53 | #define APPLESMC_READ_CMD 0x10 | 56 | #define APPLESMC_READ_CMD 0x10 |
54 | #define APPLESMC_WRITE_CMD 0x11 | 57 | #define APPLESMC_WRITE_CMD 0x11 |
@@ -57,8 +60,8 @@ | |||
57 | 60 | ||
58 | #define KEY_COUNT_KEY "#KEY" /* r-o ui32 */ | 61 | #define KEY_COUNT_KEY "#KEY" /* r-o ui32 */ |
59 | 62 | ||
60 | #define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */ | 63 | #define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */ |
61 | #define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */ | 64 | #define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */ |
62 | #define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ | 65 | #define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ |
63 | 66 | ||
64 | #define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ | 67 | #define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ |
@@ -104,6 +107,15 @@ static const char* temperature_sensors_sets[][36] = { | |||
104 | /* Set 6: Macbook3 set */ | 107 | /* Set 6: Macbook3 set */ |
105 | { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", | 108 | { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", |
106 | "Th0S", "Th1H", NULL }, | 109 | "Th0S", "Th1H", NULL }, |
110 | /* Set 7: Macbook Air */ | ||
111 | { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", | ||
112 | "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, | ||
113 | /* Set 8: Macbook Pro 4,1 (Penryn) */ | ||
114 | { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", | ||
115 | "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, | ||
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 }, | ||
107 | }; | 119 | }; |
108 | 120 | ||
109 | /* List of keys used to read/write fan speeds */ | 121 | /* List of keys used to read/write fan speeds */ |
@@ -163,25 +175,25 @@ static unsigned int key_at_index; | |||
163 | static struct workqueue_struct *applesmc_led_wq; | 175 | static struct workqueue_struct *applesmc_led_wq; |
164 | 176 | ||
165 | /* | 177 | /* |
166 | * __wait_status - Wait up to 2ms for the status port to get a certain value | 178 | * __wait_status - Wait up to 32ms for the status port to get a certain value |
167 | * (masked with 0x0f), returning zero if the value is obtained. Callers must | 179 | * (masked with 0x0f), returning zero if the value is obtained. Callers must |
168 | * hold applesmc_lock. | 180 | * hold applesmc_lock. |
169 | */ | 181 | */ |
170 | static int __wait_status(u8 val) | 182 | static int __wait_status(u8 val) |
171 | { | 183 | { |
172 | unsigned int i; | 184 | int us; |
173 | 185 | ||
174 | val = val & APPLESMC_STATUS_MASK; | 186 | val = val & APPLESMC_STATUS_MASK; |
175 | 187 | ||
176 | for (i = 0; i < 200; i++) { | 188 | for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { |
189 | udelay(us); | ||
177 | if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { | 190 | if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { |
178 | if (debug) | 191 | if (debug) |
179 | printk(KERN_DEBUG | 192 | printk(KERN_DEBUG |
180 | "Waited %d us for status %x\n", | 193 | "Waited %d us for status %x\n", |
181 | i*10, val); | 194 | 2 * us - APPLESMC_MIN_WAIT, val); |
182 | return 0; | 195 | return 0; |
183 | } | 196 | } |
184 | udelay(10); | ||
185 | } | 197 | } |
186 | 198 | ||
187 | printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", | 199 | printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", |
@@ -191,6 +203,25 @@ static int __wait_status(u8 val) | |||
191 | } | 203 | } |
192 | 204 | ||
193 | /* | 205 | /* |
206 | * special treatment of command port - on newer macbooks, it seems necessary | ||
207 | * to resend the command byte before polling the status again. Callers must | ||
208 | * hold applesmc_lock. | ||
209 | */ | ||
210 | static int send_command(u8 cmd) | ||
211 | { | ||
212 | int us; | ||
213 | for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { | ||
214 | outb(cmd, APPLESMC_CMD_PORT); | ||
215 | udelay(us); | ||
216 | if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) | ||
217 | return 0; | ||
218 | } | ||
219 | printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", | ||
220 | cmd, inb(APPLESMC_CMD_PORT)); | ||
221 | return -EIO; | ||
222 | } | ||
223 | |||
224 | /* | ||
194 | * applesmc_read_key - reads len bytes from a given key, and put them in buffer. | 225 | * applesmc_read_key - reads len bytes from a given key, and put them in buffer. |
195 | * Returns zero on success or a negative error on failure. Callers must | 226 | * Returns zero on success or a negative error on failure. Callers must |
196 | * hold applesmc_lock. | 227 | * hold applesmc_lock. |
@@ -205,8 +236,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len) | |||
205 | return -EINVAL; | 236 | return -EINVAL; |
206 | } | 237 | } |
207 | 238 | ||
208 | outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT); | 239 | if (send_command(APPLESMC_READ_CMD)) |
209 | if (__wait_status(0x0c)) | ||
210 | return -EIO; | 240 | return -EIO; |
211 | 241 | ||
212 | for (i = 0; i < 4; i++) { | 242 | for (i = 0; i < 4; i++) { |
@@ -249,8 +279,7 @@ static int applesmc_write_key(const char* key, u8* buffer, u8 len) | |||
249 | return -EINVAL; | 279 | return -EINVAL; |
250 | } | 280 | } |
251 | 281 | ||
252 | outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT); | 282 | if (send_command(APPLESMC_WRITE_CMD)) |
253 | if (__wait_status(0x0c)) | ||
254 | return -EIO; | 283 | return -EIO; |
255 | 284 | ||
256 | for (i = 0; i < 4; i++) { | 285 | for (i = 0; i < 4; i++) { |
@@ -284,8 +313,7 @@ static int applesmc_get_key_at_index(int index, char* key) | |||
284 | readkey[2] = index >> 8; | 313 | readkey[2] = index >> 8; |
285 | readkey[3] = index; | 314 | readkey[3] = index; |
286 | 315 | ||
287 | outb(APPLESMC_GET_KEY_BY_INDEX_CMD, APPLESMC_CMD_PORT); | 316 | if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD)) |
288 | if (__wait_status(0x0c)) | ||
289 | return -EIO; | 317 | return -EIO; |
290 | 318 | ||
291 | for (i = 0; i < 4; i++) { | 319 | for (i = 0; i < 4; i++) { |
@@ -315,8 +343,7 @@ static int applesmc_get_key_type(char* key, char* type) | |||
315 | { | 343 | { |
316 | int i; | 344 | int i; |
317 | 345 | ||
318 | outb(APPLESMC_GET_KEY_TYPE_CMD, APPLESMC_CMD_PORT); | 346 | if (send_command(APPLESMC_GET_KEY_TYPE_CMD)) |
319 | if (__wait_status(0x0c)) | ||
320 | return -EIO; | 347 | return -EIO; |
321 | 348 | ||
322 | for (i = 0; i < 4; i++) { | 349 | for (i = 0; i < 4; i++) { |
@@ -325,7 +352,7 @@ static int applesmc_get_key_type(char* key, char* type) | |||
325 | return -EIO; | 352 | return -EIO; |
326 | } | 353 | } |
327 | 354 | ||
328 | outb(5, APPLESMC_DATA_PORT); | 355 | outb(6, APPLESMC_DATA_PORT); |
329 | 356 | ||
330 | for (i = 0; i < 6; i++) { | 357 | for (i = 0; i < 6; i++) { |
331 | if (__wait_status(0x05)) | 358 | if (__wait_status(0x05)) |
@@ -527,17 +554,27 @@ out: | |||
527 | static ssize_t applesmc_light_show(struct device *dev, | 554 | static ssize_t applesmc_light_show(struct device *dev, |
528 | struct device_attribute *attr, char *sysfsbuf) | 555 | struct device_attribute *attr, char *sysfsbuf) |
529 | { | 556 | { |
557 | static int data_length; | ||
530 | int ret; | 558 | int ret; |
531 | u8 left = 0, right = 0; | 559 | u8 left = 0, right = 0; |
532 | u8 buffer[6]; | 560 | u8 buffer[10], query[6]; |
533 | 561 | ||
534 | mutex_lock(&applesmc_lock); | 562 | mutex_lock(&applesmc_lock); |
535 | 563 | ||
536 | ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6); | 564 | if (!data_length) { |
565 | ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query); | ||
566 | if (ret) | ||
567 | goto out; | ||
568 | data_length = clamp_val(query[0], 0, 10); | ||
569 | printk(KERN_INFO "applesmc: light sensor data length set to " | ||
570 | "%d\n", data_length); | ||
571 | } | ||
572 | |||
573 | ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); | ||
537 | left = buffer[2]; | 574 | left = buffer[2]; |
538 | if (ret) | 575 | if (ret) |
539 | goto out; | 576 | goto out; |
540 | ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6); | 577 | ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); |
541 | right = buffer[2]; | 578 | right = buffer[2]; |
542 | 579 | ||
543 | out: | 580 | out: |
@@ -1233,39 +1270,57 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { | |||
1233 | { .accelerometer = 0, .light = 0, .temperature_set = 5 }, | 1270 | { .accelerometer = 0, .light = 0, .temperature_set = 5 }, |
1234 | /* MacBook3: accelerometer and temperature set 6 */ | 1271 | /* MacBook3: accelerometer and temperature set 6 */ |
1235 | { .accelerometer = 1, .light = 0, .temperature_set = 6 }, | 1272 | { .accelerometer = 1, .light = 0, .temperature_set = 6 }, |
1273 | /* MacBook Air: accelerometer, backlight and temperature set 7 */ | ||
1274 | { .accelerometer = 1, .light = 1, .temperature_set = 7 }, | ||
1275 | /* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ | ||
1276 | { .accelerometer = 1, .light = 1, .temperature_set = 8 }, | ||
1277 | /* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ | ||
1278 | { .accelerometer = 1, .light = 1, .temperature_set = 9 }, | ||
1236 | }; | 1279 | }; |
1237 | 1280 | ||
1238 | /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". | 1281 | /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". |
1239 | * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ | 1282 | * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ |
1240 | static __initdata struct dmi_system_id applesmc_whitelist[] = { | 1283 | static __initdata struct dmi_system_id applesmc_whitelist[] = { |
1284 | { applesmc_dmi_match, "Apple MacBook Air", { | ||
1285 | DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | ||
1286 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, | ||
1287 | &applesmc_dmi_data[7]}, | ||
1288 | { applesmc_dmi_match, "Apple MacBook Pro 4", { | ||
1289 | DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | ||
1290 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, | ||
1291 | &applesmc_dmi_data[8]}, | ||
1292 | { applesmc_dmi_match, "Apple MacBook Pro 3", { | ||
1293 | DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), | ||
1294 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") }, | ||
1295 | &applesmc_dmi_data[9]}, | ||
1241 | { applesmc_dmi_match, "Apple MacBook Pro", { | 1296 | { applesmc_dmi_match, "Apple MacBook Pro", { |
1242 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1297 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1243 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, | 1298 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, |
1244 | (void*)&applesmc_dmi_data[0]}, | 1299 | &applesmc_dmi_data[0]}, |
1245 | { applesmc_dmi_match, "Apple MacBook (v2)", { | 1300 | { applesmc_dmi_match, "Apple MacBook (v2)", { |
1246 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1301 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1247 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, | 1302 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, |
1248 | (void*)&applesmc_dmi_data[1]}, | 1303 | &applesmc_dmi_data[1]}, |
1249 | { applesmc_dmi_match, "Apple MacBook (v3)", { | 1304 | { applesmc_dmi_match, "Apple MacBook (v3)", { |
1250 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1305 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1251 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, | 1306 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, |
1252 | (void*)&applesmc_dmi_data[6]}, | 1307 | &applesmc_dmi_data[6]}, |
1253 | { applesmc_dmi_match, "Apple MacBook", { | 1308 | { applesmc_dmi_match, "Apple MacBook", { |
1254 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1309 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1255 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, | 1310 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, |
1256 | (void*)&applesmc_dmi_data[2]}, | 1311 | &applesmc_dmi_data[2]}, |
1257 | { applesmc_dmi_match, "Apple Macmini", { | 1312 | { applesmc_dmi_match, "Apple Macmini", { |
1258 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1313 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1259 | DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, | 1314 | DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, |
1260 | (void*)&applesmc_dmi_data[3]}, | 1315 | &applesmc_dmi_data[3]}, |
1261 | { applesmc_dmi_match, "Apple MacPro2", { | 1316 | { applesmc_dmi_match, "Apple MacPro2", { |
1262 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1317 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1263 | DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, | 1318 | DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, |
1264 | (void*)&applesmc_dmi_data[4]}, | 1319 | &applesmc_dmi_data[4]}, |
1265 | { applesmc_dmi_match, "Apple iMac", { | 1320 | { applesmc_dmi_match, "Apple iMac", { |
1266 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), | 1321 | DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), |
1267 | DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, | 1322 | DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, |
1268 | (void*)&applesmc_dmi_data[5]}, | 1323 | &applesmc_dmi_data[5]}, |
1269 | { .ident = NULL } | 1324 | { .ident = NULL } |
1270 | }; | 1325 | }; |
1271 | 1326 | ||