diff options
author | Lv Zheng <lv.zheng@intel.com> | 2015-02-05 03:27:29 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-02-05 09:42:18 -0500 |
commit | 9e295ac14d6a59180beed0735e6a504c2ee87761 (patch) | |
tree | 90268475ce9937bd908dfabb0cffde87381abe5c /drivers/acpi/ec.c | |
parent | ca37bfdfbc8d0a3ec73e4b97bb26dcfa51d515aa (diff) |
ACPI / EC: Reduce ec_poll() by referencing the last register access timestamp.
Timeout in the ec_poll() doesn't refer to the last register access time. It
thus can win the competition against the acpi_ec_gpe_handler() if a
transaction takes longer than 1ms but individual register accesses are less
than 1ms. In some cases, it can make the following silicon bug easier to
be triggered:
GPE EN is not wired to the GPE trigger line, so when GPE STS is already
set when 1 is written to GPE EN, no GPE can be triggered.
This patch adds register access timestamp reference support for ec_poll()
to reduce the number of ec_poll() invocations.
Reported-by: Venkat Raghavulu <venkat.raghavulu@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2540870e89f7..e000cf70378b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -71,6 +71,7 @@ enum ec_command { | |||
71 | #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ | 71 | #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ |
72 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ | 72 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ |
73 | #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ | 73 | #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ |
74 | #define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */ | ||
74 | #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query | 75 | #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query |
75 | * when trying to clear the EC */ | 76 | * when trying to clear the EC */ |
76 | 77 | ||
@@ -118,6 +119,7 @@ struct transaction { | |||
118 | u8 wlen; | 119 | u8 wlen; |
119 | u8 rlen; | 120 | u8 rlen; |
120 | u8 flags; | 121 | u8 flags; |
122 | unsigned long timestamp; | ||
121 | }; | 123 | }; |
122 | 124 | ||
123 | static int acpi_ec_query(struct acpi_ec *ec, u8 *data); | 125 | static int acpi_ec_query(struct acpi_ec *ec, u8 *data); |
@@ -155,6 +157,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) | |||
155 | { | 157 | { |
156 | u8 x = inb(ec->data_addr); | 158 | u8 x = inb(ec->data_addr); |
157 | 159 | ||
160 | ec->curr->timestamp = jiffies; | ||
158 | pr_debug("EC_DATA(R) = 0x%2.2x\n", x); | 161 | pr_debug("EC_DATA(R) = 0x%2.2x\n", x); |
159 | return x; | 162 | return x; |
160 | } | 163 | } |
@@ -163,12 +166,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) | |||
163 | { | 166 | { |
164 | pr_debug("EC_SC(W) = 0x%2.2x\n", command); | 167 | pr_debug("EC_SC(W) = 0x%2.2x\n", command); |
165 | outb(command, ec->command_addr); | 168 | outb(command, ec->command_addr); |
169 | ec->curr->timestamp = jiffies; | ||
166 | } | 170 | } |
167 | 171 | ||
168 | static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) | 172 | static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) |
169 | { | 173 | { |
170 | pr_debug("EC_DATA(W) = 0x%2.2x\n", data); | 174 | pr_debug("EC_DATA(W) = 0x%2.2x\n", data); |
171 | outb(data, ec->data_addr); | 175 | outb(data, ec->data_addr); |
176 | ec->curr->timestamp = jiffies; | ||
172 | } | 177 | } |
173 | 178 | ||
174 | #ifdef DEBUG | 179 | #ifdef DEBUG |
@@ -359,6 +364,7 @@ static void start_transaction(struct acpi_ec *ec) | |||
359 | { | 364 | { |
360 | ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; | 365 | ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; |
361 | ec->curr->flags = 0; | 366 | ec->curr->flags = 0; |
367 | ec->curr->timestamp = jiffies; | ||
362 | advance_transaction(ec); | 368 | advance_transaction(ec); |
363 | } | 369 | } |
364 | 370 | ||
@@ -370,20 +376,25 @@ static int ec_poll(struct acpi_ec *ec) | |||
370 | while (repeat--) { | 376 | while (repeat--) { |
371 | unsigned long delay = jiffies + | 377 | unsigned long delay = jiffies + |
372 | msecs_to_jiffies(ec_delay); | 378 | msecs_to_jiffies(ec_delay); |
379 | unsigned long usecs = ACPI_EC_UDELAY_POLL; | ||
373 | do { | 380 | do { |
374 | /* don't sleep with disabled interrupts */ | 381 | /* don't sleep with disabled interrupts */ |
375 | if (EC_FLAGS_MSI || irqs_disabled()) { | 382 | if (EC_FLAGS_MSI || irqs_disabled()) { |
376 | udelay(ACPI_EC_MSI_UDELAY); | 383 | usecs = ACPI_EC_MSI_UDELAY; |
384 | udelay(usecs); | ||
377 | if (ec_transaction_completed(ec)) | 385 | if (ec_transaction_completed(ec)) |
378 | return 0; | 386 | return 0; |
379 | } else { | 387 | } else { |
380 | if (wait_event_timeout(ec->wait, | 388 | if (wait_event_timeout(ec->wait, |
381 | ec_transaction_completed(ec), | 389 | ec_transaction_completed(ec), |
382 | msecs_to_jiffies(1))) | 390 | usecs_to_jiffies(usecs))) |
383 | return 0; | 391 | return 0; |
384 | } | 392 | } |
385 | spin_lock_irqsave(&ec->lock, flags); | 393 | spin_lock_irqsave(&ec->lock, flags); |
386 | advance_transaction(ec); | 394 | if (time_after(jiffies, |
395 | ec->curr->timestamp + | ||
396 | usecs_to_jiffies(usecs))) | ||
397 | advance_transaction(ec); | ||
387 | spin_unlock_irqrestore(&ec->lock, flags); | 398 | spin_unlock_irqrestore(&ec->lock, flags); |
388 | } while (time_before(jiffies, delay)); | 399 | } while (time_before(jiffies, delay)); |
389 | pr_debug("controller reset, restart transaction\n"); | 400 | pr_debug("controller reset, restart transaction\n"); |