diff options
Diffstat (limited to 'drivers/hwmon/applesmc.c')
| -rw-r--r-- | drivers/hwmon/applesmc.c | 258 |
1 files changed, 155 insertions, 103 deletions
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 4c0743660e9..3288f13d2d8 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -8,7 +8,7 @@ * * Based on hdaps.c driver: * Copyright (C) 2005 Robert Love <rml@novell.com> - * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> + * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> * * Fan control based on smcFanControl: * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com> @@ -43,6 +43,7 @@ #include <linux/leds.h> #include <linux/hwmon.h> #include <linux/workqueue.h> +#include <linux/err.h> /* data port used by Apple SMC */ #define APPLESMC_DATA_PORT 0x300 @@ -53,11 +54,11 @@ #define APPLESMC_MAX_DATA_LENGTH 32 -/* wait up to 32 ms for a status change. */ -#define APPLESMC_MIN_WAIT 0x0040 -#define APPLESMC_MAX_WAIT 0x8000 +/* wait up to 128 ms for a status change. */ +#define APPLESMC_MIN_WAIT 0x0010 +#define APPLESMC_RETRY_WAIT 0x0100 +#define APPLESMC_MAX_WAIT 0x20000 -#define APPLESMC_STATUS_MASK 0x0f #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 #define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 @@ -80,6 +81,8 @@ #define FANS_MANUAL "FS! " /* r-w ui16 */ #define FAN_ID_FMT "F%dID" /* r-o char[16] */ +#define TEMP_SENSOR_TYPE "sp78" + /* List of keys used to read/write fan speeds */ static const char *const fan_speed_fmt[] = { "F%dAc", /* actual speed */ @@ -96,10 +99,6 @@ static const char *const fan_speed_fmt[] = { #define APPLESMC_INPUT_FUZZ 4 /* input event threshold */ #define APPLESMC_INPUT_FLAT 4 -#define SENSOR_X 0 -#define SENSOR_Y 1 -#define SENSOR_Z 2 - #define to_index(attr) (to_sensor_dev_attr(attr)->index & 0xffff) #define to_option(attr) (to_sensor_dev_attr(attr)->index >> 16) @@ -135,11 +134,13 @@ static struct applesmc_registers { unsigned int temp_count; /* number of temperature registers */ unsigned int temp_begin; /* temperature lower index bound */ unsigned int temp_end; /* temperature upper index bound */ + unsigned int index_count; /* size of temperature index array */ int num_light_sensors; /* number of light sensors */ bool has_accelerometer; /* has motion sensor */ bool has_key_backlight; /* has keyboard backlight */ bool init_complete; /* true when fully initialized */ struct applesmc_entry *cache; /* cached key entries */ + const char **index; /* temperature key index */ } smcreg = { .mutex = __MUTEX_INITIALIZER(smcreg.mutex), }; @@ -162,73 +163,106 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 32ms for the status port to get a certain value - * (masked with 0x0f), returning zero if the value is obtained. Callers must + * wait_read - Wait for a byte to appear on SMC port. Callers must * hold applesmc_lock. */ -static int __wait_status(u8 val) +static int wait_read(void) { + u8 status; int us; - - val = val & APPLESMC_STATUS_MASK; - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) + status = inb(APPLESMC_CMD_PORT); + /* read: wait for smc to settle */ + if (status & 0x01) return 0; } + pr_warn("wait_read() fail: 0x%02x\n", status); return -EIO; } /* - * special treatment of command port - on newer macbooks, it seems necessary - * to resend the command byte before polling the status again. Callers must - * hold applesmc_lock. + * send_byte - Write to SMC port, retrying when necessary. Callers + * must hold applesmc_lock. */ -static int send_command(u8 cmd) +static int send_byte(u8 cmd, u16 port) { + u8 status; int us; + + outb(cmd, port); for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - outb(cmd, APPLESMC_CMD_PORT); udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) + status = inb(APPLESMC_CMD_PORT); + /* write: wait for smc to settle */ + if (status & 0x02) + continue; + /* ready: cmd accepted, return */ + if (status & 0x04) return 0; + /* timeout: give up */ + if (us << 1 == APPLESMC_MAX_WAIT) + break; + /* busy: long wait and resend */ + udelay(APPLESMC_RETRY_WAIT); + outb(cmd, port); } + + pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status); return -EIO; } +static int send_command(u8 cmd) +{ + return send_byte(cmd, APPLESMC_CMD_PORT); +} + static int send_argument(const char *key) { int i; - for (i = 0; i < 4; i++) { - outb(key[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) + for (i = 0; i < 4; i++) + if (send_byte(key[i], APPLESMC_DATA_PORT)) return -EIO; - } return 0; } static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) { + u8 status, data = 0; int i; if (send_command(cmd) || send_argument(key)) { - pr_warn("%s: read arg fail\n", key); + pr_warn("%.4s: read arg fail\n", key); return -EIO; } - outb(len, APPLESMC_DATA_PORT); + /* This has no effect on newer (2012) SMCs */ + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: read len fail\n", key); + return -EIO; + } for (i = 0; i < len; i++) { - if (__wait_status(0x05)) { - pr_warn("%s: read data fail\n", key); + if (wait_read()) { + pr_warn("%.4s: read data[%d] fail\n", key, i); return -EIO; } buffer[i] = inb(APPLESMC_DATA_PORT); } + /* Read the data port until bit0 is cleared */ + for (i = 0; i < 16; i++) { + udelay(APPLESMC_MIN_WAIT); + status = inb(APPLESMC_CMD_PORT); + if (!(status & 0x01)) + break; + data = inb(APPLESMC_DATA_PORT); + } + if (i) + pr_warn("flushed %d bytes, last value is: %d\n", i, data); + return 0; } @@ -241,14 +275,16 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) return -EIO; } - outb(len, APPLESMC_DATA_PORT); + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: write len fail\n", key); + return -EIO; + } for (i = 0; i < len; i++) { - if (__wait_status(0x04)) { + if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { pr_warn("%s: write data fail\n", key); return -EIO; } - outb(buffer[i], APPLESMC_DATA_PORT); } return 0; @@ -344,8 +380,10 @@ static int applesmc_get_lower_bound(unsigned int *lo, const char *key) while (begin != end) { int middle = begin + (end - begin) / 2; entry = applesmc_get_entry_by_index(middle); - if (IS_ERR(entry)) + if (IS_ERR(entry)) { + *lo = 0; return PTR_ERR(entry); + } if (strcmp(entry->key, key) < 0) begin = middle + 1; else @@ -364,8 +402,10 @@ static int applesmc_get_upper_bound(unsigned int *hi, const char *key) while (begin != end) { int middle = begin + (end - begin) / 2; entry = applesmc_get_entry_by_index(middle); - if (IS_ERR(entry)) + if (IS_ERR(entry)) { + *hi = smcreg.key_count; return PTR_ERR(entry); + } if (strcmp(key, entry->key) < 0) end = middle; else @@ -428,30 +468,19 @@ static int applesmc_has_key(const char *key, bool *value) } /* - * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). + * applesmc_read_s16 - Read 16-bit signed big endian register */ -static int applesmc_read_motion_sensor(int index, s16 *value) +static int applesmc_read_s16(const char *key, s16 *value) { u8 buffer[2]; int ret; - switch (index) { - case SENSOR_X: - ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2); - break; - case SENSOR_Y: - ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2); - break; - case SENSOR_Z: - ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2); - break; - default: - ret = -EINVAL; - } + ret = applesmc_read_key(key, buffer, 2); + if (ret) + return ret; *value = ((s16)buffer[0] << 8) | buffer[1]; - - return ret; + return 0; } /* @@ -478,6 +507,30 @@ static void applesmc_device_init(void) pr_warn("failed to init the device\n"); } +static int applesmc_init_index(struct applesmc_registers *s) +{ + const struct applesmc_entry *entry; + unsigned int i; + + if (s->index) + return 0; + + s->index = kcalloc(s->temp_count, sizeof(s->index[0]), GFP_KERNEL); + if (!s->index) + return -ENOMEM; + + for (i = s->temp_begin; i < s->temp_end; i++) { + entry = applesmc_get_entry_by_index(i); + if (IS_ERR(entry)) + continue; + if (strcmp(entry->type, TEMP_SENSOR_TYPE)) + continue; + s->index[s->index_count++] = entry->key; + } + + return 0; +} + /* * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent. */ @@ -485,16 +538,25 @@ static int applesmc_init_smcreg_try(void) { struct applesmc_registers *s = &smcreg; bool left_light_sensor, right_light_sensor; + unsigned int count; u8 tmp[1]; int ret; if (s->init_complete) return 0; - ret = read_register_count(&s->key_count); + ret = read_register_count(&count); if (ret) return ret; + if (s->cache && s->key_count != count) { + pr_warn("key count changed from %d to %d\n", + s->key_count, count); + kfree(s->cache); + s->cache = NULL; + } + s->key_count = count; + if (!s->cache) s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL); if (!s->cache) @@ -513,6 +575,10 @@ static int applesmc_init_smcreg_try(void) return ret; s->temp_count = s->temp_end - s->temp_begin; + ret = applesmc_init_index(s); + if (ret) + return ret; + ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor); if (ret) return ret; @@ -529,8 +595,8 @@ static int applesmc_init_smcreg_try(void) s->num_light_sensors = left_light_sensor + right_light_sensor; s->init_complete = true; - pr_info("key=%d fan=%d temp=%d acc=%d lux=%d kbd=%d\n", - s->key_count, s->fan_count, s->temp_count, + pr_info("key=%d fan=%d temp=%d index=%d acc=%d lux=%d kbd=%d\n", + s->key_count, s->fan_count, s->temp_count, s->index_count, s->has_accelerometer, s->num_light_sensors, s->has_key_backlight); @@ -538,6 +604,15 @@ static int applesmc_init_smcreg_try(void) return 0; } +static void applesmc_destroy_smcreg(void) +{ + kfree(smcreg.index); + smcreg.index = NULL; + kfree(smcreg.cache); + smcreg.cache = NULL; + smcreg.init_complete = false; +} + /* * applesmc_init_smcreg - Initialize register cache. * @@ -558,19 +633,11 @@ static int applesmc_init_smcreg(void) msleep(INIT_WAIT_MSECS); } - kfree(smcreg.cache); - smcreg.cache = NULL; + applesmc_destroy_smcreg(); return ret; } -static void applesmc_destroy_smcreg(void) -{ - kfree(smcreg.cache); - smcreg.cache = NULL; - smcreg.init_complete = false; -} - /* Device model stuff */ static int applesmc_probe(struct platform_device *dev) { @@ -620,8 +687,8 @@ static struct platform_driver applesmc_driver = { */ static void applesmc_calibrate(void) { - applesmc_read_motion_sensor(SENSOR_X, &rest_x); - applesmc_read_motion_sensor(SENSOR_Y, &rest_y); + applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x); + applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y); rest_x = -rest_x; } @@ -630,9 +697,9 @@ static void applesmc_idev_poll(struct input_polled_dev *dev) struct input_dev *idev = dev->input; s16 x, y; - if (applesmc_read_motion_sensor(SENSOR_X, &x)) + if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x)) return; - if (applesmc_read_motion_sensor(SENSOR_Y, &y)) + if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y)) return; x = -x; @@ -655,13 +722,13 @@ static ssize_t applesmc_position_show(struct device *dev, int ret; s16 x, y, z; - ret = applesmc_read_motion_sensor(SENSOR_X, &x); + ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x); if (ret) goto out; - ret = applesmc_read_motion_sensor(SENSOR_Y, &y); + ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y); if (ret) goto out; - ret = applesmc_read_motion_sensor(SENSOR_Z, &z); + ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z); if (ret) goto out; @@ -714,44 +781,27 @@ out: static ssize_t applesmc_show_sensor_label(struct device *dev, struct device_attribute *devattr, char *sysfsbuf) { - int index = smcreg.temp_begin + to_index(devattr); - const struct applesmc_entry *entry; + const char *key = smcreg.index[to_index(devattr)]; - entry = applesmc_get_entry_by_index(index); - if (IS_ERR(entry)) - return PTR_ERR(entry); - - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key); + return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); } /* Displays degree Celsius * 1000 */ static ssize_t applesmc_show_temperature(struct device *dev, struct device_attribute *devattr, char *sysfsbuf) { - int index = smcreg.temp_begin + to_index(devattr); - const struct applesmc_entry *entry; + const char *key = smcreg.index[to_index(devattr)]; int ret; - u8 buffer[2]; - unsigned int temp; + s16 value; + int temp; - entry = applesmc_get_entry_by_index(index); - if (IS_ERR(entry)) - return PTR_ERR(entry); - if (entry->len > 2) - return -EINVAL; - - ret = applesmc_read_entry(entry, buffer, entry->len); + ret = applesmc_read_s16(key, &value); if (ret) return ret; - if (entry->len == 2) { - temp = buffer[0] * 1000; - temp += (buffer[1] >> 6) * 250; - } else { - temp = buffer[0] * 4000; - } + temp = 250 * (value >> 6); - return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp); + return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp); } static ssize_t applesmc_show_fan_speed(struct device *dev, @@ -782,7 +832,7 @@ static ssize_t applesmc_store_fan_speed(struct device *dev, char newkey[5]; u8 buffer[2]; - if (strict_strtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) + if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) return -EINVAL; /* Bigger than a 14-bit value */ sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr)); @@ -822,7 +872,7 @@ static ssize_t applesmc_store_fan_manual(struct device *dev, unsigned long input; u16 val; - if (strict_strtoul(sysfsbuf, 10, &input) < 0) + if (kstrtoul(sysfsbuf, 10, &input) < 0) return -EINVAL; ret = applesmc_read_key(FANS_MANUAL, buffer, 2); @@ -894,7 +944,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev, ret = queue_work(applesmc_led_wq, &backlight_work); if (debug && (!ret)) - printk(KERN_DEBUG "applesmc: work was already on the queue.\n"); + dev_dbg(led_cdev->dev, "work was already on the queue.\n"); } static ssize_t applesmc_key_count_show(struct device *dev, @@ -977,7 +1027,7 @@ static ssize_t applesmc_key_at_index_store(struct device *dev, { unsigned long newkey; - if (strict_strtoul(sysfsbuf, 10, &newkey) < 0 + if (kstrtoul(sysfsbuf, 10, &newkey) < 0 || newkey >= smcreg.key_count) return -EINVAL; @@ -1189,8 +1239,10 @@ static int applesmc_dmi_match(const struct dmi_system_id *id) return 1; } -/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". - * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ +/* + * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". + * So we need to put "Apple MacBook Pro" before "Apple MacBook". + */ static __initdata struct dmi_system_id applesmc_whitelist[] = { { applesmc_dmi_match, "Apple MacBook Air", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), @@ -1259,7 +1311,7 @@ static int __init applesmc_init(void) if (ret) goto out_info; - ret = applesmc_create_nodes(temp_group, smcreg.temp_count); + ret = applesmc_create_nodes(temp_group, smcreg.index_count); if (ret) goto out_fans; |
