diff options
-rw-r--r-- | drivers/acpi/ec.c | 55 |
1 files changed, 36 insertions, 19 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 1fa14634c3f6..982b67faaaf3 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -77,11 +77,12 @@ enum ec_command { | |||
77 | 77 | ||
78 | enum { | 78 | enum { |
79 | EC_FLAGS_QUERY_PENDING, /* Query is pending */ | 79 | EC_FLAGS_QUERY_PENDING, /* Query is pending */ |
80 | EC_FLAGS_GPE_STORM, /* GPE storm detected */ | ||
81 | EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and | 80 | EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and |
82 | * OpReg are installed */ | 81 | * OpReg are installed */ |
83 | EC_FLAGS_STARTED, /* Driver is started */ | 82 | EC_FLAGS_STARTED, /* Driver is started */ |
84 | EC_FLAGS_STOPPED, /* Driver is stopped */ | 83 | EC_FLAGS_STOPPED, /* Driver is stopped */ |
84 | EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the | ||
85 | * current command processing */ | ||
85 | }; | 86 | }; |
86 | 87 | ||
87 | #define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ | 88 | #define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ |
@@ -229,8 +230,10 @@ static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) | |||
229 | { | 230 | { |
230 | if (open) | 231 | if (open) |
231 | acpi_enable_gpe(NULL, ec->gpe); | 232 | acpi_enable_gpe(NULL, ec->gpe); |
232 | else | 233 | else { |
234 | BUG_ON(ec->reference_count < 1); | ||
233 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); | 235 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); |
236 | } | ||
234 | if (acpi_ec_is_gpe_raised(ec)) { | 237 | if (acpi_ec_is_gpe_raised(ec)) { |
235 | /* | 238 | /* |
236 | * On some platforms, EN=1 writes cannot trigger GPE. So | 239 | * On some platforms, EN=1 writes cannot trigger GPE. So |
@@ -246,8 +249,10 @@ static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) | |||
246 | { | 249 | { |
247 | if (close) | 250 | if (close) |
248 | acpi_disable_gpe(NULL, ec->gpe); | 251 | acpi_disable_gpe(NULL, ec->gpe); |
249 | else | 252 | else { |
253 | BUG_ON(ec->reference_count < 1); | ||
250 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); | 254 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); |
255 | } | ||
251 | } | 256 | } |
252 | 257 | ||
253 | static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) | 258 | static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) |
@@ -290,6 +295,24 @@ static void acpi_ec_complete_request(struct acpi_ec *ec) | |||
290 | wake_up(&ec->wait); | 295 | wake_up(&ec->wait); |
291 | } | 296 | } |
292 | 297 | ||
298 | static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag) | ||
299 | { | ||
300 | if (!test_bit(flag, &ec->flags)) { | ||
301 | acpi_ec_disable_gpe(ec, false); | ||
302 | pr_debug("+++++ Polling enabled +++++\n"); | ||
303 | set_bit(flag, &ec->flags); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag) | ||
308 | { | ||
309 | if (test_bit(flag, &ec->flags)) { | ||
310 | clear_bit(flag, &ec->flags); | ||
311 | acpi_ec_enable_gpe(ec, false); | ||
312 | pr_debug("+++++ Polling disabled +++++\n"); | ||
313 | } | ||
314 | } | ||
315 | |||
293 | /* | 316 | /* |
294 | * acpi_ec_submit_flushable_request() - Increase the reference count unless | 317 | * acpi_ec_submit_flushable_request() - Increase the reference count unless |
295 | * the flush operation is not in | 318 | * the flush operation is not in |
@@ -404,8 +427,13 @@ err: | |||
404 | * otherwise will take a not handled IRQ as a false one. | 427 | * otherwise will take a not handled IRQ as a false one. |
405 | */ | 428 | */ |
406 | if (!(status & ACPI_EC_FLAG_SCI)) { | 429 | if (!(status & ACPI_EC_FLAG_SCI)) { |
407 | if (in_interrupt() && t) | 430 | if (in_interrupt() && t) { |
408 | ++t->irq_count; | 431 | if (t->irq_count < ec_storm_threshold) |
432 | ++t->irq_count; | ||
433 | /* Allow triggering on 0 threshold */ | ||
434 | if (t->irq_count == ec_storm_threshold) | ||
435 | acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM); | ||
436 | } | ||
409 | } | 437 | } |
410 | out: | 438 | out: |
411 | if (status & ACPI_EC_FLAG_SCI) | 439 | if (status & ACPI_EC_FLAG_SCI) |
@@ -482,6 +510,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
482 | spin_unlock_irqrestore(&ec->lock, tmp); | 510 | spin_unlock_irqrestore(&ec->lock, tmp); |
483 | ret = ec_poll(ec); | 511 | ret = ec_poll(ec); |
484 | spin_lock_irqsave(&ec->lock, tmp); | 512 | spin_lock_irqsave(&ec->lock, tmp); |
513 | if (t->irq_count == ec_storm_threshold) | ||
514 | acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); | ||
485 | pr_debug("***** Command(%s) stopped *****\n", | 515 | pr_debug("***** Command(%s) stopped *****\n", |
486 | acpi_ec_cmd_string(t->command)); | 516 | acpi_ec_cmd_string(t->command)); |
487 | ec->curr = NULL; | 517 | ec->curr = NULL; |
@@ -509,24 +539,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
509 | goto unlock; | 539 | goto unlock; |
510 | } | 540 | } |
511 | } | 541 | } |
512 | /* disable GPE during transaction if storm is detected */ | ||
513 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
514 | /* It has to be disabled, so that it doesn't trigger. */ | ||
515 | acpi_ec_disable_gpe(ec, false); | ||
516 | } | ||
517 | 542 | ||
518 | status = acpi_ec_transaction_unlocked(ec, t); | 543 | status = acpi_ec_transaction_unlocked(ec, t); |
519 | 544 | ||
520 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | 545 | if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) |
521 | msleep(1); | 546 | msleep(1); |
522 | /* It is safe to enable the GPE outside of the transaction. */ | ||
523 | acpi_ec_enable_gpe(ec, false); | ||
524 | } else if (t->irq_count > ec_storm_threshold) { | ||
525 | pr_info("GPE storm detected(%d GPEs), " | ||
526 | "transactions will use polling mode\n", | ||
527 | t->irq_count); | ||
528 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); | ||
529 | } | ||
530 | if (ec->global_lock) | 547 | if (ec->global_lock) |
531 | acpi_release_global_lock(glk); | 548 | acpi_release_global_lock(glk); |
532 | unlock: | 549 | unlock: |