diff options
Diffstat (limited to 'drivers/hwmon/applesmc.c')
| -rw-r--r-- | drivers/hwmon/applesmc.c | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 28270886051..3288f13d2d8 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -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,10 +54,10 @@ #define APPLESMC_MAX_DATA_LENGTH 32 -/* wait up to 32 ms for a status change. */ +/* wait up to 128 ms for a status change. */ #define APPLESMC_MIN_WAIT 0x0010 #define APPLESMC_RETRY_WAIT 0x0100 -#define APPLESMC_MAX_WAIT 0x8000 +#define APPLESMC_MAX_WAIT 0x20000 #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 @@ -229,6 +230,7 @@ static int send_argument(const char *key) 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)) { @@ -236,6 +238,7 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) return -EIO; } + /* 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; @@ -249,6 +252,17 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) 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; } @@ -524,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) @@ -921,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, |
