diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index a18887990f4a..2500cc7ebca5 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -151,65 +151,58 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * wait_read - Wait for a byte to appear on SMC port. Callers must - * hold applesmc_lock. + * Wait for specific status bits on the SMC */ -static int wait_read(void) + +static int wait_status(u8 val, u8 mask) { - unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ) / USEC_PER_SEC; u8 status; int us; + unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ) / USEC_PER_SEC; for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - usleep_range(us, us * 16); status = inb(APPLESMC_CMD_PORT); - /* read: wait for smc to settle */ - if (status & 0x01) + if ((status & mask) == val) return 0; - /* timeout: give up */ if (time_after(jiffies, end)) break; - } - - pr_warn("wait_read() fail: 0x%02x\n", status); + usleep_range(us, us * 16); + } + pr_warn("wait_status timeout: 0x%02x\n", status); return -EIO; } - + /* * send_byte - Write to SMC port, retrying when necessary. Callers * must hold applesmc_lock. */ -static int send_byte(u8 cmd, u16 port) -{ - u8 status; - int us; - unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ) / USEC_PER_SEC; +static int send_byte_data(u8 cmd, u16 port, bool skip) +{ + u8 wstat=0x44; + if (skip) + wstat=0x40; + if (wait_status(0x44, 0xF6)) + goto fail; outb(cmd, port); - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - usleep_range(us, us * 16); - 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 (time_after(jiffies, end)) - 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); + if (! wait_status(wstat, 0xFE)) + return 0; +fail: + pr_warn("send_byte_data(0x%02x, 0x%04x) fail\n", cmd, APPLESMC_CMD_PORT); return -EIO; } static int send_command(u8 cmd) { - return send_byte(cmd, APPLESMC_CMD_PORT); + if (wait_status(0x40, 0xF2)) { pr_warn("send_command fail 1\n"); + goto fail;} + outb(cmd, APPLESMC_CMD_PORT); + if (wait_status(0x4C, 0xFF)) { pr_warn("send_command fail 2\n"); + goto fail;} + return 0; +fail: + pr_warn("send_cmd(0x%02x, 0x%04x) fail\n", cmd, APPLESMC_CMD_PORT); + return -EIO; } static int send_argument(const char *key) @@ -217,7 +210,7 @@ static int send_argument(const char *key) int i; for (i = 0; i < 4; i++) - if (send_byte(key[i], APPLESMC_DATA_PORT)) + if (send_byte_data(key[i], APPLESMC_DATA_PORT,false)) return -EIO; return 0; } @@ -233,13 +226,13 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) } /* This has no effect on newer (2012) SMCs */ - if (send_byte(len, APPLESMC_DATA_PORT)) { + if (send_byte_data(len, APPLESMC_DATA_PORT,false)) { pr_warn("%.4s: read len fail\n", key); return -EIO; } for (i = 0; i < len; i++) { - if (wait_read()) { + if (wait_status(0x45,0xFF)) { pr_warn("%.4s: read data[%d] fail\n", key, i); return -EIO; } @@ -269,14 +262,14 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) return -EIO; } - if (send_byte(len, APPLESMC_DATA_PORT)) { + if (send_byte_data(len, APPLESMC_DATA_PORT,false)) { pr_warn("%.4s: write len fail\n", key); return -EIO; } for (i = 0; i < len; i++) { - if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { - pr_warn("%s: write data fail\n", key); + if (send_byte_data(buffer[i], APPLESMC_DATA_PORT,(i==(len-1)))) { + pr_warn("%s: write data fail at %i\n", key, i); return -EIO; } }